Difference between revisions of "Lua:Class:Addresslist"
Mr millchick (talk | contribs) (Verified Lua-exposed properties/methods from source. Added 13 examples, noted unexposed properties (SelCount, colors, events). Fixed descriptions, enhanced indexing notes, improved structure.) |
|||
| Line 1: | Line 1: | ||
[[Category:Lua]] | [[Category:Lua]] | ||
| − | + | '''AddressList''' '''class''': ('''Inheritance''': ''[[Lua:Class:Panel|Panel]]''->''[[Lua:Class:WinControl|WinControl]]''->''[[Lua:Class:Control|Control]]''->''[[Lua:Class:Component|Component]]''->''[[Lua:Class:Object|Object]]'') | |
| − | The AddressList class represents | + | The AddressList class represents Cheat Engine's address list (also known as the cheat table), which is the primary container for managing [[Lua:Class:MemoryRecord|MemoryRecord]] entries. This class provides methods to create, retrieve, and manipulate memory records, as well as trigger UI updates for description, address, type, and value changes. |
| − | + | ||
| + | The AddressList is accessible through the [[Lua:getAddressList|getAddressList]] function, which returns the main address list instance from Cheat Engine's main form. | ||
== Properties == | == Properties == | ||
| − | {| class="wikitable" style="width:100%" | + | |
| + | {|class="wikitable" style="width:100%" | ||
! Property | ! Property | ||
! Type | ! Type | ||
| + | ! Access | ||
! Description | ! Description | ||
| − | |||
| − | |||
| − | |||
| − | |||
|- | |- | ||
| Count | | Count | ||
| Integer | | Integer | ||
| − | | | + | | Read-only |
| − | + | | The total number of [[Lua:Class:MemoryRecord|MemoryRecord]] entries in the address list (all levels, including children). | |
| − | |||
| − | |||
| − | | The number of | ||
|- | |- | ||
| SelectedRecord | | SelectedRecord | ||
| [[Lua:Class:MemoryRecord|MemoryRecord]] | | [[Lua:Class:MemoryRecord|MemoryRecord]] | ||
| − | | | + | | Read/Write |
| + | | Gets or sets the currently selected memory record. Returns '''nil''' if no record is selected. Setting this property deselects all other records and selects the specified one. | ||
|- | |- | ||
| − | | MemoryRecord[] | + | | MemoryRecord[index] |
| [[Lua:Class:MemoryRecord|MemoryRecord]] | | [[Lua:Class:MemoryRecord|MemoryRecord]] | ||
| − | | Array to | + | | Read-only |
| + | | Array accessor to retrieve memory records by index (0-based). Can also be accessed using function call syntax: '''AddressList[index]'''. Same as '''getMemoryRecord(index)'''. Alternatively, can be accessed with a string to search by description. | ||
|- | |- | ||
| [index] | | [index] | ||
| [[Lua:Class:MemoryRecord|MemoryRecord]] | | [[Lua:Class:MemoryRecord|MemoryRecord]] | ||
| − | | | + | | Read-only |
| − | + | | Default array accessor. Equivalent to '''MemoryRecord[index]'''. | |
| − | | | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
|} | |} | ||
| + | |||
| + | === Properties Not Exposed to Lua === | ||
| + | |||
| + | The following properties are available in the Pascal TAddressList class but are '''not''' exposed to Lua scripts: | ||
| + | * LoadedTableVersion | ||
| + | * SelCount | ||
| + | * CheckboxActiveSelectedColor, CheckboxActiveColor, CheckboxSelectedColor, CheckboxColor | ||
| + | * SelectedBackgroundColor, SelectedSecondaryBackgroundColor | ||
| + | * ExpandSignColor, IncreaseArrowColor, DecreaseArrowColor | ||
| + | * MouseHighlightedRecord | ||
| + | * OnDescriptionChange, OnAddressChange, OnTypeChange, OnValueChange, OnAutoAssemblerEdit (event callbacks) | ||
| + | |||
| + | These properties are used internally by Cheat Engine's UI but cannot be accessed or modified from Lua scripts. | ||
== Methods == | == Methods == | ||
| − | {| class="wikitable" style="width:100%" | + | |
| + | {|class="wikitable" style="width:100%" | ||
! Method | ! Method | ||
! Parameters | ! Parameters | ||
| Line 82: | Line 59: | ||
| None | | None | ||
| Integer | | Integer | ||
| − | | Returns the number of memory records. | + | | Returns the total number of memory records in the address list. |
|- | |- | ||
| getMemoryRecord | | getMemoryRecord | ||
| − | | Integer (index) | + | | Integer (index) OR String (description) |
| [[Lua:Class:MemoryRecord|MemoryRecord]] | | [[Lua:Class:MemoryRecord|MemoryRecord]] | ||
| − | | Returns the memory record at the | + | | Returns the memory record at the specified index (0-based). Also accepts a string description to search by description. Returns '''nil''' if index is out of bounds or description not found. |
|- | |- | ||
| getMemoryRecordByDescription | | getMemoryRecordByDescription | ||
| String (description) | | String (description) | ||
| [[Lua:Class:MemoryRecord|MemoryRecord]] | | [[Lua:Class:MemoryRecord|MemoryRecord]] | ||
| − | | Returns the memory record with the | + | | Returns the first memory record with the specified description. Returns '''nil''' if not found. Uses an internal hash table for fast lookups. |
|- | |- | ||
| getMemoryRecordByID | | getMemoryRecordByID | ||
| − | | Integer ( | + | | Integer (id) |
| [[Lua:Class:MemoryRecord|MemoryRecord]] | | [[Lua:Class:MemoryRecord|MemoryRecord]] | ||
| − | | Returns the memory record with the | + | | Returns the memory record with the specified unique ID. Returns '''nil''' if not found. |
|- | |- | ||
| createMemoryRecord | | createMemoryRecord | ||
| None | | None | ||
| [[Lua:Class:MemoryRecord|MemoryRecord]] | | [[Lua:Class:MemoryRecord|MemoryRecord]] | ||
| − | | Creates a | + | | Creates a new memory record and adds it to the address list. The record is initialized with default values (description: "Plugin Address", address: "0", type: vtDword). Returns the newly created [[Lua:Class:MemoryRecord|MemoryRecord]] object. |
|- | |- | ||
| getSelectedRecords | | getSelectedRecords | ||
| None | | None | ||
| − | | Table | + | | Table |
| − | | Returns | + | | Returns a table (1-indexed) containing all currently selected [[Lua:Class:MemoryRecord|MemoryRecord]] objects. Returns '''nil''' if no records are selected. |
| + | |- | ||
| + | | getSelectedRecord | ||
| + | | None | ||
| + | | [[Lua:Class:MemoryRecord|MemoryRecord]] | ||
| + | | Returns the currently selected memory record. Same as accessing the '''SelectedRecord''' property. Returns '''nil''' if no record is selected. | ||
| + | |- | ||
| + | | setSelectedRecord | ||
| + | | [[Lua:Class:MemoryRecord|MemoryRecord]] (memrec) | ||
| + | | None | ||
| + | | Sets the currently selected memory record, deselecting all others. Same as setting the '''SelectedRecord''' property. | ||
|- | |- | ||
| doDescriptionChange | | doDescriptionChange | ||
| None | | None | ||
| None | | None | ||
| − | | Shows the GUI window to change the description of the selected entry. | + | | Shows the GUI window to change the description of the selected entry. Opens a dialog allowing the user to modify the description. Equivalent to double-clicking the description column. |
|- | |- | ||
| doAddressChange | | doAddressChange | ||
| None | | None | ||
| None | | None | ||
| − | | Shows the GUI window to change the address of the selected entry. | + | | Shows the GUI window to change the address of the selected entry. Opens a dialog allowing the user to modify the address and pointer settings. Equivalent to double-clicking the address column. |
|- | |- | ||
| doTypeChange | | doTypeChange | ||
| None | | None | ||
| None | | None | ||
| − | | Shows the GUI window to change the type of the selected entries. | + | | Shows the GUI window to change the type of the selected entries. Opens a dialog allowing the user to change the variable type. Equivalent to double-clicking the type column. |
|- | |- | ||
| doValueChange | | doValueChange | ||
| None | | None | ||
| None | | None | ||
| − | | Shows the GUI window to change the value of the selected entries. | + | | Shows the GUI window to change the value of the selected entries. Opens a dialog allowing the user to modify the value. Equivalent to double-clicking the value column. |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
|- | |- | ||
| disableAllWithoutExecute | | disableAllWithoutExecute | ||
| None | | None | ||
| None | | None | ||
| − | | Disables all | + | | Disables all active auto assembler script entries without executing their [DISABLE] sections. Useful for quickly disabling all scripts when detaching from a process or when scripts have failed. |
|- | |- | ||
| rebuildDescriptionCache | | rebuildDescriptionCache | ||
| None | | None | ||
| None | | None | ||
| − | | Rebuilds the description-to | + | | Rebuilds the internal hash table used for fast description-based lookups. Call this after making many changes to descriptions if you need to ensure '''getMemoryRecordByDescription''' reflects the latest state. |
|} | |} | ||
| − | == | + | === Inherited Properties and Methods === |
| − | + | ||
| − | + | As AddressList inherits from Panel, it also has access to all [[Lua:Class:Panel|Panel]], [[Lua:Class:WinControl|WinControl]], [[Lua:Class:Control|Control]], [[Lua:Class:Component|Component]], and [[Lua:Class:Object|Object]] properties and methods. However, most inherited UI properties are not typically used in scripting contexts since the AddressList is part of the main Cheat Engine form. | |
| − | + | ||
| − | + | Useful inherited properties include: | |
| − | + | * '''Visible''' (boolean) - Show/hide the address list panel | |
| − | + | * '''Enabled''' (boolean) - Enable/disable user interaction | |
| − | + | * '''PopupMenu''' ([[Lua:Class:PopupMenu|PopupMenu]]) - Custom context menu for the address list | |
| − | + | ||
| − | + | == Usage Examples == | |
| − | + | ||
| − | + | === Basic Address List Access === | |
| − | + | ||
| − | + | <pre> | |
| − | + | -- Get the main address list | |
| − | + | local addressList = getAddressList() | |
| − | + | ||
| − | + | -- Get the count of all records | |
| − | + | print("Total records: " .. addressList.Count) | |
| − | + | ||
| − | + | -- Get selected records to find selection count | |
| − | + | local selected = addressList.getSelectedRecords() | |
| − | + | if selected then | |
| − | + | print("Selected records: " .. #selected) | |
| − | + | else | |
| − | + | print("Selected records: 0") | |
| + | end | ||
| + | |||
| + | -- Access records by index (0-based) | ||
| + | if addressList.Count > 0 then | ||
| + | local firstRecord = addressList[0] | ||
| + | print("First record: " .. firstRecord.Description) | ||
| + | |||
| + | -- Alternative syntax | ||
| + | local secondRecord = addressList.getMemoryRecord(1) | ||
| + | if secondRecord then | ||
| + | print("Second record: " .. secondRecord.Description) | ||
| + | end | ||
| + | end | ||
| + | |||
| + | -- Access by description (alternative to index) | ||
| + | local healthRec = addressList["Player Health"] | ||
| + | if healthRec then | ||
| + | print("Found by description: " .. healthRec.Description) | ||
| + | end | ||
| + | |||
| + | -- Get/set selected record | ||
| + | local selected = addressList.SelectedRecord | ||
| + | if selected then | ||
| + | print("Currently selected: " .. selected.Description) | ||
| + | print("Value: " .. selected.Value) | ||
| + | end | ||
| + | </pre> | ||
| + | |||
| + | === Creating Memory Records === | ||
| + | |||
| + | <pre> | ||
| + | local addressList = getAddressList() | ||
| + | |||
| + | -- Create a basic memory record | ||
| + | local mr = addressList.createMemoryRecord() | ||
| + | mr.Description = "Player Health" | ||
| + | mr.Address = "game.exe+1234" | ||
| + | mr.Type = vtDword | ||
| + | |||
| + | -- Create multiple records | ||
| + | local stats = {"Health", "Mana", "Stamina"} | ||
| + | local offsets = {0x10, 0x14, 0x18} | ||
| + | |||
| + | for i = 1, #stats do | ||
| + | local record = addressList.createMemoryRecord() | ||
| + | record.Description = "Player " .. stats[i] | ||
| + | record.Address = string.format("player_base+%X", offsets[i]) | ||
| + | record.Type = vtFloat | ||
| + | record.ShowAsHex = false | ||
| + | end | ||
| + | |||
| + | print("Created " .. #stats .. " new records") | ||
| + | print("Total records now: " .. addressList.Count) | ||
| + | </pre> | ||
| + | |||
| + | === Finding Records by Description === | ||
| + | |||
| + | <pre> | ||
| + | local addressList = getAddressList() | ||
| + | |||
| + | -- Find by exact description | ||
| + | local healthRecord = addressList.getMemoryRecordByDescription("Player Health") | ||
| + | if healthRecord then | ||
| + | print("Health value: " .. healthRecord.Value) | ||
| + | healthRecord.Active = true | ||
| + | else | ||
| + | print("Player Health not found") | ||
| + | end | ||
| + | |||
| + | -- Alternative: Search by index with description | ||
| + | local record = addressList.getMemoryRecord("Player Mana") | ||
| + | if record then | ||
| + | print("Mana address: " .. record.Address) | ||
| + | end | ||
| + | |||
| + | -- Note: Description lookup is case-sensitive | ||
| + | local notFound = addressList.getMemoryRecordByDescription("player health") -- Won't find "Player Health" | ||
| + | </pre> | ||
| − | == | + | === Finding Records by ID === |
| − | |||
| − | |||
<pre> | <pre> | ||
| − | -- | + | local addressList = getAddressList() |
| − | + | ||
| − | print( | + | -- Get a record's ID and store it |
| + | local healthRecord = addressList.getMemoryRecordByDescription("Player Health") | ||
| + | if healthRecord then | ||
| + | local storedID = healthRecord.ID | ||
| + | print("Stored ID: " .. storedID) | ||
| + | |||
| + | -- Later, retrieve by ID (even if description changed) | ||
| + | local retrievedRecord = addressList.getMemoryRecordByID(storedID) | ||
| + | if retrievedRecord then | ||
| + | print("Retrieved: " .. retrievedRecord.Description) | ||
| + | print("Same record: " .. tostring(healthRecord == retrievedRecord)) | ||
| + | end | ||
end | end | ||
| − | -- | + | -- IDs are unique and persistent within a session |
| − | local | + | -- Useful for tracking records across description changes |
| − | + | </pre> | |
| − | + | ||
| + | === Working with Selected Records === | ||
| + | |||
| + | <pre> | ||
| + | local addressList = getAddressList() | ||
-- Get all selected records | -- Get all selected records | ||
| − | local selected = | + | local selected = addressList.getSelectedRecords() |
| − | for i, | + | if selected then |
| − | print("Selected:", | + | print("Processing " .. #selected .. " selected records") |
| + | |||
| + | for i = 1, #selected do | ||
| + | local record = selected[i] | ||
| + | print(i .. ": " .. record.Description) | ||
| + | |||
| + | -- Modify all selected records | ||
| + | record.Active = true | ||
| + | record.Color = 0x0000FF -- Red (BGR format) | ||
| + | end | ||
| + | else | ||
| + | print("No records selected") | ||
| + | end | ||
| + | |||
| + | -- Select a specific record programmatically | ||
| + | local targetRecord = addressList.getMemoryRecordByDescription("God Mode") | ||
| + | if targetRecord then | ||
| + | addressList.SelectedRecord = targetRecord | ||
| + | print("Selected: " .. targetRecord.Description) | ||
| + | end | ||
| + | |||
| + | -- Check current selection | ||
| + | local currentSelection = addressList.getSelectedRecord() | ||
| + | if currentSelection then | ||
| + | print("Current selection: " .. currentSelection.Description) | ||
| + | end | ||
| + | </pre> | ||
| + | |||
| + | === Triggering UI Dialogs === | ||
| + | |||
| + | <pre> | ||
| + | local addressList = getAddressList() | ||
| + | |||
| + | -- Select a record first | ||
| + | local record = addressList.getMemoryRecordByDescription("Player Health") | ||
| + | if record then | ||
| + | addressList.SelectedRecord = record | ||
| + | |||
| + | -- Open description editor dialog | ||
| + | -- addressList.doDescriptionChange() | ||
| + | |||
| + | -- Open address editor dialog | ||
| + | -- addressList.doAddressChange() | ||
| + | |||
| + | -- Open type selector dialog | ||
| + | -- addressList.doTypeChange() | ||
| + | |||
| + | -- Open value editor dialog | ||
| + | -- addressList.doValueChange() | ||
| + | |||
| + | -- Note: These dialogs are modal and will pause script execution | ||
| + | -- Uncomment the lines above to test interactively | ||
| + | end | ||
| + | </pre> | ||
| + | |||
| + | === Managing Auto Assembler Scripts === | ||
| + | |||
| + | <pre> | ||
| + | local addressList = getAddressList() | ||
| + | |||
| + | -- Create an auto assembler script | ||
| + | local script = addressList.createMemoryRecord() | ||
| + | script.Description = "Infinite Health" | ||
| + | script.Type = vtAutoAssembler | ||
| + | script.Script = [[ | ||
| + | [ENABLE] | ||
| + | aobscan(health_code,89 0D * * * * 8B 45 08) | ||
| + | alloc(newmem,32) | ||
| + | |||
| + | label(code) | ||
| + | label(return) | ||
| + | |||
| + | newmem: | ||
| + | code: | ||
| + | mov [health_address],#1000 // Set to 1000 | ||
| + | mov [edx],ecx | ||
| + | jmp return | ||
| + | |||
| + | health_code: | ||
| + | jmp newmem | ||
| + | nop | ||
| + | return: | ||
| + | registersymbol(health_code) | ||
| + | |||
| + | [DISABLE] | ||
| + | health_code: | ||
| + | db 89 0D * * * * 8B 45 08 | ||
| + | unregistersymbol(health_code) | ||
| + | dealloc(newmem) | ||
| + | ]] | ||
| + | |||
| + | -- Activate the script | ||
| + | script.Active = true | ||
| + | |||
| + | -- Later, disable all scripts without executing [DISABLE] sections | ||
| + | -- Useful when the process is about to close or has crashed | ||
| + | addressList.disableAllWithoutExecute() | ||
| + | </pre> | ||
| + | |||
| + | === Iterating Through All Records === | ||
| + | |||
| + | <pre> | ||
| + | local addressList = getAddressList() | ||
| + | |||
| + | -- Iterate through all records (including children) | ||
| + | print("=== All Records ===") | ||
| + | for i = 0, addressList.Count - 1 do | ||
| + | local record = addressList[i] | ||
| + | local indent = string.rep(" ", record.Parent and 1 or 0) | ||
| + | |||
| + | print(string.format("%s[%d] %s = %s (%s)", | ||
| + | indent, i, record.Description, record.Value, | ||
| + | record.Active and "Active" or "Inactive")) | ||
| + | end | ||
| + | |||
| + | -- Find all active records | ||
| + | print("\n=== Active Records ===") | ||
| + | local activeCount = 0 | ||
| + | for i = 0, addressList.Count - 1 do | ||
| + | local record = addressList[i] | ||
| + | if record.Active then | ||
| + | print(record.Description) | ||
| + | activeCount = activeCount + 1 | ||
| + | end | ||
end | end | ||
| + | print("Total active: " .. activeCount) | ||
| − | -- | + | -- Find all records of a specific type |
| − | + | print("\n=== Float Records ===") | |
| + | for i = 0, addressList.Count - 1 do | ||
| + | local record = addressList[i] | ||
| + | if record.Type == vtFloat or record.Type == vtDouble then | ||
| + | print(record.Description .. " @ " .. record.Address) | ||
| + | end | ||
| + | end | ||
</pre> | </pre> | ||
| + | |||
| + | === Batch Operations === | ||
| + | |||
| + | <pre> | ||
| + | local addressList = getAddressList() | ||
| + | |||
| + | -- Deactivate all records | ||
| + | print("Deactivating all records...") | ||
| + | for i = 0, addressList.Count - 1 do | ||
| + | addressList[i].Active = false | ||
| + | end | ||
| + | |||
| + | -- Activate records matching a pattern | ||
| + | print("Activating player-related records...") | ||
| + | local activatedCount = 0 | ||
| + | for i = 0, addressList.Count - 1 do | ||
| + | local record = addressList[i] | ||
| + | if record.Description:lower():find("player") then | ||
| + | record.Active = true | ||
| + | activatedCount = activatedCount + 1 | ||
| + | end | ||
| + | end | ||
| + | print("Activated " .. activatedCount .. " records") | ||
| + | |||
| + | -- Change all values matching a condition | ||
| + | for i = 0, addressList.Count - 1 do | ||
| + | local record = addressList[i] | ||
| + | local numValue = tonumber(record.Value) | ||
| + | |||
| + | if numValue and numValue < 100 then | ||
| + | record.Value = "999" | ||
| + | print("Changed " .. record.Description .. " to 999") | ||
| + | end | ||
| + | end | ||
| + | </pre> | ||
| + | |||
| + | === Rebuilding Description Cache === | ||
| + | |||
| + | <pre> | ||
| + | local addressList = getAddressList() | ||
| + | |||
| + | -- Make many description changes | ||
| + | for i = 0, addressList.Count - 1 do | ||
| + | local record = addressList[i] | ||
| + | record.Description = "Modified_" .. record.Description | ||
| + | end | ||
| + | |||
| + | -- Rebuild the description cache for fast lookups | ||
| + | addressList.rebuildDescriptionCache() | ||
| + | |||
| + | -- Now lookups will work correctly | ||
| + | local modifiedRecord = addressList.getMemoryRecordByDescription("Modified_Player Health") | ||
| + | if modifiedRecord then | ||
| + | print("Found: " .. modifiedRecord.Description) | ||
| + | end | ||
| + | |||
| + | -- Note: The cache is automatically updated for individual changes, | ||
| + | -- but rebuilding is useful after bulk modifications or when debugging | ||
| + | </pre> | ||
| + | |||
| + | === Working with Hierarchical Records === | ||
| + | |||
| + | <pre> | ||
| + | local addressList = getAddressList() | ||
| + | |||
| + | -- Create a parent group | ||
| + | local group = addressList.createMemoryRecord() | ||
| + | group.Description = "Player Stats" | ||
| + | group.IsGroupHeader = true | ||
| + | group.Options = "[moActivateChildrenAsWell,moDeactivateChildrenAsWell]" | ||
| + | |||
| + | -- Create children and attach to group | ||
| + | local childDescriptions = {"Health", "Mana", "Stamina"} | ||
| + | for i, desc in ipairs(childDescriptions) do | ||
| + | local child = addressList.createMemoryRecord() | ||
| + | child.Description = desc | ||
| + | child.Type = vtDword | ||
| + | child.Address = string.format("player_base+%X", (i-1) * 4) | ||
| + | child.appendToEntry(group) | ||
| + | end | ||
| + | |||
| + | -- Activate the group (children will activate too) | ||
| + | group.Active = true | ||
| + | |||
| + | -- Note: All records are still in the flat addressList.Count | ||
| + | -- Parent-child relationships are maintained separately | ||
| + | print("Total records: " .. addressList.Count) | ||
| + | print("Group children: " .. group.Count) | ||
| + | </pre> | ||
| + | |||
| + | === Advanced: Monitoring Changes === | ||
| + | |||
| + | <pre> | ||
| + | local addressList = getAddressList() | ||
| + | |||
| + | -- Store initial state | ||
| + | local recordStates = {} | ||
| + | for i = 0, addressList.Count - 1 do | ||
| + | local record = addressList[i] | ||
| + | recordStates[record.ID] = { | ||
| + | description = record.Description, | ||
| + | value = record.Value, | ||
| + | active = record.Active | ||
| + | } | ||
| + | end | ||
| + | |||
| + | -- Later, check for changes | ||
| + | print("=== Changes Detected ===") | ||
| + | for i = 0, addressList.Count - 1 do | ||
| + | local record = addressList[i] | ||
| + | local oldState = recordStates[record.ID] | ||
| + | |||
| + | if oldState then | ||
| + | if oldState.value ~= record.Value then | ||
| + | print(record.Description .. " value changed: " .. | ||
| + | oldState.value .. " -> " .. record.Value) | ||
| + | end | ||
| + | |||
| + | if oldState.active ~= record.Active then | ||
| + | print(record.Description .. " activation changed: " .. | ||
| + | tostring(oldState.active) .. " -> " .. tostring(record.Active)) | ||
| + | end | ||
| + | end | ||
| + | end | ||
| + | </pre> | ||
| + | |||
| + | == Important Notes == | ||
| + | |||
| + | === Record Indexing === | ||
| + | |||
| + | * The '''MemoryRecord[index]''' property uses '''0-based indexing''' | ||
| + | * The '''getSelectedRecords()''' method returns a '''1-indexed table''' (Lua convention) | ||
| + | * Child indices within parent records use '''0-based indexing''' | ||
| + | * You can use '''AddressList[index]''' as a shorthand for '''AddressList.MemoryRecord[index]''' | ||
| + | * You can also use '''AddressList["description"]''' to search by description | ||
| + | |||
| + | === Description Lookup Performance === | ||
| + | |||
| + | * '''getMemoryRecordByDescription''' uses an internal hash table for O(1) lookups | ||
| + | * The cache is automatically maintained for most operations | ||
| + | * Some reserved descriptions (e.g., "BYTE", "WORD", "DWORD", "QWORD", "INT", "FLOAT", "DOUBLE") cannot be found by description as they're in the forbidden list | ||
| + | * If lookups fail after many changes, call '''rebuildDescriptionCache()''' | ||
| + | * Description lookups are '''case-sensitive''' | ||
| + | |||
| + | === Selection Behavior === | ||
| + | |||
| + | * Setting '''SelectedRecord''' deselects all other records | ||
| + | * Multiple selection is typically done through the UI (Ctrl+Click, Shift+Click) | ||
| + | * '''getSelectedRecords()''' includes records at all hierarchy levels if selected | ||
| + | * Selected records remain selected until explicitly changed | ||
| + | * Use '''getSelectedRecord()''' or '''SelectedRecord''' property to get the main selected record | ||
| + | |||
| + | === Memory Record Lifecycle === | ||
| + | |||
| + | * Creating records with '''createMemoryRecord()''' adds them to the address list | ||
| + | * Records are removed from the count when deleted via '''memoryrecord.delete()''' | ||
| + | * The Count property reflects all records at all hierarchy levels (including children) | ||
| + | * Deleting a parent record automatically deletes all its children | ||
| + | |||
| + | === Thread Safety === | ||
| + | |||
| + | * Address list operations should be performed on the main thread | ||
| + | * Use [[Lua:synchronize|synchronize]] if accessing from background threads | ||
| + | * UI dialog methods ('''do*Change''') must be called from the main thread and will block script execution | ||
{{LuaSeeAlso}} | {{LuaSeeAlso}} | ||
=== Related Functions === | === Related Functions === | ||
| − | * [[Lua: | + | * [[Lua:getAddressList|getAddressList]] - Get the main AddressList object |
| − | * [[Lua: | + | * [[Lua:getMainForm|getMainForm]] - Get the main Cheat Engine form |
=== Related Classes === | === Related Classes === | ||
| − | * [[Lua:Class:MemoryRecord|MemoryRecord]] | + | * [[Lua:Class:MemoryRecord|MemoryRecord]] - Individual address list entries |
| + | * [[Lua:Class:MemoryRecordHotkey|MemoryRecordHotkey]] - Hotkeys attached to memory records | ||
| + | * [[Lua:Class:Panel|Panel]] - Parent class | ||
| + | * [[Lua:Class:WinControl|WinControl]] - Grandparent class | ||
| + | * [[Lua:Class:Control|Control]] - Base control class | ||
| + | * [[Lua:Class:Component|Component]] - Base component class | ||
Latest revision as of 20:20, 25 October 2025
AddressList class: (Inheritance: Panel->WinControl->Control->Component->Object)
The AddressList class represents Cheat Engine's address list (also known as the cheat table), which is the primary container for managing MemoryRecord entries. This class provides methods to create, retrieve, and manipulate memory records, as well as trigger UI updates for description, address, type, and value changes.
The AddressList is accessible through the getAddressList function, which returns the main address list instance from Cheat Engine's main form.
Contents
- 1 Properties
- 2 Methods
- 3 Usage Examples
- 3.1 Basic Address List Access
- 3.2 Creating Memory Records
- 3.3 Finding Records by Description
- 3.4 Finding Records by ID
- 3.5 Working with Selected Records
- 3.6 Triggering UI Dialogs
- 3.7 Managing Auto Assembler Scripts
- 3.8 Iterating Through All Records
- 3.9 Batch Operations
- 3.10 Rebuilding Description Cache
- 3.11 Working with Hierarchical Records
- 3.12 Advanced: Monitoring Changes
- 4 Important Notes
- 5 See also
Properties[edit]
| Property | Type | Access | Description |
|---|---|---|---|
| Count | Integer | Read-only | The total number of MemoryRecord entries in the address list (all levels, including children). |
| SelectedRecord | MemoryRecord | Read/Write | Gets or sets the currently selected memory record. Returns nil if no record is selected. Setting this property deselects all other records and selects the specified one. |
| MemoryRecord[index] | MemoryRecord | Read-only | Array accessor to retrieve memory records by index (0-based). Can also be accessed using function call syntax: AddressList[index]. Same as getMemoryRecord(index). Alternatively, can be accessed with a string to search by description. |
| [index] | MemoryRecord | Read-only | Default array accessor. Equivalent to MemoryRecord[index]. |
Properties Not Exposed to Lua[edit]
The following properties are available in the Pascal TAddressList class but are not exposed to Lua scripts:
- LoadedTableVersion
- SelCount
- CheckboxActiveSelectedColor, CheckboxActiveColor, CheckboxSelectedColor, CheckboxColor
- SelectedBackgroundColor, SelectedSecondaryBackgroundColor
- ExpandSignColor, IncreaseArrowColor, DecreaseArrowColor
- MouseHighlightedRecord
- OnDescriptionChange, OnAddressChange, OnTypeChange, OnValueChange, OnAutoAssemblerEdit (event callbacks)
These properties are used internally by Cheat Engine's UI but cannot be accessed or modified from Lua scripts.
Methods[edit]
| Method | Parameters | Returns | Description |
|---|---|---|---|
| getCount | None | Integer | Returns the total number of memory records in the address list. |
| getMemoryRecord | Integer (index) OR String (description) | MemoryRecord | Returns the memory record at the specified index (0-based). Also accepts a string description to search by description. Returns nil if index is out of bounds or description not found. |
| getMemoryRecordByDescription | String (description) | MemoryRecord | Returns the first memory record with the specified description. Returns nil if not found. Uses an internal hash table for fast lookups. |
| getMemoryRecordByID | Integer (id) | MemoryRecord | Returns the memory record with the specified unique ID. Returns nil if not found. |
| createMemoryRecord | None | MemoryRecord | Creates a new memory record and adds it to the address list. The record is initialized with default values (description: "Plugin Address", address: "0", type: vtDword). Returns the newly created MemoryRecord object. |
| getSelectedRecords | None | Table | Returns a table (1-indexed) containing all currently selected MemoryRecord objects. Returns nil if no records are selected. |
| getSelectedRecord | None | MemoryRecord | Returns the currently selected memory record. Same as accessing the SelectedRecord property. Returns nil if no record is selected. |
| setSelectedRecord | MemoryRecord (memrec) | None | Sets the currently selected memory record, deselecting all others. Same as setting the SelectedRecord property. |
| doDescriptionChange | None | None | Shows the GUI window to change the description of the selected entry. Opens a dialog allowing the user to modify the description. Equivalent to double-clicking the description column. |
| doAddressChange | None | None | Shows the GUI window to change the address of the selected entry. Opens a dialog allowing the user to modify the address and pointer settings. Equivalent to double-clicking the address column. |
| doTypeChange | None | None | Shows the GUI window to change the type of the selected entries. Opens a dialog allowing the user to change the variable type. Equivalent to double-clicking the type column. |
| doValueChange | None | None | Shows the GUI window to change the value of the selected entries. Opens a dialog allowing the user to modify the value. Equivalent to double-clicking the value column. |
| disableAllWithoutExecute | None | None | Disables all active auto assembler script entries without executing their [DISABLE] sections. Useful for quickly disabling all scripts when detaching from a process or when scripts have failed. |
| rebuildDescriptionCache | None | None | Rebuilds the internal hash table used for fast description-based lookups. Call this after making many changes to descriptions if you need to ensure getMemoryRecordByDescription reflects the latest state. |
Inherited Properties and Methods[edit]
As AddressList inherits from Panel, it also has access to all Panel, WinControl, Control, Component, and Object properties and methods. However, most inherited UI properties are not typically used in scripting contexts since the AddressList is part of the main Cheat Engine form.
Useful inherited properties include:
- Visible (boolean) - Show/hide the address list panel
- Enabled (boolean) - Enable/disable user interaction
- PopupMenu (PopupMenu) - Custom context menu for the address list
Usage Examples[edit]
Basic Address List Access[edit]
-- Get the main address list
local addressList = getAddressList()
-- Get the count of all records
print("Total records: " .. addressList.Count)
-- Get selected records to find selection count
local selected = addressList.getSelectedRecords()
if selected then
print("Selected records: " .. #selected)
else
print("Selected records: 0")
end
-- Access records by index (0-based)
if addressList.Count > 0 then
local firstRecord = addressList[0]
print("First record: " .. firstRecord.Description)
-- Alternative syntax
local secondRecord = addressList.getMemoryRecord(1)
if secondRecord then
print("Second record: " .. secondRecord.Description)
end
end
-- Access by description (alternative to index)
local healthRec = addressList["Player Health"]
if healthRec then
print("Found by description: " .. healthRec.Description)
end
-- Get/set selected record
local selected = addressList.SelectedRecord
if selected then
print("Currently selected: " .. selected.Description)
print("Value: " .. selected.Value)
end
Creating Memory Records[edit]
local addressList = getAddressList()
-- Create a basic memory record
local mr = addressList.createMemoryRecord()
mr.Description = "Player Health"
mr.Address = "game.exe+1234"
mr.Type = vtDword
-- Create multiple records
local stats = {"Health", "Mana", "Stamina"}
local offsets = {0x10, 0x14, 0x18}
for i = 1, #stats do
local record = addressList.createMemoryRecord()
record.Description = "Player " .. stats[i]
record.Address = string.format("player_base+%X", offsets[i])
record.Type = vtFloat
record.ShowAsHex = false
end
print("Created " .. #stats .. " new records")
print("Total records now: " .. addressList.Count)
Finding Records by Description[edit]
local addressList = getAddressList()
-- Find by exact description
local healthRecord = addressList.getMemoryRecordByDescription("Player Health")
if healthRecord then
print("Health value: " .. healthRecord.Value)
healthRecord.Active = true
else
print("Player Health not found")
end
-- Alternative: Search by index with description
local record = addressList.getMemoryRecord("Player Mana")
if record then
print("Mana address: " .. record.Address)
end
-- Note: Description lookup is case-sensitive
local notFound = addressList.getMemoryRecordByDescription("player health") -- Won't find "Player Health"
Finding Records by ID[edit]
local addressList = getAddressList()
-- Get a record's ID and store it
local healthRecord = addressList.getMemoryRecordByDescription("Player Health")
if healthRecord then
local storedID = healthRecord.ID
print("Stored ID: " .. storedID)
-- Later, retrieve by ID (even if description changed)
local retrievedRecord = addressList.getMemoryRecordByID(storedID)
if retrievedRecord then
print("Retrieved: " .. retrievedRecord.Description)
print("Same record: " .. tostring(healthRecord == retrievedRecord))
end
end
-- IDs are unique and persistent within a session
-- Useful for tracking records across description changes
Working with Selected Records[edit]
local addressList = getAddressList()
-- Get all selected records
local selected = addressList.getSelectedRecords()
if selected then
print("Processing " .. #selected .. " selected records")
for i = 1, #selected do
local record = selected[i]
print(i .. ": " .. record.Description)
-- Modify all selected records
record.Active = true
record.Color = 0x0000FF -- Red (BGR format)
end
else
print("No records selected")
end
-- Select a specific record programmatically
local targetRecord = addressList.getMemoryRecordByDescription("God Mode")
if targetRecord then
addressList.SelectedRecord = targetRecord
print("Selected: " .. targetRecord.Description)
end
-- Check current selection
local currentSelection = addressList.getSelectedRecord()
if currentSelection then
print("Current selection: " .. currentSelection.Description)
end
Triggering UI Dialogs[edit]
local addressList = getAddressList()
-- Select a record first
local record = addressList.getMemoryRecordByDescription("Player Health")
if record then
addressList.SelectedRecord = record
-- Open description editor dialog
-- addressList.doDescriptionChange()
-- Open address editor dialog
-- addressList.doAddressChange()
-- Open type selector dialog
-- addressList.doTypeChange()
-- Open value editor dialog
-- addressList.doValueChange()
-- Note: These dialogs are modal and will pause script execution
-- Uncomment the lines above to test interactively
end
Managing Auto Assembler Scripts[edit]
local addressList = getAddressList() -- Create an auto assembler script local script = addressList.createMemoryRecord() script.Description = "Infinite Health" script.Type = vtAutoAssembler script.Script = [[ [ENABLE] aobscan(health_code,89 0D * * * * 8B 45 08) alloc(newmem,32) label(code) label(return) newmem: code: mov [health_address],#1000 // Set to 1000 mov [edx],ecx jmp return health_code: jmp newmem nop return: registersymbol(health_code) [DISABLE] health_code: db 89 0D * * * * 8B 45 08 unregistersymbol(health_code) dealloc(newmem) ]] -- Activate the script script.Active = true -- Later, disable all scripts without executing [DISABLE] sections -- Useful when the process is about to close or has crashed addressList.disableAllWithoutExecute()
Iterating Through All Records[edit]
local addressList = getAddressList()
-- Iterate through all records (including children)
print("=== All Records ===")
for i = 0, addressList.Count - 1 do
local record = addressList[i]
local indent = string.rep(" ", record.Parent and 1 or 0)
print(string.format("%s[%d] %s = %s (%s)",
indent, i, record.Description, record.Value,
record.Active and "Active" or "Inactive"))
end
-- Find all active records
print("\n=== Active Records ===")
local activeCount = 0
for i = 0, addressList.Count - 1 do
local record = addressList[i]
if record.Active then
print(record.Description)
activeCount = activeCount + 1
end
end
print("Total active: " .. activeCount)
-- Find all records of a specific type
print("\n=== Float Records ===")
for i = 0, addressList.Count - 1 do
local record = addressList[i]
if record.Type == vtFloat or record.Type == vtDouble then
print(record.Description .. " @ " .. record.Address)
end
end
Batch Operations[edit]
local addressList = getAddressList()
-- Deactivate all records
print("Deactivating all records...")
for i = 0, addressList.Count - 1 do
addressList[i].Active = false
end
-- Activate records matching a pattern
print("Activating player-related records...")
local activatedCount = 0
for i = 0, addressList.Count - 1 do
local record = addressList[i]
if record.Description:lower():find("player") then
record.Active = true
activatedCount = activatedCount + 1
end
end
print("Activated " .. activatedCount .. " records")
-- Change all values matching a condition
for i = 0, addressList.Count - 1 do
local record = addressList[i]
local numValue = tonumber(record.Value)
if numValue and numValue < 100 then
record.Value = "999"
print("Changed " .. record.Description .. " to 999")
end
end
Rebuilding Description Cache[edit]
local addressList = getAddressList()
-- Make many description changes
for i = 0, addressList.Count - 1 do
local record = addressList[i]
record.Description = "Modified_" .. record.Description
end
-- Rebuild the description cache for fast lookups
addressList.rebuildDescriptionCache()
-- Now lookups will work correctly
local modifiedRecord = addressList.getMemoryRecordByDescription("Modified_Player Health")
if modifiedRecord then
print("Found: " .. modifiedRecord.Description)
end
-- Note: The cache is automatically updated for individual changes,
-- but rebuilding is useful after bulk modifications or when debugging
Working with Hierarchical Records[edit]
local addressList = getAddressList()
-- Create a parent group
local group = addressList.createMemoryRecord()
group.Description = "Player Stats"
group.IsGroupHeader = true
group.Options = "[moActivateChildrenAsWell,moDeactivateChildrenAsWell]"
-- Create children and attach to group
local childDescriptions = {"Health", "Mana", "Stamina"}
for i, desc in ipairs(childDescriptions) do
local child = addressList.createMemoryRecord()
child.Description = desc
child.Type = vtDword
child.Address = string.format("player_base+%X", (i-1) * 4)
child.appendToEntry(group)
end
-- Activate the group (children will activate too)
group.Active = true
-- Note: All records are still in the flat addressList.Count
-- Parent-child relationships are maintained separately
print("Total records: " .. addressList.Count)
print("Group children: " .. group.Count)
Advanced: Monitoring Changes[edit]
local addressList = getAddressList()
-- Store initial state
local recordStates = {}
for i = 0, addressList.Count - 1 do
local record = addressList[i]
recordStates[record.ID] = {
description = record.Description,
value = record.Value,
active = record.Active
}
end
-- Later, check for changes
print("=== Changes Detected ===")
for i = 0, addressList.Count - 1 do
local record = addressList[i]
local oldState = recordStates[record.ID]
if oldState then
if oldState.value ~= record.Value then
print(record.Description .. " value changed: " ..
oldState.value .. " -> " .. record.Value)
end
if oldState.active ~= record.Active then
print(record.Description .. " activation changed: " ..
tostring(oldState.active) .. " -> " .. tostring(record.Active))
end
end
end
Important Notes[edit]
Record Indexing[edit]
- The MemoryRecord[index] property uses 0-based indexing
- The getSelectedRecords() method returns a 1-indexed table (Lua convention)
- Child indices within parent records use 0-based indexing
- You can use AddressList[index] as a shorthand for AddressList.MemoryRecord[index]
- You can also use AddressList["description"] to search by description
Description Lookup Performance[edit]
- getMemoryRecordByDescription uses an internal hash table for O(1) lookups
- The cache is automatically maintained for most operations
- Some reserved descriptions (e.g., "BYTE", "WORD", "DWORD", "QWORD", "INT", "FLOAT", "DOUBLE") cannot be found by description as they're in the forbidden list
- If lookups fail after many changes, call rebuildDescriptionCache()
- Description lookups are case-sensitive
Selection Behavior[edit]
- Setting SelectedRecord deselects all other records
- Multiple selection is typically done through the UI (Ctrl+Click, Shift+Click)
- getSelectedRecords() includes records at all hierarchy levels if selected
- Selected records remain selected until explicitly changed
- Use getSelectedRecord() or SelectedRecord property to get the main selected record
Memory Record Lifecycle[edit]
- Creating records with createMemoryRecord() adds them to the address list
- Records are removed from the count when deleted via memoryrecord.delete()
- The Count property reflects all records at all hierarchy levels (including children)
- Deleting a parent record automatically deletes all its children
Thread Safety[edit]
- Address list operations should be performed on the main thread
- Use synchronize if accessing from background threads
- UI dialog methods (do*Change) must be called from the main thread and will block script execution
See also[edit]
Related Functions[edit]
- getAddressList - Get the main AddressList object
- getMainForm - Get the main Cheat Engine form
Related Classes[edit]
- MemoryRecord - Individual address list entries
- MemoryRecordHotkey - Hotkeys attached to memory records
- Panel - Parent class
- WinControl - Grandparent class
- Control - Base control class
- Component - Base component class