The mem function reads and writes the game's memory at any address, in the same way as LunaLua's mem. Every live object additionally has a mem method, a memdump method and an address field relative to its own record; those, and the offsets found so far, are documented under Instance Memory Offsets on the vehicle, prop and actor pages. Players have the same three, relative to their game logic object. This page covers the global addresses.

WARNING: The addresses are the Windows build's. A read returns whatever is at the address, and a wrong write crashes the game or corrupts the race with no error of any kind. Reads are guarded against unmapped pages and nothing else.

Static members#

These elements are static to the class itself and can be accessed like so:

local mode = ss.mem(0xd2c728, "int")

Static functions#

FunctionReturn ValuesDescription
ss.mem( integer address, string type ) any value Returns the value of the given type at the given address. Returns nil when the address is not mapped.
ss.mem( integer address, string type, any value ) boolean written Sets the value of the given type at the given address. Returns whether the write could be made: false for an unmapped page. A read-only page, such as code or constants, is unprotected for the write and protected again afterwards.
ss.mem.readable( integer address, integer size ) boolean readable Returns whether the given number of bytes at the given address are mapped. This is the same test a read makes.

Types#

The type names a read or write accepts. Each entry lists its aliases.

TypeFieldDescription
integer byte, u8 One unsigned byte.
integer sbyte, i8 One signed byte.
integer word, u16 Two bytes, unsigned.
integer short, i16 Two bytes, signed.
integer dword, u32, pointer Four bytes, unsigned. A pointer is read and written as one of these.
integer int, i32 Four bytes, signed.
number float, f32 A single-precision floating point value.
number double, f64 A double-precision floating point value.
boolean bool One byte, read as true for any value other than 0. A write stores 1 or 0.
string string A NUL-terminated string. A number as the third argument is the longest string to read, 256 by default, so ss.mem(addr, "string", 2) reads two characters and never writes. A write takes a string and stores it with its terminator.

Guards#

Reads are guarded: an address whose page is not mapped returns nil rather than crashing, and ss.mem.readable asks the same question without reading. Writes return true, or false for an unmapped page. Each guarded access is a page query, which takes tens of microseconds on a virtual machine: suitable for a script that reads a few values when something happens, not for thousands of reads per frame. LuaJIT's ffi remains available for unguarded, faster access.

Translated builds#

On the translated builds (Linux, Windows, macOS, Android and iOS) the same addresses are guest memory and ss.mem reaches them in the same way. The hook's own patches differ between builds, however, so anything a mod depends on is better served by a seam in the API than by a write to an address.

Global memory offsets#

These addresses can be read and written with ss.mem. They are absolute.

WARNING: Be careful when manipulating memory addresses directly! Doing so may yield unexpected results.
FieldFieldtypeDescription
0xd2c728 int The race's mode index. 6 means no mode: the front end, or a dev-boot race, which never sets it. Use field ss.race.mode_index
0xd13e14 byte The engine log's master enable.
0xd65f8c pointer The engine log's filter callback.
0xd59698 pointer The Nemesis selection object. The 24 slot indices that ss.race.nemesis_slots writes are at +0x14 from there.

Example#

local mode = ss.mem(0xd2c728, "int")          -- the race's mode index
ss.mem(0xd13e14, "byte", 1)                   -- enables the engine log
local vx = ss.vehicle:mem(0x17bc, "float")    -- the car's cached velocity x, through the instance method
ss.vehicle:memdump(0x1750, 0x1810)            -- the position, rotation and velocity block, dword by dword