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 


Lua Breakpoints for Pausing on File Open/Close

 
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: 3

PostPosted: Tue Jun 30, 2026 7:14 am    Post subject: Lua Breakpoints for Pausing on File Open/Close Reply with quote

Ended up developing out the script in my previous post into a more polished and versatile form, and wanted to share with the community in case someone else ever has a similar need.

The purpose of this script is to log and optionally pause execution when the attached game opens or closes a file with a filename that matches a relevant pattern, e.g. so that you can trigger something like Branch Mapper, without having it slow down the UI interaction needed to actually get a game to start loading things --- to make it easier to troubleshoot behaviors that occur while loading a save file or modded asset.

This works by attaching a scripted breakpoint to the underlying ntdll NtCreateFile and NtClose low-level API functions; with a bit of hacked-together data structure traversal and bookkeeping to capture both the relevant arguments and the return values. This should in theory also collect usages of the higher level kernel32 API functions, though I've not tested this. With some modification this could probably also be made to collect calls to the equivalent 64-bit API; that's left as an exercise for the viewer.

Here's the code, hopefully someone finds this useful:

Code:
--- Copyright (C) 2026 Anson Mansfield
--- Released under the MIT License.
--- SPDX-License-Identifier: MIT

---@class FileTracker
FileTracker = {}

--- Filename patterns to track, and their logging/pause behaviors.
--- @type {pattern: string, on_open: integer?, on_opened: integer?, on_close: integer?}[]
FileTracker.track = {
    {pattern = "%.png$", on_open = 1, on_close = 0}, --- log open for PNG images
    {pattern = "%.vmf$", on_open = 2, on_close = 1}, --- pause on open / log close for valve map files
    {pattern = "%.ogg$", on_open = 1, on_close = 1}, --- log open/close for OGG sound effects
    {pattern =      "$", on_open = 1, on_close = 0}, -- log every file opened
}

--- Table from currently-open file handles to their filenames.
--- @type {[integer]: string}
FileTracker.open = {}

--- Table from pending return breakpoint addresses to the corresponding captured arguments from the call.
--- @type {[integer]: {phandle: integer, filename: string}}
FileTracker.pendingOpen = {}

--- @protected
function FileTracker.onNtCreateFileCall()
    local returnAddress = readInteger(ESP)

    --- arg 1, out pointer to handle
    local phandle = readInteger(ESP + 0x04)
    if phandle == 0 then
        return 0
    end

    --- arg 3, in pointer to object attribute struct
    local objectAttributes = readInteger(ESP + 0x0C)
    if objectAttributes == 0 then
        return 0
    end
    local objectName = readInteger(objectAttributes + 0x08)
    if objectName == 0 then
        return 0
    end
    local length = readSmallInteger(objectName)
    local buffer = readInteger(objectName + 0x04)
    if buffer == 0 then
        return 0
    end
    local filename = readString(buffer, length, true)

    for _, entry in ipairs(FileTracker.track) do
        if string.find(filename, entry.pattern) then
            FileTracker.pendingOpen[returnAddress] = {
                phandle = phandle,
                filename = filename,
            }
            debug_setBreakpoint(returnAddress, FileTracker.onNtCreateFileReturn)
            break
        end
    end

    return 0
end

--- @protected
function FileTracker.onNtCreateFileReturn()
    local pending = FileTracker.pendingOpen[EIP]
    if pending == nil then
        return 0
    end
    FileTracker.pendingOpen[EIP] = nil
    debug_removeBreakpoint(EIP)

    local filename = pending.filename
    local phandle = pending.phandle

    local result

    if EAX ~= 0 then
        result = string.format("<errno %d>", EAX)
    else
        local handle = readInteger(phandle)
        FileTracker.open[handle] = filename
        result = string.format("%d", handle)
    end

    for _, entry in ipairs(FileTracker.track) do
        if string.find(filename, entry.pattern) then
            FileTracker.modeBehavior(
                entry.on_open,
                string.format("open(\"%s\") -> %s", filename, result)
            )
            break
        end
    end

    return 0
end

--- @protected
function FileTracker.onNtCloseCall()
    local handle = readInteger(ESP + 0x04)
    local filename = FileTracker.open[handle]
    if filename == nil then
        return 0
    end
    FileTracker.open[handle] = nil

    for _, entry in ipairs(FileTracker.track) do
        if string.find(filename, entry.pattern) then
            FileTracker.modeBehavior(
                entry.on_close,
                string.format("close(%d) -- \"%s\"", handle, filename)
            )
            break
        end
    end

    return 0
end

--- @protected
function FileTracker.modeBehavior(modeValue, message)
    if modeValue >= 1 then
        print(message)
    end
    if modeValue >= 2 then
        pause()
        print("pause() -- resume with unpause()")
    end
end

--- @public
function FileTracker.begin()
    debug_setBreakpoint(getAddress("ntdll.NtClose"), FileTracker.onNtCloseCall)
    debug_setBreakpoint(getAddress("ntdll.NtCreateFile"), FileTracker.onNtCreateFileCall)
end

FileTracker.begin()
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