Page 1 of 1

Launching games directly from Cheat Engine

Posted: Wed Jul 08, 2026 2:06 am
by HNI96

Update 3
Launch Game Autorun Files Version 1.02
Added a 'Launch Game' button on the main form of cheat engine. This is useful when opening the table after opening cheat engine, or when game has been closed or crashed and you want to launch it again.

Update 2
Launch Game Autorun Files Version 1.01
I've added a clever trick to make the script compatible with tables that overwrite the onOpenProcess() function. Also fixed a typo that doesn't change any functionality. Basically, when the main form loads, the script stores the old onOpenProcess in another variable, overwrites the onOpenProcess with it's own, then changes it back when it's done.

Update 1
Launch Game Autorun Files Version 1.0
I've rewritten the script to directly read and write necessary data in the .ct file instead of using extra memory records that pollute the table. There's also many new features:

  • Launch Game
  • Launch With Advanced Options (parameters, debug and BreakOnEntry)
  • Ask whether to launch or not when an instance already exists. If not it only attaches to the process. If yes it doesn't attach as there's more than 1 process with same name.
  • Advanced Options are saved between sessions so long as the cheat table is also saved.

The code:

Code: Select all

--[[
**************************************
Launch Game Autorun Files Version 1.02
**************************************
--]]

-- Constants
local metadata_name = 'launchgame.dat'

-- Globals
launch_command = {'', '', ' ', false, false}

-- Functions
local launch_gui
local launch_options_gui
local get_path
local write_metadata
local read_metadata
local launch_game
local original_onOpenProcess = onOpenProcess
local custom_onOpenProcess
local create_launch_menu_item


-- [1] = Process Name
-- [2] = Path
-- [3] = Parameters
-- [4] = Debug
-- [5] = BreakOnEntry
launch_gui = function()
    read_metadata()
    if #launch_command[1] > 0 and #launch_command[2] > 0 then
        local message = string.format('Launch %s?', launch_command[1])
        local response = messageDialog(message, mtConfirmation, mbYes, mbNo)

    if response == mrYes then
        launch_options_gui()
    end
else
    return 1
end

return 0
end

launch_options_gui = function()
    local form = createForm()
    form.Caption = "Launch Options"
    form.Width = 400
    form.Height = 150

form.centerScreen()
form.show()

local y = 10
local params_label = createLabel(form)
params_label.Caption = 'Parameters'
params_label.setPosition(10, y)
y = y + 20

local params_input = createEdit(form)
params_input.setPosition(10, y)
params_input.Width = form.Width - 20
if #launch_command[3] > 0 then
    params_input.Text = launch_command[3]
end
y = y + 20

local checkBoxes = {};
checkBoxes[1] = createCheckBox( form );
checkBoxes[2] = createCheckBox( form );

local checkbox_captions = {'Debug', 'BreakOnEntry'}

for x = 1, #checkBoxes do
    checkBoxes[x].AllowGrayed = false
    checkBoxes[x].Caption = checkbox_captions[x]
    checkBoxes[x].setPosition(10, y + x * 20)
    if launch_command[3 + x] == true then
        checkBoxes[x].State = 1
    else
        checkBoxes[x].State = 0
    end
end

y = y + 20 * #checkBoxes

local launch_button = createButton(form)
launch_button.Caption = 'Launch'
launch_button.Width = 60
launch_button.setPosition((form.Width - launch_button.Width)/2,
                        form.Height - launch_button.Height)

launch_button.onClick = function()
    if #params_input.Text > 0 then
        launch_command[3] = params_input.Text
    else
        launch_command[3] = ' '
    end
    launch_command[4] = checkBoxes[1].Checked
    launch_command[5] = checkBoxes[2].Checked
    form.destroy()
    launch_game()
end
end

create_launch_menu_item = function()
    if not has_launch_menu then
        local myMenu = createMenuItem(getMainForm())
        myMenu.Caption = "Launch Game"
        myMenu.onClick = function()
            synchronize(function()
                local err = launch_gui()
                if err ~= 0 then
                    local str  = 'Note: You need to launch game and attach process manually at least once for it to work.'
                    local message = string.format('Error: unable to launch\nprocess = <%s>\npath = <%s>\n\n%s',
                                                launch_command[1], launch_command[2], str)
                    showMessage(message)
                end
            end)
        end
        getMainForm().Menu.Items.add(myMenu)
        has_launch_menu = true
    end
end

get_path = function(pid)
  local modules = enumModules(pid)

  if modules and #modules > 0 then
     return modules[1].PathToFile
  end

  return ''
end

write_metadata = function()
    local table_file = findTableFile(metadata_name)
    local metadata = ""

for i = 1, #launch_command do
    local str = tostring(launch_command[i])
    metadata = metadata .. str .. '\n'
end

if not table_file then
 table_file = createTableFile(metadata_name)
end

table_file.getData().clear()
table_file.getData().writeString(metadata)
end

read_metadata = function()
    -- Original function taken from the wiki:
    -- https://wiki.cheatengine.org/index.php?title=Lua:Class:TableFile
    local fileStr = nil
    local tableFile = findTableFile(metadata_name)

