--[[ Multi-Emulator Auto Address Range + Auto Symbol Entry Creation Features: - Automatically detects PCSX2, Cemu, ePSXe, DOSBox, PPSSPP, DuckStation, MelonDS, DeSmuME, RMG (N64), mupen64 (N64) - Automatically sets scan range and relative base address - Automatically adds address entries with the [AUTO] prefix - Memory view right-click menu "Copy relative offset to clipboard" (Ctrl+]) - Memory view right-click menu "Add relative offset of selected address to address list" - Found list popup menu "Add relative offset to address list" (supports multi‑select; visible only when relative base is enabled) Auto-created entries are prefixed with [AUTO] and are automatically cleaned up when switching processes (only top-level entries are removed). Other: This LUA script is AI‑driven. DOSBox module based on CE author's script: https://forum.cheatengine.org/viewtopic.php?t=624001 PPSSPP module based on ppsspp_WM.ahk source code. Cemu module based on: https://forum.cheatengine.org/viewtopic.php?p=5781484 PCSX2 module based on: https://forums.pcsx2.net/Thread-PCSX2-1-7-Cheat-Engine-Script-Compatibility DuckStation module based on built‑in symbol RAM. MelonDS / DeSmuME modules based on standalone NDS script v2.3 (signature + pointer offset+8). RMG (N64) module based on mupen64plus.dll export DebugMemGetPointer, using background thread to prevent hang. mupen64 (N64) module based on exported symbol CORE_RDRAM for direct base retrieval. Ver 1.4.8 (New: supports mupen64 emulator (N64) using symbol CORE_RDRAM) ]] ------------------------------------------------------------------------------- -- 0. User configuration ------------------------------------------------------------------------------- local SHOW_MEMORY_VIEW_ON_LOAD = false ------------------------------------------------------------------------------- -- 0.5 Clean up lingering timers + reset counter ------------------------------------------------------------------------------- if ppssppTimer and ppssppTimer.Enabled then ppssppTimer.Enabled = false ppssppTimer.destroy() end ppssppTimer = nil ppsspp_AttemptCount = 0 -- PPSSPP retry counter if foundListMenuTimer and foundListMenuTimer.Enabled then foundListMenuTimer.Enabled = false foundListMenuTimer.destroy() end foundListMenuTimer = nil ------------------------------------------------------------------------------- -- 1. Safe helper functions ------------------------------------------------------------------------------- local function safeGetAddress(name) local ok, val = pcall(getAddress, name) if ok and val and val ~= 0 then return val end return nil end ------------------------------------------------------------------------------- -- 2. Ensure memory view window exists ------------------------------------------------------------------------------- local mv = getMemoryViewForm() if mv == nil then mv = createMemoryView() end ------------------------------------------------------------------------------- -- 3. Create "Copy relative offset" and "Add relative offset to address list" menu items (memory view) ------------------------------------------------------------------------------- if miCopyRelativeAddress ~= nil then miCopyRelativeAddress.destroy() miCopyRelativeAddress = nil end if miAddRelativeOffset ~= nil then miAddRelativeOffset.destroy() miAddRelativeOffset = nil end function addRelativeOffsetToAddressList() local hv = mv.HexadecimalView if not hv or not hv.UseRelativeBase then return end local base = hv.RelativeBase local selStart = hv.SelectionStart if selStart == 0 then return end local offset = selStart - base local offsetStr if offset >= 0 then offsetStr = string.format("+%x", offset) else offsetStr = string.format("-%x", -offset) end addSymbolToAddressList(offsetStr, offsetStr) end miCopyRelativeAddress = createMenuItem(mv.memorypopup) miCopyRelativeAddress.Name = "miCopyRelativeAddress" miCopyRelativeAddress.Caption = "Copy relative offset to clipboard" miCopyRelativeAddress.ShortCut = textToShortCut("Ctrl+]") miCopyRelativeAddress.OnClick = function() local hv = mv.HexadecimalView if hv.UseRelativeBase then local s if hv.SelectionStart >= hv.RelativeBase then s = string.format("+%x", hv.SelectionStart - hv.RelativeBase) else s = string.format("-%x", hv.RelativeBase - hv.SelectionStart) end if hv.SelectionStop ~= hv.SelectionStart then if hv.SelectionStop >= hv.RelativeBase then s = string.format("%s to +%x", s, hv.SelectionStop - hv.RelativeBase) else s = string.format("%s to -%x", s, hv.RelativeBase - hv.SelectionStop) end end writeToClipboard(s) end end miAddRelativeOffset = createMenuItem(mv.memorypopup) miAddRelativeOffset.Name = "miAddRelativeOffset" miAddRelativeOffset.Caption = "Add relative offset of selected address to address list" miAddRelativeOffset.OnClick = addRelativeOffsetToAddressList local insertIndex = -1 for i = 0, mv.memorypopup.Items.Count - 1 do local item = mv.memorypopup.Items[i] if item.Name == "Addthisaddresstothelist1" then insertIndex = i break end end if insertIndex == -1 then for i = 0, mv.memorypopup.Items.Count - 1 do local item = mv.memorypopup.Items[i] if item.Caption == "Add this address to the list" or item.Caption == "Add this address to the list..." then insertIndex = i break end end end if insertIndex ~= -1 then mv.memorypopup.Items.Insert(insertIndex + 1, miCopyRelativeAddress) mv.memorypopup.Items.Insert(insertIndex + 2, miAddRelativeOffset) else mv.memorypopup.Items.Add(miCopyRelativeAddress) mv.memorypopup.Items.Add(miAddRelativeOffset) end local oldmemorypopuponpopup = mv.memorypopup.OnPopup or function() end mv.memorypopup.OnPopup = function(s) local visible = mv.HexadecimalView.UseRelativeBase if miCopyRelativeAddress then miCopyRelativeAddress.Visible = visible end if miAddRelativeOffset then miAddRelativeOffset.Visible = visible end return oldmemorypopuponpopup(s) end ------------------------------------------------------------------------------- -- 4. Manage auto-added address entries (with [AUTO] prefix) ------------------------------------------------------------------------------- local AUTO_PREFIX = "[AUTO] " function removeScriptedSymbols() local addrList = getAddressList() if not addrList then return end local toRemove = {} for i = 0, addrList.Count - 1 do local rec = addrList.getMemoryRecord(i) if rec.Description and rec.Description:find(AUTO_PREFIX, 1, true) == 1 then if not rec.Parent then table.insert(toRemove, rec) end end end for _, rec in ipairs(toRemove) do rec.delete() end end function addSymbolToAddressList(addressExpr, displayName) local addrList = getAddressList() if not addrList then return false end for i = 0, addrList.Count - 1 do local rec = addrList.getMemoryRecord(i) if rec.Address == addressExpr then return false end end local rec = addrList.createMemoryRecord() rec.Address = addressExpr rec.Description = AUTO_PREFIX .. displayName rec.ShowAsHex = true return true end ------------------------------------------------------------------------------- -- 5. Address range retrieval functions for each emulator ------------------------------------------------------------------------------- local N64_RAM_SIZE = 0x400000 -- 4MB, expansion pack would be 0x800000 local TARGET_PROCESSES = {} TARGET_PROCESSES["pcsx2"] = function() local baseAddress = readQword("EEmem") if baseAddress then return string.format("%016X", baseAddress), string.format("%016X", baseAddress + 0x20000000) end return end TARGET_PROCESSES["cemu"] = function() local success, baseAddress = pcall(function() return executeCode(safeGetAddress("Cemu.memory_getBase"), 0, 2000) end) if success and baseAddress then registerSymbol("baseAddress", baseAddress) return string.format("%016X", baseAddress), string.format("%016X", baseAddress + 0x20000000) end return end TARGET_PROCESSES["epsxe"] = function() local aobPattern = "81E1FFFF1F0081C1" local foundAddress = AOBScan(aobPattern) if foundAddress then local address = tonumber(foundAddress[0], 16) + 8 registerSymbol("ePSXe_x32", address) local value = readInteger(address) foundAddress:destroy() return string.format("%016X", value), string.format("%016X", value + 0x200000) end return end TARGET_PROCESSES["dosbox"] = function() local ms = createMemScan() ms.ScanValue = 'IBM COMPATIBLE' ms.VarType = vtString ms.ScanWritable = 'scanDontCare' ms.ScanExecutable = 'scanDontCare' ms.ScanCopyOnWrite = 'scanDontCare' ms.scan() local r = ms.Results ms.destroy() if not r or #r == 0 then return end local chosenAddr = r[1] for _, addr in ipairs(r) do if addr > chosenAddr then chosenAddr = addr end end local baseAddr = chosenAddr - 0xFE00E registerSymbol('base', baseAddr) return string.format("%016X", baseAddr), string.format("%016X", baseAddr + 0x1000000) end TARGET_PROCESSES["duckstation"] = function() local baseAddress = readQword("RAM") if baseAddress then return string.format("%016X", baseAddress), string.format("%016X", baseAddress + 0x200000) end return end -- New mupen64 (N64) support, directly uses symbol CORE_RDRAM TARGET_PROCESSES["mupen64"] = function() local baseAddress = safeGetAddress("CORE_RDRAM") if baseAddress then return string.format("%016X", baseAddress), string.format("%016X", baseAddress + N64_RAM_SIZE) end return end ------------------------------------------------------------------------------- -- 6. Common setup function ------------------------------------------------------------------------------- function applyBaseAddress(fromAddress, toAddress, baseValue, addressExpr, displayName) MainForm.FromAddress.Text = fromAddress MainForm.ToAddress.Text = toAddress local mvhv = mv.HexadecimalView mvhv.UseRelativeBase = true mvhv.RelativeBase = baseValue mvhv.Address = baseValue if SHOW_MEMORY_VIEW_ON_LOAD then mv.show() end if addressExpr and displayName then addSymbolToAddressList(addressExpr, displayName) end end ------------------------------------------------------------------------------- -- 7. PPSSPP configuration function ------------------------------------------------------------------------------- local ppsspp_WindowClass = "PPSSPPWnd" local ppsspp_Configured = false local ppsspp_AttemptCount = 0 local PPSSPP_MAX_ATTEMPTS = 10 function getPPSSPPModuleInfo() local moduleList = enumModules() for _, mod in ipairs(moduleList) do if string.match(mod.Name:lower(), "^ppsspp.*%.exe$") then return mod.Name, mod.Address end end return nil, nil end function configurePPSSPP() local hwnd = findWindow(ppsspp_WindowClass) if hwnd == nil then return false end local currentPid = getOpenedProcessID() local targetPid = getWindowProcessID(hwnd) if currentPid ~= targetPid then return false end local moduleName, processBase = getPPSSPPModuleInfo() if moduleName == nil or processBase == 0 then return false end local WM_USER_GET_BASE_POINTER = 0xB118 local function sendWMs(lParam) return sendMessage(hwnd, WM_USER_GET_BASE_POINTER, 0, lParam) end local reply0 = sendWMs(0) local reply2 = sendWMs(2) if type(reply0) ~= "number" or type(reply2) ~= "number" then return false end local reply1, reply3 if targetIs64Bit() then reply1 = sendWMs(1) reply3 = sendWMs(3) if type(reply1) ~= "number" or type(reply3) ~= "number" then return false end else reply1, reply3 = 0, 0 end local baseAddr = (reply1 * 0x100000000) + reply0 local basePtr = (reply3 * 0x100000000) + reply2 if baseAddr == 0 or basePtr == 0 then return false end local offset = basePtr - processBase if offset < 0 then return false end local actualBase = baseAddr + 0x8800000 removeScriptedSymbols() clearEphemeralSymbols() local fromAddress = string.format("%016X", actualBase) local toAddress = string.format("%016X", actualBase + 0x2000000) local addrExpr = string.format("[%s+%X]+8800000", moduleName, offset) applyBaseAddress(fromAddress, toAddress, actualBase, addrExpr, "PPSSPP Memory") registerSymbol("PPSSPP_BasePtr", basePtr) return true end ------------------------------------------------------------------------------- -- 8. Clear ephemeral symbols ------------------------------------------------------------------------------- function clearEphemeralSymbols() unregisterSymbol("ePSXe_x32") unregisterSymbol("base") unregisterSymbol("baseAddress") unregisterSymbol("PPSSPP_BasePtr") unregisterSymbol("melonDS64") unregisterSymbol("DeSmuMEx32") unregisterSymbol("RDRAM") -- RMG symbol -- mupen64 uses CORE_RDRAM directly, no registration needed, so no cleanup end ------------------------------------------------------------------------------- -- 9. PPSSPP timer (with max attempts) ------------------------------------------------------------------------------- local function ppssppTimerCallback() if not ppsspp_Configured then ppsspp_AttemptCount = ppsspp_AttemptCount + 1 if ppsspp_AttemptCount > PPSSPP_MAX_ATTEMPTS then --print("PPSSPP configuration exceeded max attempts (" .. PPSSPP_MAX_ATTEMPTS .. "), stopping retries.") if ppssppTimer then ppssppTimer.Enabled = false ppssppTimer.destroy() ppssppTimer = nil end return end if configurePPSSPP() then ppsspp_Configured = true if ppssppTimer then ppssppTimer.Enabled = false ppssppTimer.destroy() ppssppTimer = nil end end else if ppssppTimer then ppssppTimer.Enabled = false ppssppTimer.destroy() ppssppTimer = nil end end end ppssppTimer = createTimer(nil, false) ppssppTimer.Interval = 2000 ppssppTimer.OnTimer = ppssppTimerCallback ppssppTimer.Enabled = false ------------------------------------------------------------------------------- -- 10. NDS emulator specific functions ------------------------------------------------------------------------------- function isMelonDSProcess() local moduleList = enumModules() if not moduleList then return false end for _, mod in ipairs(moduleList) do local name = mod.Name or "" if name:lower():find("melonds") then return true end end return false end function isDesmumeProcess() local moduleList = enumModules() if not moduleList then return false end for _, mod in ipairs(moduleList) do local name = mod.Name or "" if name:lower():find("desmume") then return true end end return false end local function getMelonDSMainRAMStart() local pattern = "FF DE FF E7 FF DE FF E7 FF DE FF" local results = AOBScan(pattern) if results == nil then return nil end local count = results.Count if count == 0 then results.destroy(); return nil end local found = nil for i = 0, count - 1 do local addrStr = results[i] local addrNum = tonumber(addrStr, 16) if addrNum ~= nil then local low16 = addrNum & 0xFFFF if low16 == 0 then found = addrNum break end end end results.destroy() return found end local function getDesmumeMainRAMStart() local pattern = "01 00 00 00 00 00 40 00" local results = AOBScan(pattern) if results == nil then return nil end local count = results.Count if count == 0 then results.destroy(); return nil end local addrStr = results[0] results.destroy() return tonumber(addrStr, 16) end local function updateRelativeBase(baseAddr) if mv and mv.HexadecimalView then if baseAddr then mv.HexadecimalView.UseRelativeBase = true mv.HexadecimalView.RelativeBase = baseAddr mv.HexadecimalView.Address = baseAddr if SHOW_MEMORY_VIEW_ON_LOAD then mv.show() end else mv.HexadecimalView.UseRelativeBase = false end end end local function handleMelonDS() clearEphemeralSymbols() removeScriptedSymbols() local startAddr = getMelonDSMainRAMStart() if startAddr then local endAddr = startAddr + 0x400000 local fromStr = string.format("%016X", startAddr) local toStr = string.format("%016X", endAddr) applyBaseAddress(fromStr, toStr, startAddr, "melonDS64", "MelonDS Base") registerSymbol("melonDS64", startAddr) else unregisterSymbol("melonDS64") updateRelativeBase(nil) print("melonDS process detected, but main RAM start address could not be located (game not loaded or signature not found).") end end local function handleDesmume() clearEphemeralSymbols() removeScriptedSymbols() if targetIs64Bit() then print("DeSmuME 64-bit version is not supported (signature only works for 32-bit), skipping setup.") updateRelativeBase(nil) unregisterSymbol("DeSmuMEx32") return end local aobAddr = getDesmumeMainRAMStart() if aobAddr then local ptrAddr = aobAddr + 8 local baseAddr = readInteger(ptrAddr) if baseAddr == nil or baseAddr == 0 then updateRelativeBase(nil) unregisterSymbol("DeSmuMEx32") return end local endAddr = baseAddr + 0x400000 local fromStr = string.format("%016X", baseAddr) local toStr = string.format("%016X", endAddr) applyBaseAddress(fromStr, toStr, baseAddr, "[DeSmuMEx32]", "DeSmuME Base") registerSymbol("DeSmuMEx32", ptrAddr) else unregisterSymbol("DeSmuMEx32") updateRelativeBase(nil) print("DeSmuME process detected, but signature could not be located (game not loaded or signature outdated).") end end ------------------------------------------------------------------------------- -- 11. N64 (RMG) specific functions (asynchronous thread with pcall protection) ------------------------------------------------------------------------------- function isRMGProcess() -- Use safe lookup to avoid getAddress throwing if safeGetAddress("mupen64plus.dll") then return true end if safeGetAddress("DebugMemGetPointer") then return true end -- Fallback: check process name local moduleList = enumModules() if moduleList then for _, mod in ipairs(moduleList) do if mod.Name:lower():find("rmg") or mod.Name:lower():find("mupen64plus") then return true end end end return false end function setupN64() --print("[N64] Starting background RDRAM base retrieval...") local funcAddr = safeGetAddress("DebugMemGetPointer") if not funcAddr then print("[N64] Cannot find DebugMemGetPointer") return end if isPaused() then --print("[N64] Process paused, attempting to unpause...") unpause() sleep(50) end local success, base = pcall(executeCodeEx, 0, 3000, funcAddr, {type=0, value=1}) if success and base and base ~= 0 then --print(string.format("[N64] Retrieved RDRAM base: 0x%X", base)) synchronize(function() registerSymbol("RDRAM", base) local fromStr = string.format("%016X", base) local toStr = string.format("%016X", base + N64_RAM_SIZE) applyBaseAddress(fromStr, toStr, base, "RDRAM", "N64 RDRAM") --print("[N64] Auto-configuration completed") end) else print("[N64] Failed to retrieve base: " .. tostring(base or "pcall exception")) end end function handleRMG() clearEphemeralSymbols() removeScriptedSymbols() local thread = createThread(function() setupN64() end) thread.Name = "N64 auto base retrieval" end ------------------------------------------------------------------------------- -- 12. Delayed execution function (unified flow) ------------------------------------------------------------------------------- local applyTimer = nil local function applyEmulatorSettings(pid, handle, caption) if not mv then mv = getMemoryViewForm() if not mv then return end end -- 1. NDS if isMelonDSProcess() then handleMelonDS() return elseif isDesmumeProcess() then handleDesmume() return end -- 2. PPSSPP (via window class) local hwnd = findWindow(ppsspp_WindowClass) if hwnd ~= nil and getWindowProcessID(hwnd) == pid then ppsspp_Configured = false if configurePPSSPP() then ppsspp_Configured = true if ppssppTimer then ppssppTimer.Enabled = false ppssppTimer.destroy() ppssppTimer = nil end else if not ppssppTimer then ppssppTimer = createTimer(nil, false) ppssppTimer.Interval = 2000 ppssppTimer.OnTimer = ppssppTimerCallback end ppsspp_AttemptCount = 0 ppssppTimer.Enabled = true end return end -- 3. Detect other emulators (including RMG and mupen64) local processType = nil -- Check RMG first (because process name may not match but dll exists) if isRMGProcess() then processType = "rmg" else -- Match by process name (improved: use gsub to strip .exe, supports multi-dot filenames) local moduleList = enumModules() local processName = nil for _, module in ipairs(moduleList) do local name = module.Name:lower() if name:match("%.exe$") then processName = name:gsub("%.exe$", "") break end end if processName then if processName:match("^pcsx2") then processType = "pcsx2" elseif processName:match("^cemu") then processType = "cemu" elseif processName:match("^epsxe") then processType = "epsxe" elseif processName:match("dosbox") then processType = "dosbox" elseif processName:match("^duckstation") then processType = "duckstation" elseif processName:match("^mupen64") then -- new mupen64 match processType = "mupen64" end end end -- Unified handling if processType then clearEphemeralSymbols() removeScriptedSymbols() if processType == "rmg" then handleRMG() -- asynchronous else local fromAddress, toAddress = TARGET_PROCESSES[processType]() if fromAddress and toAddress then local baseVal = tonumber(fromAddress, 16) local addrExpr, displayName if processType == "dosbox" then addrExpr = "base" displayName = "DOSBox Base" elseif processType == "epsxe" then addrExpr = "[ePSXe_x32]" displayName = "ePSXe Base" elseif processType == "pcsx2" then addrExpr = "[EEmem]" displayName = "PCSX2 EEmem" elseif processType == "cemu" then addrExpr = "baseAddress" displayName = "Cemu Base" elseif processType == "duckstation" then addrExpr = "[RAM]" displayName = "DuckStation RAM" elseif processType == "mupen64" then addrExpr = "CORE_RDRAM" displayName = "N64 RDRAM" end if addrExpr and displayName then applyBaseAddress(fromAddress, toAddress, baseVal, addrExpr, displayName) else -- fallback (should not happen) MainForm.FromAddress.Text = fromAddress MainForm.ToAddress.Text = toAddress mv.HexadecimalView.UseRelativeBase = true mv.HexadecimalView.RelativeBase = baseVal mv.HexadecimalView.Address = baseVal if SHOW_MEMORY_VIEW_ON_LOAD then mv.show() end end end end else -- No known emulator matched, reset to full range MainForm.FromAddress.Text = "0000000000000000" MainForm.ToAddress.Text = "7fffffffffffffff" mv.HexadecimalView.UseRelativeBase = false clearEphemeralSymbols() removeScriptedSymbols() end end ------------------------------------------------------------------------------- -- 13. OnProcessOpened ------------------------------------------------------------------------------- local oldOnProcessOpened = MainForm.OnProcessOpened MainForm.OnProcessOpened = function(pid, handle, caption) if oldOnProcessOpened then pcall(oldOnProcessOpened, pid, handle, caption) end if applyTimer then applyTimer.Enabled = false applyTimer.destroy() applyTimer = nil end applyTimer = createTimer(nil, false) applyTimer.Interval = 150 applyTimer.OnTimer = function() applyTimer.Enabled = false applyTimer.destroy() applyTimer = nil applyEmulatorSettings(pid, handle, caption) end applyTimer.Enabled = true end ------------------------------------------------------------------------------- -- 14. Execute once at startup ------------------------------------------------------------------------------- local currentPid = getOpenedProcessID() if currentPid ~= 0 then local timer = createTimer(nil, false) timer.Interval = 200 timer.OnTimer = function() timer.Enabled = false timer.destroy() applyEmulatorSettings(currentPid, getOpenedProcessHandle(), "") end timer.Enabled = true end ------------------------------------------------------------------------------- -- 15. Found list popup menu ------------------------------------------------------------------------------- local miAddRelativeOffsetFromFoundList = nil function addRelativeOffsetFromFoundList() local lv = getMainForm().Foundlist3 if not lv then messageDialog("Found list (Foundlist3) not found.", mtError, mbOK) return end local hv = mv and mv.HexadecimalView if not hv or not hv.UseRelativeBase then messageDialog("Relative base is not currently enabled. Cannot calculate offset.\nPlease attach a supported emulator to automatically set the base address.", mtWarning, mbOK) return end local base = hv.RelativeBase local count = 0 for i = 0, lv.Items.Count - 1 do local item = lv.Items[i] if item and item.Selected then local addrStr = item.Caption if addrStr and addrStr ~= "" then local addrPart = addrStr:match("^([^:]+)") if not addrPart then addrPart = addrStr end local addrNum = tonumber(addrPart, 16) if addrNum then local offset = addrNum - base local offsetStr if offset >= 0 then offsetStr = string.format("+%x", offset) else offsetStr = string.format("-%x", -offset) end addSymbolToAddressList(offsetStr, offsetStr) count = count + 1 end end end end if count > 0 then messageDialog(string.format("Successfully added %d relative offset entries to the address list.", count), mtInformation, mbOK) else messageDialog("No valid addresses selected, or selected addresses could not be parsed.", mtWarning, mbOK) end end local function setupFoundListMenu() local popup = getMainForm().foundlistpopup if not popup then return false end for i = 0, popup.Items.Count - 1 do if popup.Items[i].Caption == "Add relative offset of selected addresses to address list" then miAddRelativeOffsetFromFoundList = popup.Items[i] return true end end local item = createMenuItem(popup) item.Name = "miAddRelativeOffsetFromFoundList" item.Caption = "Add relative offset of selected addresses to address list" item.OnClick = addRelativeOffsetFromFoundList item.Visible = false popup.Items.Insert(0, item) local sep = createMenuItem(popup) sep.Caption = "-" sep.Visible = false popup.Items.Insert(1, sep) miAddRelativeOffsetFromFoundList = item local oldPopup = popup.OnPopup or function() end popup.OnPopup = function(sender) oldPopup(sender) if miAddRelativeOffsetFromFoundList then local visible = mv and mv.HexadecimalView and mv.HexadecimalView.UseRelativeBase or false miAddRelativeOffsetFromFoundList.Visible = visible if popup.Items.Count > 1 and popup.Items[1].Caption == "-" then popup.Items[1].Visible = visible end end end return true end foundListMenuTimer = createTimer(nil, false) foundListMenuTimer.Interval = 300 local attemptCount = 0 foundListMenuTimer.OnTimer = function(timer) attemptCount = attemptCount + 1 if setupFoundListMenu() then timer.Enabled = false timer.destroy() foundListMenuTimer = nil elseif attemptCount >= 50 then timer.Enabled = false timer.destroy() foundListMenuTimer = nil end end foundListMenuTimer.Enabled = true