Lua:getAddressSafe
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
| 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
An integer representing the resolved address on success, or nil if resolution fails.
Description
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
- 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
| 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")