if tableFile ~= nil then
    local stringStream = createStringStream()
    stringStream.Position = 0 -- if not set before using 'copyFrom' the 'StringStream' object will be inconsistent.
    stringStream.copyFrom(tableFile.Stream, tableFile.Stream.Size)
    fileStr = stringStream.DataString
    stringStream.destroy()

    local i = 1
    for line in string.gmatch(fileStr, "[^\n]+") do
        if i <= 3 then
            launch_command[i] = line
        elseif line == 'false' then
                launch_command[i] = false
        elseif line == 'true' then
                launch_command[i] = true
        end
        i = i + 1
    end
end
end

launch_game = function()
    local process_name = launch_command[1]

if getProcessIDFromProcessName(process_name) then
    local message = string.format('%s is already running. Do you want to launch it anyways?', process_name)
    local response = messageDialog(message, mtConfirmation, mbYes, mbNo)

    if response == mrNo then
        openProcess(process_name)
    else
        createProcess(launch_command[2], launch_command[3], launch_command[4], launch_command[5])
        sleep(2000)
    end
else
    createProcess(launch_command[2], launch_command[3], launch_command[4], launch_command[5])
    sleep(2000)
    openProcess(process_name)
end
end

custom_onOpenProcess = function(processId)
  launch_command[1] = process
  launch_command[2] = get_path(processId)
  write_metadata()
  if original_onOpenProcess then
    onOpenProcess = original_onOpenProcess
  end
end

createThread(function()
    synchronize(create_launch_menu_item())
    while (getMainForm() == nil) or (not getMainForm().Visible) do
        sleep(1000)
    end

synchronize(function()
    original_onOpenProcess = onOpenProcess
    onOpenProcess = custom_onOpenProcess
    launch_gui()
end)
end)

Some Images:

img1
img1
Launch Prompt.png (10.63 KiB) Viewed 188 times
img2
img2
Advanced Options.png (11.66 KiB) Viewed 188 times
img3
img3
Process already running.png (12.69 KiB) Viewed 188 times
img4
img4
The CT file.png (16 KiB) Viewed 188 times
img5
img5
Launch Game Button.png (7.47 KiB) Viewed 170 times
img6
img6
Laucn Game Button Error Message.png (8.75 KiB) Viewed 170 times
launchgame.lua
Launch Game Autorun Files Version 1.02
(6.26 KiB) Downloaded 11 times
launchgame.lua
Launch Game Autorun Files Version 1.01
(5.33 KiB) Downloaded 3 times
launchgame.lua
Launch Game Autorun Files Version 1.0
(5.08 KiB) Downloaded 4 times

old post

Since I'm too lazy to manually click to open cheat engine, launch the game, minimize the game, attach cheat engine to process, activate cheats, go back to game, I've decided to automate all that. After wasting time messing around with reg query to get game's exe file path from registry, I've realized I was severely over complicating stuff. Thus I came up with a simple script that should work in every game, allowing you to automatically launch the game when you open the cheat table. My knowledge of Lua is still very superficial, and I'm learning as I go, thus it's likely there are many areas where the script could be improved.

A quick explanation of what happens:
1) The first time the table is opened, nothing happens until the user attaches cheat engine to the game's process.
2) When a process is attached, onOpenProcess is executed, saving the game's file path and process name in the addresses of new memory records.
3) When the cheat table is opened again, it prompts the user if they want to launch the game.
4) Using the saved file's path and process name, and createProcess of course, cheat engine launches the game and attaches to the process.
5) After that you can create a favorite group of scripts that are automatically enabled, meaning you never have to minimize the game.

Code: Select all

local path_record = AddressList.getMemoryRecordByDescription('GAME PATH')
local name_record = AddressList.getMemoryRecordByDescription('PROCESS NAME')
local parent = AddressList.getMemoryRecordByDescription('START')

function launch_game(path, process_name)
  if not path then return 2 end
  createProcess(path)
  getMainForm().WindowState = 'wsMinimized'
  sleep(1000)

  local pid = getProcessIDFromProcessName(process_name)
  if pid then
    openProcess(pid)
  else
    return 1
  end
  return 0
end

local function get_path(pid)
  local modules = enumModules(pid)

  if modules and #modules > 0 then
     return modules[1].PathToFile
  end

  return nil
end

function onOpenProcess(processId)
  if not parent then
    parent = AddressList.createMemoryRecord()
    parent.Description = 'START'
    parent.IsGroupHeader = true
    parent.Options = '[moHideChildren]'
  end

  if not path_record then
    path_record = AddressList.createMemoryRecord()
    path_record.Description = 'GAME PATH'
    path_record.appendToEntry(parent)
  end
  path_record.Address = get_path(processId)

  if not name_record then
    name_record = AddressList.createMemoryRecord()
    name_record.Description = 'PROCESS NAME'
    name_record.appendToEntry(parent)
  end
  name_record.Address = process
end

if path_record and name_record then
  local response = messageDialog("Launch Game?", mtConfirmation, mbYes, mbNo)
  if response == mrYes then
    local game_path = path_record.Address
    local game_process = name_record.Address
    if launch_game(game_path, game_process) ~= 0 then
       showMessage('Failed to launch game')
    end
  end
end