Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 


How to trigger Branch Mapper from Lua?

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Lua Scripting
View previous topic :: View next topic  
Author Message
AJMansfield
How do I cheat?
Reputation: 0

Joined: 24 Jun 2026
Posts: 2

PostPosted: Wed Jun 24, 2026 7:05 am    Post subject: How to trigger Branch Mapper from Lua? Reply with quote

How can I trigger Branch Mapper to start from a Lua script?

Background: I'm currently developing cheats for a game that seems to perform a sort of 'normalization' each time it loads saves from disk, that wipes away some types of previously-cheated progress and makes everything more complicated. There's a lot of other things that it also does during save load, though, and trying to locate this specific normalization routine in order to bypass it is as yet an unsolved problem.

I can't trigger Branch Mapper before I start the save load, as the single-stepping causes the UI to chug to the point that I'm then literally unable to even click the 'load' button, but the load process itself is so fast on modern hardware that it's impossible for me to trigger Branch Mapper on a normal save file.

I've managed to get a bit of success by synthesizing a save files that includes a bunch of extra junk data in 'comment' directives to slow down the save load process, hitting 'load', starting Branch Mapper, and then letting it run overnight. I messed up the DLL load order that time, though, and a save with this junk data is always going to trigger this normalizer, so I can't map out the alternative path that bypasses this normalizer using this approach.

Therefore, I want to trigger Branch Mapper to start directly from a lua-scripted breakpoint directly at the underlying windows API call that opens the save file for reading.

So far, I've managed to cobble together a function for setting a single-shot breakpoint when opening a particular named file. However, I'm at a loss for how to actually trigger Branch Mapper to start mapping branches from this breakpoint:

Code:

function startBranchMapperOnCreateFileA(targetFile)
    local createFileA = getAddress("kernel32.CreateFileA")
    local function onCreateFileA()
        if EIP ~= createFileA then
            return 0
        end

        local filenamePtr = readInteger(ESP + 4)
        local filename = readString(filenamePtr)

        if filename then
            print("CreateFileA: "..filename)
            if string.find(string.upper(filename), string.upper(targetFile), 1, true)
            then
                print("Target file opened")
                debug_removeBreakpoint(createFileA)

                -- How to trigger branch mapper to start?

                return 1
            end
        end

        return 1
    end

    debug_setBreakpoint(createFileA, onCreateFileA)
end


As I rubber-duck this problem out into this post, it becomes apparent that it's actually adequate for me to just "return 0" when the filename matches to get the debugger to just not automatically resume execution; then I can manually start Branch Mapper while the process is paused in the debugger.

Either way, though, the question still occurred to me. Hopefully I'll not need its answer, when it turns out to be even more complicated than I currently imagine and I do actually end up needing to script this so I can run this mapping process a dozen times to gather more data. But perhaps some future user will be helped, at least by finding this question and the do-B-not-A answer I found.

How can I trigger Branch Mapper to start from a Lua script?
Back to top
View user's profile Send private message
Dark Byte
Site Admin
Reputation: 474

Joined: 09 May 2003
Posts: 25952
Location: The netherlands

PostPosted: Wed Jun 24, 2026 2:29 pm    Post subject: Reply with quote

hmm, not very easily, but theoretically it's possible.

There's no build in lua code that does this automatically for you, but you 'could' execute the GUI controls that lead to the branch mapper dialog, parse the threadlist for the thread you're interested in, uncheck all other checkboxes, and execute the OnClick code of the start button. (requires form create notifications and timers)

but yeah, it's not really a neat solution and you'd first have to resume the current breakpoint before it'd work. So you'll end up placing an infinite loop at your breakpoint location (eb fe), start a timer that does all this above, resume from the current breakpoint, and let the timer run, in which, after the branch mapper has started, you restore the original code so it the thread you broke on continues without interfering with the debugger.

And one more annoying thing, because why not, the infinite loop you first placed will be stored in the branch mapper results, so you'll have to remove that one entry. (maybe lua command 'pause/unpause' might work instead of a infinite loop)

_________________
Tools give you results. Knowledge gives you control.

Like my help? Join me on Patreon so i can keep helping
Back to top
View user's profile Send private message MSN Messenger
AJMansfield
How do I cheat?
Reputation: 0

Joined: 24 Jun 2026
Posts: 2

PostPosted: Thu Jun 25, 2026 7:03 am    Post subject: Reply with quote

Quote:
(maybe lua command 'pause/unpause' might work instead of a infinite loop)


Ok thank you so much for this! That's exactly the insight I needed to make this work.

As I suggested I'd try in my original post, I tried configuring my file-open breakpoint to just break into the debugger (with a `return 1`), where I'd then manually start Branch Mapper and then resume from that breakpoint. But resuming the breakpoint interfered with Branch Mapper's single-stepping.

Having my breakpoint issue a `pause()` after the filename match worked perfectly, though! Allowing me to start Branch Mapper and then manually run `unpause()` in the console.

Also needed to make one other update, since it turns out the game uses the raw ntdll.NtCreateFile function directly rather than kernel32.CreateFileA. Which in theory should make this script even more universally-applicable as AFAIK the kernel32 file functions are just wrappers for the ntdll functions anyway.

Here's the script I ended up with:

Code:
NtCreateFileA = getAddress("ntdll.NtCreateFile")

OnCreateFilePatterns = {
    "%.dsn$",
    "%.fst$",
}

function OnNtCreateFile()
    if EIP ~= NtCreateFileA then
        return 0
    end
    local objectAttributes = readInteger(ESP + 0x0C)
    if objectAttributes == 0 then
        return 0
    end
    local objectName = readInteger(objectAttributes + 8)
    if objectName == 0 then
        return 0
    end
    local length = readSmallInteger(objectName)
    local buffer = readInteger(objectName + 4)
    if buffer == 0 then
        return 0
    end
    local filename = readString(buffer, length, true)

    print("Opening:", filename)
    for i, p in ipairs(OnCreateFilePatterns) do
        if string.find(filename, p) then
            print("Paused:", getOpenedProcessID())
            pause()
            print("Resume with `unpause()`.")
            return 0
        end
    end

    return 0
end

function SetOnNtCreateFile()
    debug_setBreakpoint(NtCreateFileA, OnNtCreateFile)
end


BTW, is there a set of language server annotation stubs somewhere for providing in-IDE documentation for CheatEngine's lua API? I've done some work before with creating annotation stubs to help with authoring lua mods for Baba Is You, so if nobody's created such a thing yet I might see about contributing a set of LSP stubs upstream (or, some scripts for generating them from the underlying source).

EDIT: I found a set of LSP stubs: github dungbeest/CheatEngine-LuaEnvironment (can't post URLs yet...)
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Lua Scripting All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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


Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites