-- This file is part of mimgui project -- Licensed under the MIT License -- Copyright (c) 2018, FYP assert(getMoonloaderVersion() >= 025) local imgui = require 'mimgui.imgui' local DX9 = require 'mimgui.dx9' local ffi = require 'ffi' local winmsg = require 'windows.message' local memory = require 'memory' local mimgui = {} local renderer = nil local subscriptionsInitialize = {} local subscriptionsNewFrame = {} local eventsRegistered = false local active = false local cursorActive = false local playerLocked = false local iniFilePath = nil local defaultGlyphRanges = nil setmetatable(mimgui, {__index = imgui, __newindex = function(t, k, v) if imgui[k] then print('[mimgui] Warning! Overwriting existing key "'..k..'"!') end rawset(t, k, v) end}) -- background "Shift" triggering fix memory.fill(0x00531155, 0x90, 5, true) local function ShowCursor(show) if show then showCursor(true) elseif cursorActive then showCursor(false) end cursorActive = show end local function LockPlayer(lock) if lock then lockPlayerControl(true) elseif playerLocked then lockPlayerControl(false) end playerLocked = lock end -- MoonLoader v.027 if not isCursorActive then isCursorActive = function() return cursorActive end end local function InitializeRenderer() -- init renderer local hwnd = ffi.cast('HWND', readMemory(0x00C8CF88, 4, false)) local d3ddevice = ffi.cast('LPDIRECT3DDEVICE9', getD3DDevicePtr()) renderer = assert(DX9.new(d3ddevice, hwnd)) renderer:SwitchContext() -- configure imgui imgui.GetIO().ImeWindowHandle = nil -- default causes crash. TODO: why? imgui.GetIO().LogFilename = nil local confdir = getWorkingDirectory() .. [[\config\mimgui\]] if not doesDirectoryExist(confdir) then createDirectory(confdir) end iniFilePath = ffi.new('char[260]', confdir .. script.this.filename .. '.ini') imgui.GetIO().IniFilename = iniFilePath -- change font local fontFile = getFolderPath(0x14) .. '\\trebucbd.ttf' assert(doesFileExist(fontFile), '[mimgui] Font "' .. fontFile .. '" doesn\'t exist!') local builder = imgui.ImFontGlyphRangesBuilder() builder:AddRanges(imgui.GetIO().Fonts:GetGlyphRangesCyrillic()) builder:AddText([[‚„…†‡€‰‹‘’“”•–—™›№]]) defaultGlyphRanges = imgui.ImVector_ImWchar() builder:BuildRanges(defaultGlyphRanges) imgui.GetIO().Fonts:AddFontFromFileTTF(fontFile, 14, nil, defaultGlyphRanges[0].Data) -- invoke initializers for _, cb in ipairs(subscriptionsInitialize) do cb() end end local function RegisterEvents() addEventHandler('onD3DPresent', function() if active then if not renderer then InitializeRenderer() end if renderer and not renderer.lost then renderer:SwitchContext() for _, sub in ipairs(subscriptionsNewFrame) do if sub._render and sub._before then sub:_before() end end renderer:NewFrame() local hideCursor = true for _, sub in ipairs(subscriptionsNewFrame) do if sub._render then sub:_draw() hideCursor = hideCursor and sub.HideCursor end end if hideCursor and not isCursorActive() then imgui.SetMouseCursor(imgui.lib.ImGuiMouseCursor_None) end renderer:EndFrame() end end end) local keyState = {} local WM_MOUSEHWHEEL = 0x020E local mouseMsgs = { [WM_MOUSEHWHEEL]=true, [winmsg.WM_LBUTTONDOWN]=true, [winmsg.WM_LBUTTONDBLCLK]=true, [winmsg.WM_RBUTTONDOWN]=true, [winmsg.WM_RBUTTONDBLCLK]=true, [winmsg.WM_MBUTTONDOWN]=true, [winmsg.WM_MBUTTONDBLCLK]=true, [winmsg.WM_LBUTTONUP]=true, [winmsg.WM_RBUTTONUP]=true, [winmsg.WM_MBUTTONUP]=true, [winmsg.WM_MOUSEWHEEL]=true, [winmsg.WM_SETCURSOR]=true } local keyboardMsgs = { [winmsg.WM_KEYDOWN]=true, [winmsg.WM_SYSKEYDOWN]=true, [winmsg.WM_KEYUP]=true, [winmsg.WM_SYSKEYUP]=true, [winmsg.WM_CHAR]=true } addEventHandler('onWindowMessage', function(msg, wparam, lparam) if not renderer then return end if not mimgui.DisableInput then local keyboard = keyboardMsgs[msg] local mouse = mouseMsgs[msg] if active and (keyboard or mouse) then renderer:SwitchContext() local io = imgui.GetIO() renderer:WindowMessage(msg, wparam, lparam) if (keyboard and io.WantCaptureKeyboard) or (mouse and io.WantCaptureMouse) then if msg == winmsg.WM_KEYDOWN or msg == winmsg.WM_SYSKEYDOWN then keyState[wparam] = false consumeWindowMessage(true, true, true) elseif msg == winmsg.WM_KEYUP or msg == winmsg.WM_SYSKEYUP then if not keyState[wparam] then consumeWindowMessage(true, true, true) end else consumeWindowMessage(true, true, true) end end end end -- save key states to prevent key sticking if msg == winmsg.WM_KILLFOCUS then keyState = {} elseif wparam < 256 then if msg == winmsg.WM_KEYDOWN or msg == winmsg.WM_SYSKEYDOWN then keyState[wparam] = true elseif msg == winmsg.WM_KEYUP or msg == winmsg.WM_SYSKEYUP then keyState[wparam] = false end end end) addEventHandler('onD3DDeviceLost', function() if renderer and not renderer.lost then renderer:InvalidateDeviceObjects() renderer.lost = true end end) addEventHandler('onD3DDeviceReset', function() if renderer then renderer.lost = false end end) addEventHandler('onScriptTerminate', function(scr) if scr == script.this then ShowCursor(false) LockPlayer(false) end end) local updaterThread = lua_thread.create(function() while true do wait(0) local activate, hideCursor, lockPlayer = false, true, false if #subscriptionsNewFrame > 0 then for i, sub in ipairs(subscriptionsNewFrame) do if type(sub.Condition) == 'function' then sub._render = sub.Condition() else sub._render = sub.Condition and true end if sub._render then hideCursor = hideCursor and sub.HideCursor lockPlayer = lockPlayer or sub.LockPlayer end activate = activate or sub._render end end active = activate ShowCursor(active and not hideCursor) LockPlayer(active and lockPlayer) end end) updaterThread.work_in_pause = true end local function Unsubscribe(t, sub) for i, v in ipairs(t) do if v == sub then table.remove(t, i) return end end end local function ImGuiEnum(name) return setmetatable({__name = name}, {__index = function(t, k) return imgui.lib[t.__name .. k] end}) end --- API --- mimgui._VERSION = '1.7.0' mimgui.DisableInput = false mimgui.ComboFlags = ImGuiEnum('ImGuiComboFlags_') mimgui.Dir = ImGuiEnum('ImGuiDir_') mimgui.ColorEditFlags = ImGuiEnum('ImGuiColorEditFlags_') mimgui.Col = ImGuiEnum('ImGuiCol_') mimgui.WindowFlags = ImGuiEnum('ImGuiWindowFlags_') mimgui.NavInput = ImGuiEnum('ImGuiNavInput_') mimgui.FocusedFlags = ImGuiEnum('ImGuiFocusedFlags_') mimgui.Cond = ImGuiEnum('ImGuiCond_') mimgui.BackendFlags = ImGuiEnum('ImGuiBackendFlags_') mimgui.TreeNodeFlags = ImGuiEnum('ImGuiTreeNodeFlags_') mimgui.StyleVar = ImGuiEnum('ImGuiStyleVar_') mimgui.DrawCornerFlags = ImGuiEnum('ImDrawCornerFlags_') mimgui.DragDropFlags = ImGuiEnum('ImGuiDragDropFlags_') mimgui.SelectableFlags = ImGuiEnum('ImGuiSelectableFlags_') mimgui.InputTextFlags = ImGuiEnum('ImGuiInputTextFlags_') mimgui.MouseCursor = ImGuiEnum('ImGuiMouseCursor_') mimgui.FontAtlasFlags = ImGuiEnum('ImFontAtlasFlags_') mimgui.HoveredFlags = ImGuiEnum('ImGuiHoveredFlags_') mimgui.ConfigFlags = ImGuiEnum('ImGuiConfigFlags_') mimgui.DrawListFlags = ImGuiEnum('ImDrawListFlags_') mimgui.DataType = ImGuiEnum('ImGuiDataType_') mimgui.Key = ImGuiEnum('ImGuiKey_') function mimgui.OnInitialize(cb) assert(type(cb) == 'function') table.insert(subscriptionsInitialize, cb) return {Unsubscribe = function() Unsubscribe(subscriptionsInitialize, cb) end} end function mimgui.OnFrame(cond, cbBeforeFrame, cbDraw) assert(type(cond) == 'function') assert(type(cbBeforeFrame) == 'function') if cbDraw then assert(type(cbDraw) == 'function') end if not eventsRegistered then RegisterEvents() eventsRegistered = true end local sub = { Condition = cond, LockPlayer = false, HideCursor = false, _before = cbDraw and cbBeforeFrame or nil, _draw = cbDraw or cbBeforeFrame, _render = false, } function sub:Unsubscribe() Unsubscribe(subscriptionsNewFrame, self) end function sub:IsActive() return self._render end table.insert(subscriptionsNewFrame, sub) return sub end function mimgui.SwitchContext() return renderer:SwitchContext() end function mimgui.CreateTextureFromFile(path) return renderer:CreateTextureFromFile(path) end function mimgui.CreateTextureFromFileInMemory(src, size) return renderer:CreateTextureFromFileInMemory(src, size) end function mimgui.ReleaseTexture(tex) return renderer:ReleaseTexture(tex) end function mimgui.CreateFontsTexture() return renderer:CreateFontsTexture() end function mimgui.InvalidateFontsTexture() return renderer:InvalidateFontsTexture() end function mimgui.GetRenderer() return renderer end function mimgui.IsInitialized() return renderer ~= nil end function mimgui.StrCopy(dst, src, len) if len or tostring(ffi.typeof(dst)):find('*', 1, true) then ffi.copy(dst, src, len) else len = math.min(ffi.sizeof(dst) - 1, #src) ffi.copy(dst, src, len) dst[len] = 0 end end local new = {} setmetatable(new, { __index = function(self, key) local basetype = ffi.typeof(key) local mt = { __index = function(self, sz) return setmetatable({type = ffi.typeof('$[$]', self.type, sz)}, getmetatable(self)) end, __call = function(self, ...) return self.type(...) end } return setmetatable({type = ffi.typeof('$[1]', basetype), basetype = basetype}, { __index = function(self, sz) return setmetatable({type = ffi.typeof('$[$]', self.basetype, sz)}, mt) end, __call = mt.__call }) end, __call = function(self, t, ...) return ffi.new(t, ...) end }) mimgui.new = new return mimgui