script_name('FontChanger by sharagin') script_author('sharagin') script_version('1.0') local imgui = require 'mimgui' local encoding = require 'encoding' local ffi = require 'ffi' encoding.default = 'CP1251' local function is_utf8(str) local i, len = 1, #str while i <= len do local b = str:byte(i) if b <= 0x7F then i = i + 1 elseif b >= 0xC2 and b <= 0xDF then if i + 1 > len or str:byte(i + 1) < 0x80 or str:byte(i + 1) > 0xBF then return false end i = i + 2 elseif b >= 0xE0 and b <= 0xEF then if i + 2 > len or str:byte(i + 1) < 0x80 or str:byte(i + 1) > 0xBF or str:byte(i + 2) < 0x80 or str:byte(i + 2) > 0xBF then return false end i = i + 3 elseif b >= 0xF0 and b <= 0xF4 then if i + 3 > len or str:byte(i + 1) < 0x80 or str:byte(i + 1) > 0xBF or str:byte(i + 2) < 0x80 or str:byte(i + 2) > 0xBF or str:byte(i + 3) < 0x80 or str:byte(i + 3) > 0xBF then return false end i = i + 4 else return false end end return true end local function u8(str) if not str then return "" end if is_utf8(str) then return str end return encoding.UTF8:encode(str) end local function to_cp1251(str) if not str then return "" end if is_utf8(str) then local ok, res = pcall(function() return encoding.UTF8:decode(str) end) if ok and res then return res end end return str end ffi.cdef[[ typedef void* HDC; typedef void* HWND; typedef int (__stdcall *FONTENUMPROCA)(const void*, const void*, unsigned long, long); HDC GetDC(HWND hWnd); int ReleaseDC(HWND hWnd, HDC hDC); int EnumFontFamiliesA(HDC hdc, const char* lpLogfont, FONTENUMPROCA lpProc, long lParam); typedef struct { long lfHeight; long lfWidth; long lfEscapement; long lfOrientation; long lfWeight; unsigned char lfItalic; unsigned char lfUnderline; unsigned char lfStrikeOut; unsigned char lfCharSet; unsigned char lfOutPrecision; unsigned char lfClipPrecision; unsigned char lfQuality; unsigned char lfPitchAndFamily; char lfFaceName[32]; } ENUMLOGFONTA; ]] local windowState = imgui.new.bool(false) local systemFonts = {} local selectedFontIndex = imgui.new.int(0) local fontSize = imgui.new.int(0) local fontWeight = imgui.new.int(0) local function getSampCfgPath() local userProfile = os.getenv("USERPROFILE") or os.getenv("HOMEPATH") or "C:" return userProfile .. "\\Documents\\GTA San Andreas User Files\\SAMP\\sa-mp.cfg" end local function loadCurrentConfig() local cfgPath = getSampCfgPath() local file = io.open(cfgPath, "r") if not file then return end for line in file:lines() do local key, val = line:match("^%s*([%w_]+)%s*=%s*(.+)%s*$") if key and val then if key == "fontsize" then fontSize[0] = tonumber(val) or 0 elseif key == "fontweight" then fontWeight[0] = tonumber(val) or 0 end end end file:close() end local function saveSampConfig(fontName, size, weight) local cfgPath = getSampCfgPath() local lines = {} local keysFound = {fontface = false, fontsize = false, fontweight = false} local file = io.open(cfgPath, "r") if file then for line in file:lines() do local key = line:match("^%s*([%w_]+)%s*=") if key == "fontface" then table.insert(lines, string.format('fontface="%s"', fontName)) keysFound.fontface = true elseif key == "fontsize" then table.insert(lines, string.format('fontsize=%d', size)) keysFound.fontsize = true elseif key == "fontweight" then table.insert(lines, string.format('fontweight=%d', weight)) keysFound.fontweight = true else table.insert(lines, line) end end file:close() end if not keysFound.fontface then table.insert(lines, string.format('fontface="%s"', fontName)) end if not keysFound.fontsize then table.insert(lines, string.format('fontsize=%d', size)) end if not keysFound.fontweight then table.insert(lines, string.format('fontweight=%d', weight)) end local outFile = io.open(cfgPath, "w+") if outFile then for _, l in ipairs(lines) do outFile:write(l .. "\n") end outFile:close() return true end return false end local function getInstalledFonts() local fonts = {} local seen = {} local callback = ffi.cast("FONTENUMPROCA", function(lpelf, lpntm, FontType, lParam) local fontInfo = ffi.cast("ENUMLOGFONTA*", lpelf) local name = ffi.string(fontInfo.lfFaceName) if not seen[name] and not name:find("^@") and #name > 0 then seen[name] = true table.insert(fonts, name) end return 1 end) local hdc = ffi.C.GetDC(nil) ffi.C.EnumFontFamiliesA(hdc, nil, callback, 0) ffi.C.ReleaseDC(nil, hdc) callback:free() table.sort(fonts) return fonts end imgui.OnInitialize(function() local io = imgui.GetIO() local glyphRanges = io.Fonts:GetGlyphRangesCyrillic() local winDir = os.getenv("WINDIR") or "C:\\Windows" local fontPath = winDir .. "\\Fonts\\arial.ttf" if doesFileExist(fontPath) then io.Fonts:Clear() io.Fonts:AddFontFromFileTTF(fontPath, 14.0, nil, glyphRanges) end local style = imgui.GetStyle() local colors = style.Colors local clr = imgui.Col local ImVec4 = imgui.ImVec4 local accent = ImVec4(1.0, 0.918, 0.651, 1.0) local accentHovered = ImVec4(1.0, 0.94, 0.75, 1.0) local accentActive = ImVec4(0.9, 0.82, 0.55, 1.0) local bg = ImVec4(0.12, 0.12, 0.12, 0.95) local darkHeader = ImVec4(0.08, 0.08, 0.08, 1.0) colors[clr.WindowBg] = bg colors[clr.TitleBg] = darkHeader colors[clr.TitleBgActive] = darkHeader colors[clr.TitleBgCollapsed] = darkHeader colors[clr.Text] = ImVec4(0.95, 0.95, 0.95, 1.0) colors[clr.Button] = accent colors[clr.ButtonHovered] = accentHovered colors[clr.ButtonActive] = accentActive colors[clr.FrameBg] = ImVec4(0.2, 0.2, 0.2, 1.0) colors[clr.FrameBgHovered] = ImVec4(0.25, 0.25, 0.25, 1.0) colors[clr.FrameBgActive] = ImVec4(0.3, 0.3, 0.3, 1.0) colors[clr.SliderGrab] = accent colors[clr.SliderGrabActive] = accentActive colors[clr.CheckMark] = accent colors[clr.Header] = accent colors[clr.HeaderHovered] = accentHovered colors[clr.HeaderActive] = accentActive colors[clr.Separator] = ImVec4(1.0, 0.918, 0.651, 0.4) colors[clr.ResizeGrip] = accent colors[clr.ResizeGripHovered] = accentHovered colors[clr.ResizeGripActive] = accentActive style.WindowRounding = 6.0 style.FrameRounding = 4.0 style.GrabRounding = 4.0 end) local newFrame = imgui.OnFrame( function() return windowState[0] end, function(player) local resX, resY = getScreenResolution() imgui.SetNextWindowPos(imgui.ImVec2(resX / 2, resY / 2), imgui.Cond.FirstUseEver, imgui.ImVec2(0.5, 0.5)) imgui.SetNextWindowSize(imgui.ImVec2(420, 290), imgui.Cond.FirstUseEver) imgui.Begin(u8"FontChanger by sharagin", windowState, imgui.WindowFlags.NoCollapse) imgui.Text(u8"Выберите системный шрифт:") local currentFont = systemFonts[selectedFontIndex[0] + 1] local previewText = currentFont and u8(currentFont) or u8"Не выбрано" if imgui.BeginCombo(u8"##FontList", previewText) then for i, fontName in ipairs(systemFonts) do local isSelected = (selectedFontIndex[0] == i - 1) if imgui.Selectable(u8(fontName), isSelected) then selectedFontIndex[0] = i - 1 end if isSelected then imgui.SetItemDefaultFocus() end end imgui.EndCombo() end imgui.Separator() imgui.SliderInt(u8"Размер (fontsize: -3 до 5)", fontSize, -3, 5) imgui.SliderInt(u8"Жирность (fontweight: 0 или 1)", fontWeight, 0, 1) imgui.Spacing() imgui.PushStyleColor(imgui.Col.Text, imgui.ImVec4(0.1, 0.1, 0.1, 1.0)) if imgui.Button(u8"Сохранить шрифт в sa-mp.cfg", imgui.ImVec2(-1, 35)) then local chosenFont = systemFonts[selectedFontIndex[0] + 1] if chosenFont then local success = saveSampConfig(chosenFont, fontSize[0], fontWeight[0]) if success then sampAddChatMessage(to_cp1251(string.format("{FFEAA6}[FontChanger]{FFFFFF} Шрифт {FFEAA6}%s{FFFFFF} сохранен в sa-mp.cfg.", chosenFont)), -1) sampAddChatMessage(to_cp1251("{FFEAA6}[FontChanger]{FFFFFF} Изменения применятся при следующем входе в игру."), -1) else sampAddChatMessage(to_cp1251("{FF6666}[FontChanger]{FFFFFF} Ошибка записи в файл sa-mp.cfg."), -1) end end end imgui.PopStyleColor() imgui.End() end ) function main() while not isSampAvailable() do wait(100) end systemFonts = getInstalledFonts() loadCurrentConfig() sampRegisterChatCommand("fchange", function() windowState[0] = not windowState[0] end) sampAddChatMessage(to_cp1251("{FFEAA6}[FontChanger by sharagin]{FFFFFF} Скрипт загружен. Меню: {FFEAA6}/fchange"), -1) wait(-1) end