 |
Cheat Engine The Official Site of Cheat Engine
|
| View previous topic :: View next topic |
| Author |
Message |
theboy181 Advanced Cheater
Reputation: 0
Joined: 26 Jan 2018 Posts: 93
|
Posted: Sun Nov 09, 2025 3:22 pm Post subject: Help with dynamically remapping emulator RAM base on reboot |
|
|
I’m working with an emulator that remaps its RAM memory to a new base address every time it reboots. I’m trying to write a Cheat Engine Lua script that can automatically detect the new base address and remap it to the emulator’s real hardware memory range, for example:
0x0C000000 – 0x0CFFFFFF
Here’s what I want the script to do:
Detect the new RAM base address (it’s printed to the emulator’s log file each time it boots).
Remap that new base so CE treats it as the original hardware range (0x0C000000, etc).
Update the memory scan start and stop range in CE to match that mapping.
Update any existing addresses in the current .CT table so they stay valid after each reboot.
Basically, every time the emulator restarts, CE should re-hook automatically, find the new memory base, and continue working with the same hardware-style address layout.
Is this possible with CE’s Lua scripting or API hooking (like ReadProcessMemory hooks)?
If so, could anyone share an example or explain how to properly implement it?
I have made a script that does this, but it was not MAP_MEMORY
Thanks!
|
|
| Back to top |
|
 |
Dark Byte Site Admin
Reputation: 471
Joined: 09 May 2003 Posts: 25813 Location: The netherlands
|
|
| Back to top |
|
 |
