Difference between revisions of "Lua:getAddressSafe"
m (Reverted edits by This content is not available (Talk) to last revision by TheyCallMeTim13) |
Mr millchick (talk | contribs) (added more detailed documentation as well as examples and comparison with getAddress) |
||
| Line 1: | Line 1: | ||
[[Category:Lua]] | [[Category:Lua]] | ||
| − | '''function''' getAddressSafe(''AddressString'', ''local'' OPTIONAL) ''':''' integer | + | '''function''' getAddressSafe(''AddressString'', ''local'' OPTIONAL, ''shallow'' OPTIONAL) ''':''' integer |
| − | + | Safely resolves a symbol name to a memory address without raising exceptions. Returns nil on failure instead of throwing an error. | |
| + | |||
| + | '''Note:''' This is the recommended function for symbol resolution when the symbol might not exist. | ||
| + | |||
| + | === Function Parameters === | ||
| − | |||
{|width="85%" cellpadding="10%" cellpadding="5%" cellspacing="0" border="0" | {|width="85%" cellpadding="10%" cellpadding="5%" cellspacing="0" border="0" | ||
!align="left"|Parameter | !align="left"|Parameter | ||
!align="left"|Type | !align="left"|Type | ||
| − | !style="width: 80% | + | !style="width: 80%;" align="left"|Description |
|- | |- | ||
|AddressString | |AddressString | ||
| − | |[[CEAddressString]] | + | |string or number ('''[[CEAddressString]]''') |
| − | |The | + | |The symbol to resolve (module name, address expression, label, pointer expression). If a '''number''' is provided, it's returned unchanged |
|- | |- | ||
|local | |local | ||
| − | | | + | |boolean |
| − | | | + | |'''Optional.''' If true, uses Cheat Engine's self symbol handler instead of the target process. Default: '''false''' |
| + | |- | ||
| + | |shallow | ||
| + | |boolean | ||
| + | |'''Optional.''' If true, performs a shallow (faster) symbol lookup without deep parsing. Default: '''false''' | ||
|} | |} | ||
| + | |||
| + | |||
| + | === Returns === | ||
| + | An integer representing the resolved address on success, or '''nil''' if resolution fails. | ||
| + | |||
| + | === Description === | ||
| + | |||
| + | This function works identically to [[Lua:getAddress|getAddress]] but returns nil on failure instead of raising an exception. It resolves: | ||
| + | |||
| + | * Module names (e.g., "game.exe", "kernel32.dll") | ||
| + | * Module + offset expressions (e.g., "game.exe+1234") | ||
| + | * Symbol names from debug symbols or exports | ||
| + | * Registered symbols from '''registerSymbol()''' | ||
| + | * Auto-assembler labels | ||
| + | * Pointer expressions | ||
| + | |||
| + | |||
| + | === Return Behavior === | ||
| + | |||
| + | * '''Success:''' Returns the resolved address as an integer | ||
| + | * '''Failure:''' Returns nil (nothing pushed to Lua stack) | ||
| + | * '''Numeric input:''' Returns the number unchanged | ||
| + | * '''Exceptions:''' Catches all exceptions and returns nil | ||
| + | |||
| + | |||
| + | === Usage Examples === | ||
| + | |||
| + | '''Basic safe resolution:''' | ||
| + | local address = getAddressSafe("game.exe+12345") | ||
| + | if address then | ||
| + | print(string.format("Address: %X", address)) | ||
| + | else | ||
| + | print("Failed to resolve symbol") | ||
| + | end | ||
| + | |||
| + | '''Check if module exists:''' | ||
| + | local baseAddr = getAddressSafe("optionalModule.dll") | ||
| + | if baseAddr then | ||
| + | print("Module loaded at: " .. baseAddr) | ||
| + | else | ||
| + | print("Optional module not loaded") | ||
| + | end | ||
| + | |||
| + | '''Conditional feature detection:''' | ||
| + | local featureAddr = getAddressSafe("game.exe+NewFeatureOffset") | ||
| + | if featureAddr then | ||
| + | -- New version with feature | ||
| + | print("New version detected") | ||
| + | enableNewFeature(featureAddr) | ||
| + | else | ||
| + | -- Old version without feature | ||
| + | print("Old version, feature not available") | ||
| + | useOldMethod() | ||
| + | end | ||
| + | |||
| + | '''Safe symbol registration:''' | ||
| + | function tryRegisterSymbol(name, expr) | ||
| + | local addr = getAddressSafe(expr) | ||
| + | if addr then | ||
| + | registerSymbol(name, addr) | ||
| + | print(string.format("Registered %s = %X", name, addr)) | ||
| + | return true | ||
| + | else | ||
| + | print("Warning: Failed to register " .. name) | ||
| + | return false | ||
| + | end | ||
| + | end | ||
| + | |||
| + | tryRegisterSymbol("PlayerHealth", "game.exe+ABCD") | ||
| + | |||
| + | '''Version-dependent code:''' | ||
| + | local v1Addr = getAddressSafe("game.exe+1000") | ||
| + | local v2Addr = getAddressSafe("game.exe+2000") | ||
| + | |||
| + | if v2Addr and readBytes(v2Addr) == 0x48 then | ||
| + | print("Using version 2 offsets") | ||
| + | healthAddr = v2Addr | ||
| + | elseif v1Addr then | ||
| + | print("Using version 1 offsets") | ||
| + | healthAddr = v1Addr | ||
| + | else | ||
| + | print("Unknown version!") | ||
| + | return false | ||
| + | end | ||
| + | |||
| + | '''Numeric pass-through:''' | ||
| + | local addr = getAddressSafe(0x12345678) | ||
| + | -- Returns 0x12345678 unchanged | ||
| + | print(addr) -- 305419896 | ||
| + | |||
| + | '''With shallow lookup (faster):''' | ||
| + | -- Shallow lookup is faster but may miss complex expressions | ||
| + | local addr = getAddressSafe("game.exe", false, true) | ||
| + | if addr then | ||
| + | print("Quick module lookup: " .. addr) | ||
| + | end | ||
| + | |||
| + | |||
| + | === Shallow vs Deep Lookup === | ||
| + | |||
| + | The '''''shallow''''' parameter controls the symbol resolution depth: | ||
| + | |||
| + | '''Deep lookup (shallow = false, default):''' | ||
| + | * Performs full expression parsing | ||
| + | * Handles complex pointer arithmetic | ||
| + | * Resolves nested brackets and offsets | ||
| + | * Slower but more comprehensive | ||
| + | |||
| + | '''Shallow lookup (shallow = true):''' | ||
| + | * Quick module/symbol name lookup | ||
| + | * Skips complex expression evaluation | ||
| + | * Faster for simple module names | ||
| + | * May fail on complex expressions | ||
| + | |||
| + | '''Example:''' | ||
| + | -- Deep lookup (handles complex expressions) | ||
| + | local addr1 = getAddressSafe("[[game.exe+100]+8]+4", false, false) | ||
| + | |||
| + | -- Shallow lookup (fast, simple names only) | ||
| + | local addr2 = getAddressSafe("game.exe", false, true) | ||
| + | |||
| + | |||
| + | === Comparison: getAddress vs getAddressSafe === | ||
| + | |||
| + | {|width="85%" cellpadding="10%" cellpadding="5%" cellspacing="0" border="1" | ||
| + | !Feature | ||
| + | !getAddress | ||
| + | !getAddressSafe | ||
| + | |- | ||
| + | |On failure | ||
| + | |Raises exception | ||
| + | |Returns nil | ||
| + | |- | ||
| + | |Error handling | ||
| + | |Requires pcall or try/catch | ||
| + | |Simple if/then check | ||
| + | |- | ||
| + | |Use case | ||
| + | |Symbol must exist | ||
| + | |Symbol might not exist | ||
| + | |- | ||
| + | |Performance | ||
| + | |Same | ||
| + | |Slightly faster (no exception overhead on failure) | ||
| + | |- | ||
| + | |Code complexity | ||
| + | |More complex (needs pcall) | ||
| + | |Simpler (direct nil check) | ||
| + | |} | ||
| + | |||
| + | |||
| + | === Advanced Examples === | ||
| + | |||
| + | '''Multi-version support:''' | ||
| + | local offsets = { | ||
| + | v1 = "game.exe+10000", | ||
| + | v2 = "game.exe+20000", | ||
| + | v3 = "game.exe+30000" | ||
| + | } | ||
| + | |||
| + | local healthAddr = nil | ||
| + | for version, offset in pairs(offsets) do | ||
| + | local addr = getAddressSafe(offset) | ||
| + | if addr and readBytes(addr) == 0x89 then -- Signature check | ||
| + | healthAddr = addr | ||
| + | print("Detected " .. version) | ||
| + | break | ||
| + | end | ||
| + | end | ||
| + | |||
| + | if not healthAddr then | ||
| + | print("ERROR: No compatible version found!") | ||
| + | return false | ||
| + | end | ||
| + | |||
| + | '''Graceful degradation:''' | ||
| + | function findPlayerHealth() | ||
| + | -- Try primary location | ||
| + | local addr = getAddressSafe("PlayerHealthPrimary") | ||
| + | if addr then return addr end | ||
| + | |||
| + | -- Try secondary location | ||
| + | addr = getAddressSafe("game.exe+ABCD") | ||
| + | if addr then return addr end | ||
| + | |||
| + | -- Try pattern scan as fallback | ||
| + | addr = AOBScanUnique("89 44 24 ?? 8B 45", "+X") | ||
| + | if addr then | ||
| + | registerSymbol("PlayerHealthPrimary", addr) | ||
| + | return addr | ||
| + | end | ||
| + | |||
| + | return nil -- All methods failed | ||
| + | end | ||
| + | |||
| + | local health = findPlayerHealth() | ||
| + | if health then | ||
| + | print("Health found at: " .. health) | ||
| + | else | ||
| + | print("Could not locate health!") | ||
| + | end | ||
| + | |||
| + | '''Validate before use:''' | ||
| + | function safeWriteInteger(symbol, value) | ||
| + | local addr = getAddressSafe(symbol) | ||
| + | if not addr then | ||
| + | print("Error: Cannot resolve " .. symbol) | ||
| + | return false | ||
| + | end | ||
| + | |||
| + | writeInteger(addr, value) | ||
| + | return true | ||
| + | end | ||
| + | |||
| + | if safeWriteInteger("PlayerGold", 9999) then | ||
| + | print("Gold updated successfully") | ||
| + | end | ||
| + | |||
| + | |||
| + | === Exception Safety === | ||
| + | |||
| + | getAddressSafe catches ALL exceptions during symbol resolution: | ||
| + | |||
| + | -- These all return nil instead of crashing: | ||
| + | local addr1 = getAddressSafe("!@#$%^&*()") -- Invalid syntax | ||
| + | local addr2 = getAddressSafe(nil) -- Nil input | ||
| + | local addr3 = getAddressSafe("") -- Empty string | ||
| + | local addr4 = getAddressSafe("[[[[[") -- Malformed brackets | ||
| + | |||
| + | |||
| + | === Performance Considerations === | ||
| + | |||
| + | * Shallow lookups are faster for simple module names | ||
| + | * Deep lookups required for complex expressions | ||
| + | * Symbol resolution waits for symbol loading (can block) | ||
| + | * Cache resolved addresses in variables for repeated use: | ||
| + | |||
| + | -- Bad: Resolves every iteration | ||
| + | for i = 1, 1000 do | ||
| + | local addr = getAddressSafe("game.exe+1234") | ||
| + | writeInteger(addr, i) | ||
| + | end | ||
| + | |||
| + | -- Good: Resolve once, reuse | ||
| + | local addr = getAddressSafe("game.exe+1234") | ||
| + | if addr then | ||
| + | for i = 1, 1000 do | ||
| + | writeInteger(addr, i) | ||
| + | end | ||
| + | end | ||
| + | === Common Patterns === | ||
| + | |||
| + | '''Assert pattern (fail fast):''' | ||
| + | local addr = getAddressSafe("game.exe+1234") | ||
| + | assert(addr, "Failed to resolve game.exe+1234") | ||
| + | -- Continue with addr | ||
| + | |||
| + | '''Default value pattern:''' | ||
| + | local addr = getAddressSafe("game.exe+1234") or 0x400000 | ||
| + | -- Use default if resolution fails | ||
| + | |||
| + | '''Chain pattern (try multiple):''' | ||
| + | local addr = getAddressSafe("NewSymbol") or | ||
| + | getAddressSafe("OldSymbol") or | ||
| + | getAddressSafe("game.exe+1234") | ||
| Line 30: | Line 302: | ||
* [[Lua:targetIs64Bit|targetIs64Bit]] | * [[Lua:targetIs64Bit|targetIs64Bit]] | ||
* [[Lua:enumModules|enumModules]] | * [[Lua:enumModules|enumModules]] | ||
| + | * [[Lua:registerSymbol|registerSymbol]] | ||
| + | * [[Lua:unregisterSymbol|unregisterSymbol]] | ||
| + | * [[Lua:AOBScanUnique|AOBScanUnique]] | ||
Latest revision as of 00:19, 21 October 2025
function getAddressSafe(AddressString, local OPTIONAL, shallow OPTIONAL) : integer
Safely resolves a symbol name to a memory address without raising exceptions. Returns nil on failure instead of throwing an error.
Note: This is the recommended function for symbol resolution when the symbol might not exist.
Contents
Function Parameters[edit]
| Parameter | Type | Description |
|---|---|---|
| AddressString | string or number (CEAddressString) | The symbol to resolve (module name, address expression, label, pointer expression). If a number is provided, it's returned unchanged |
| local | boolean | Optional. If true, uses Cheat Engine's self symbol handler instead of the target process. Default: false |
| shallow | boolean | Optional. If true, performs a shallow (faster) symbol lookup without deep parsing. Default: false |
Returns[edit]
An integer representing the resolved address on success, or nil if resolution fails.
Description[edit]
This function works identically to getAddress but returns nil on failure instead of raising an exception. It resolves:
- Module names (e.g., "game.exe", "kernel32.dll")
- Module + offset expressions (e.g., "game.exe+1234")
- Symbol names from debug symbols or exports
- Registered symbols from registerSymbol()
- Auto-assembler labels
- Pointer expressions
Return Behavior[edit]
- Success: Returns the resolved address as an integer
- Failure: Returns nil (nothing pushed to Lua stack)
- Numeric input: Returns the number unchanged
- Exceptions: Catches all exceptions and returns nil
Usage Examples[edit]
Basic safe resolution:
local address = getAddressSafe("game.exe+12345")
if address then
print(string.format("Address: %X", address))
else
print("Failed to resolve symbol")
end
Check if module exists:
local baseAddr = getAddressSafe("optionalModule.dll")
if baseAddr then
print("Module loaded at: " .. baseAddr)
else
print("Optional module not loaded")
end
Conditional feature detection:
local featureAddr = getAddressSafe("game.exe+NewFeatureOffset")
if featureAddr then
-- New version with feature
print("New version detected")
enableNewFeature(featureAddr)
else
-- Old version without feature
print("Old version, feature not available")
useOldMethod()
end
Safe symbol registration:
function tryRegisterSymbol(name, expr)
local addr = getAddressSafe(expr)
if addr then
registerSymbol(name, addr)
print(string.format("Registered %s = %X", name, addr))
return true
else
print("Warning: Failed to register " .. name)
return false
end
end
tryRegisterSymbol("PlayerHealth", "game.exe+ABCD")
Version-dependent code:
local v1Addr = getAddressSafe("game.exe+1000")
local v2Addr = getAddressSafe("game.exe+2000")
if v2Addr and readBytes(v2Addr) == 0x48 then
print("Using version 2 offsets")
healthAddr = v2Addr
elseif v1Addr then
print("Using version 1 offsets")
healthAddr = v1Addr
else
print("Unknown version!")
return false
end
Numeric pass-through:
local addr = getAddressSafe(0x12345678) -- Returns 0x12345678 unchanged print(addr) -- 305419896
With shallow lookup (faster):
-- Shallow lookup is faster but may miss complex expressions
local addr = getAddressSafe("game.exe", false, true)
if addr then
print("Quick module lookup: " .. addr)
end
Shallow vs Deep Lookup[edit]
The shallow parameter controls the symbol resolution depth:
Deep lookup (shallow = false, default):
- Performs full expression parsing
- Handles complex pointer arithmetic
- Resolves nested brackets and offsets
- Slower but more comprehensive
Shallow lookup (shallow = true):
- Quick module/symbol name lookup
- Skips complex expression evaluation
- Faster for simple module names
- May fail on complex expressions
Example:
-- Deep lookup (handles complex expressions)
local addr1 = getAddressSafe("[[game.exe+100]+8]+4", false, false)
-- Shallow lookup (fast, simple names only)
local addr2 = getAddressSafe("game.exe", false, true)
Comparison: getAddress vs getAddressSafe[edit]
| Feature | getAddress | getAddressSafe |
|---|---|---|
| On failure | Raises exception | Returns nil |
| Error handling | Requires pcall or try/catch | Simple if/then check |
| Use case | Symbol must exist | Symbol might not exist |
| Performance | Same | Slightly faster (no exception overhead on failure) |
| Code complexity | More complex (needs pcall) | Simpler (direct nil check) |
Advanced Examples[edit]
Multi-version support:
local offsets = {
v1 = "game.exe+10000",
v2 = "game.exe+20000",
v3 = "game.exe+30000"
}
local healthAddr = nil
for version, offset in pairs(offsets) do
local addr = getAddressSafe(offset)
if addr and readBytes(addr) == 0x89 then -- Signature check
healthAddr = addr
print("Detected " .. version)
break
end
end
if not healthAddr then
print("ERROR: No compatible version found!")
return false
end
Graceful degradation:
function findPlayerHealth()
-- Try primary location
local addr = getAddressSafe("PlayerHealthPrimary")
if addr then return addr end
-- Try secondary location
addr = getAddressSafe("game.exe+ABCD")
if addr then return addr end
-- Try pattern scan as fallback
addr = AOBScanUnique("89 44 24 ?? 8B 45", "+X")
if addr then
registerSymbol("PlayerHealthPrimary", addr)
return addr
end
return nil -- All methods failed
end
local health = findPlayerHealth()
if health then
print("Health found at: " .. health)
else
print("Could not locate health!")
end
Validate before use:
function safeWriteInteger(symbol, value)
local addr = getAddressSafe(symbol)
if not addr then
print("Error: Cannot resolve " .. symbol)
return false
end
writeInteger(addr, value)
return true
end
if safeWriteInteger("PlayerGold", 9999) then
print("Gold updated successfully")
end
Exception Safety[edit]
getAddressSafe catches ALL exceptions during symbol resolution:
-- These all return nil instead of crashing:
local addr1 = getAddressSafe("!@#$%^&*()") -- Invalid syntax
local addr2 = getAddressSafe(nil) -- Nil input
local addr3 = getAddressSafe("") -- Empty string
local addr4 = getAddressSafe("[[[[[") -- Malformed brackets
Performance Considerations[edit]
- Shallow lookups are faster for simple module names
- Deep lookups required for complex expressions
- Symbol resolution waits for symbol loading (can block)
- Cache resolved addresses in variables for repeated use:
-- Bad: Resolves every iteration
for i = 1, 1000 do
local addr = getAddressSafe("game.exe+1234")
writeInteger(addr, i)
end
-- Good: Resolve once, reuse
local addr = getAddressSafe("game.exe+1234")
if addr then
for i = 1, 1000 do
writeInteger(addr, i)
end
end
Common Patterns[edit]
Assert pattern (fail fast):
local addr = getAddressSafe("game.exe+1234")
assert(addr, "Failed to resolve game.exe+1234")
-- Continue with addr
Default value pattern:
local addr = getAddressSafe("game.exe+1234") or 0x400000
-- Use default if resolution fails
Chain pattern (try multiple):
local addr = getAddressSafe("NewSymbol") or
getAddressSafe("OldSymbol") or
getAddressSafe("game.exe+1234")