theboy181 Advanced Cheater
Reputation: 0
Joined: 26 Jan 2018 Posts: 93
|
Posted: Sun Nov 09, 2025 7:15 pm Post subject: |
|
|
I am trying to make a single .lua script that doesn't rely on autoruns, will that be possible?
I want to replace the form and just setup in the .lua instead
| Code: | PROCESS_NAME = 'Project64.exe' -- Name of the process to attach to
local autoAttachTimerInterval = 500 -- Time interval in milliseconds for the timer
autoAttachTimer = nil -- Timer variable declaration
local emuStartAddress = 0x20000000 -- Start of the emulated memory range (Placeholder)
local emuStopAddress = 0x20800000 -- End of the emulated memory range (Placeholder)
local pidPrev = 0
-- Function to attach Cheat Engine to Project64 process
local function autoAttachToProject64()
local pid = getProcessIDFromProcessName(PROCESS_NAME)
if pid and pid ~= pidPrev then -- Check if Project64 is running
pidPrev = pid
-- Additional code to allocate memory and set base address goes here
-- (Your existing autoAssemble code to allocate memory and set up symbols)
setBaseAddress(pid)
end
end
-- Function to start the auto-attach process with a timer
local function startAutoAttachTimer()
if autoAttachTimer == nil then -- Check if the timer is not already set
autoAttachTimer = createTimer(getMainForm(), false) -- Create a new timer
autoAttachTimer.Interval = autoAttachTimerInterval
autoAttachTimer.OnTimer = autoAttachToProject64 -- Set the timer event to our function
autoAttachTimer.setEnabled(true)
end
end
startAutoAttachTimer() -- Start the auto-attach process
--allocate memory to store the base address of the emulated memory
autoAssemble([[
alloc(EmuBaseAddress, 8)
alloc(EmuSize, 8)
registersymbol(EmuBaseAddress)
registersymbol(EmuSize)
EmuBaseAddress:
dq 1000
EmuSize:
dq 100000
]], true)
autoAssemble([[
alloc(EmuRPM, 512)
alloc(EmuWPM, 512)
alloc(EmuVQE, 512)
label(invalidlength)
registersymbol(EmuRPM)
registersymbol(EmuWPM)
registersymbol(EmuVQE)
EmuRPM:
[64-bit]
add rdx,[EmuBaseAddress] //adjust the address
[/64-bit]
[32-bit]
mov eax,[EmuBaseAddress]
add [esp+8], eax //adjust address to read
[/32-bit]
jmp kernel32.ReadProcessMemory
EmuWPM:
[64-bit]
add rdx,[EmuBaseAddress] //adjust the address
[/64-bit]
[32-bit]
mov eax,[EmuBaseAddress]
add [esp+8], eax //adjust address to read
[/32-bit]
jmp kernel32.WriteProcessMemory
EmuVQE:
//Take the base address and fill in the MBI
[64-bit]
//RCX=hProcess
//RDX=lpAddress
//R8=lpBuffer
//R9=dwLength
xor rax,rax
cmp r9,#48
jb invalidlength
cmp rdx,[EmuSize]
ja invalidlength //actually unreadable, but has the same effect for ce
and rdx,fffffffffffff000
mov [r8+0],rdx //baseaddress
mov [r8+8],0 //allocationbase
mov [r8+10],0x40 //allocation protect: page execute read write (actually a dword, but store as qword to zero the unused bytes)
mov rax,[EmuSize]
sub rax,rdx
mov [r8+18],rax //RegionSize seen from base address
mov dword ptr [r8+20],0x1000 //state : MEM_COMMIT
mov dword ptr [r8+24],0x40 //protection: Page execute read write
mov dword ptr [r8+28],0x20000 //type: mem_private
mov rax,#48 //set the return size to 48 bytes
invalidlength:
ret
[/64-bit]
[32-bit]
push ebp
mov ebp,esp
//ebp+4=return address
//ebp+8=hProcess
//ebp+c=lpAddress
//ebp+10=lpBuffer
//ebp+14=dwLength
xor eax,eax
cmp [ebp+14],#28
jb invalidlength
mov ecx,[ebp+c]
cmp ecx,[EmuSize]
ja invalidlength //actually unreadable, but has the same effect for ce
mov ecx,[ebp+10]
mov eax,[ebp+c]
and eax,fffff000
mov [ecx+0],eax //baseaddress
mov [ecx+4],0 //allocationbase
mov [ecx+8],0x40 //allocation protect: page execute read write (actually a dword, but store as qword to zero the unused bytes)
mov edx,[EmuSize]
sub edx,eax
mov [ecx+c],edx //RegionSize seen from base address
mov dword ptr [ecx+10],0x1000 //state : MEM_COMMIT
mov dword ptr [ecx+14],0x40 //protection: Page execute read write
mov dword ptr [ecx+18],0x20000 //type: mem_private
mov eax,#28
invalidlength:
pop ebp
ret 10
[/32-bit]
]], true)
function setEmuPointer()
setAPIPointer(1, getAddress("EmuRPM", true)) --make RPM calls call emurpm
setAPIPointer(2, getAddress("EmuWPM", true)) --make WPM calls call emuwpm
setAPIPointer(3, getAddress("EmuVQE", true)) --make VQE calls call EmuVQE
end
function setBaseAddress(pid)
-- Set the memory range for scanning in Cheat Engine
--MainForm.FromAddress.Text = string.format("%X", emuStartAddress)
--MainForm.ToAddress.Text = string.format("%X", emuStopAddress)
openProcess(pid) -- Open the process if found
onAPIPointerChange(nil) --shouldn't be needed, but in case this ever gets changed so setAPIPointer calls it as well
setAPIPointer(1, windows_ReadProcessMemory) --make RPM calls call emurpm
setAPIPointer(2, windows_WriteProcessMemory)
setAPIPointer(3, windows_VirtualQueryEx)
writeQwordLocal("EmuBaseAddress", getAddress('20000000 - 80000000'))
writeQwordLocal("EmuSize", 0x81000000)
setEmuPointer() --hook
onAPIPointerChange(setEmuPointer) --rehook when the hook gets lost
MainForm.FromAddress.Text='80000000' -- setup RAM address range
MainForm.ToAddress.Text='80800000'
end |
I did that for PJ64 and it works, but it doesnt require MAP_MEM
Just not able to get it working with flycast.exe
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You can download files in this forum
|
|