Skip to content

memory ​

Work with owned buffers, pointer lifetimes, and memory operations.

The memory interface provides owned local buffers and access to known local-process addresses. It does not expose an arbitrary C++ function-call interface. Native hooks are provided separately through natives.hook. The reference here describes the available operations without prescribing game-address patches or undocumented layouts.

Namespace reference ​

memory.alloc ​

Zeroed buffer, 1..1,048,576 bytes, aligned to 8 bytes.

text
memory.alloc(bytes)

Returns: Owned Pointer

memory.pointer ​

Wraps an exact integer or unsigned decimal/hex address string; wrapping alone does not validate memory.

text
memory.pointer(address)

Returns: Borrowed Pointer

memory.scan ​

Initial-load phase only; unique executable-region match, otherwise a null pointer.

text
memory.scan(signature)

Returns: Borrowed Pointer

memory.scan_all ​

Initial-load phase only; zero matches gives an empty array, more than 512 matches raises an error.

text
memory.scan_all(signature)

Returns: Pointer array

memory.patch ​

Initial-load phase only; records original bytes and a replacement for conditional restoration.

text
memory.patch(pointer, bytes)

Returns: Nothing

“Initial-load phase” means execution of the entry chunk, including a module required during that execution. It does not include later menu or timer callbacks. Registering a callback does not itself end the phase, but scanning and patching should be resolved during initialization.

Signatures use whitespace-separated hexadecimal bytes and ? or ?? wildcards. They must contain at least one concrete byte and occupy 1–1,024 characters. The scan region is executable memory in the main game image. A failed or ambiguous unique scan returns a null Pointer, not nil.

Pointer reference ​

All methods use colon syntax. pointer.address is a read-only numeric property.

pointer:is_null ​

Whether the address is zero.

text
pointer:is_null()

Returns: Boolean

pointer:add ​

Signed offset with arithmetic-overflow checks; keeps an owned buffer's ownership.

text
pointer:add(offset)

Returns: Pointer

pointer:rip ​

Resolves a signed 32-bit relative displacement from a known instruction layout.

text
pointer:rip(displacement_offset, instruction_length)

Returns: Pointer

pointer:read_bytes ​

Copies 1..1,048,576 bytes.

text
pointer:read_bytes(count)

Returns: Binary string

pointer:write_bytes ​

Writes a nonempty binary string up to 1 MiB to writable memory.

text
pointer:write_bytes(bytes)

Returns: Nothing

pointer:read_string ​

Reads a terminated string; default limit 4,096, valid range 1 byte–1 MiB.

text
pointer:read_string([limit])

Returns: String

pointer:read_u8 ​

Reads an unsigned 8-bit value.

text
pointer:read_u8()

Returns: Integer

pointer:read_u16 ​

Reads an unsigned 16-bit value.

text
pointer:read_u16()

Returns: Integer

pointer:read_u32 ​

Reads an unsigned 32-bit value.

text
pointer:read_u32()

Returns: Integer

pointer:read_i32 ​

Reads a signed 32-bit value.

text
pointer:read_i32()

Returns: Integer

pointer:read_i64 ​

Reads a signed 64-bit value.

text
pointer:read_i64()

Returns: Integer

pointer:read_float ​

Reads a 32-bit float.

text
pointer:read_float()

Returns: Number

pointer:read_double ​

Reads a 64-bit floating-point value.

text
pointer:read_double()

Returns: Number

pointer:read_pointer ​

Reads an address; does not inherit ownership of the referenced allocation.

text
pointer:read_pointer()

Returns: Borrowed Pointer

pointer:write_u8 ​

Writes an unsigned 8-bit value.

text
pointer:write_u8(value)

Returns: Nothing

pointer:write_u16 ​

Writes an unsigned 16-bit value.

text
pointer:write_u16(value)

Returns: Nothing

pointer:write_u32 ​

Writes an unsigned 32-bit value.

text
pointer:write_u32(value)

Returns: Nothing

pointer:write_i32 ​

Writes a signed 32-bit value.

text
pointer:write_i32(value)

Returns: Nothing

pointer:write_i64 ​

Writes a signed 64-bit value.

text
pointer:write_i64(value)

Returns: Nothing

pointer:write_float ​

Writes a 32-bit float.

text
pointer:write_float(value)

Returns: Nothing

pointer:write_double ​

Writes a 64-bit floating-point value.

text
pointer:write_double(value)

Returns: Nothing

Use values representable by the named type. There are no read_u64, write_u64, read_vec3, write_string, or explicit free methods in this binding. Binary byte reads/writes may contain zero bytes; they use the buffer limit rather than the text limit.

read_string() raises an error when no terminator is found within its limit; it does not silently truncate. For rip(), displacement offset must be nonnegative, instruction length must exceed that offset, and at least four displacement bytes must fit within the instruction length. The result is base + instruction_length + signed_displacement.

Owned allocations remain alive while their owning handles remain referenced. Derived pointers preserve that ownership, but access beyond the original logical buffer remains invalid. Pointer arithmetic alone does not prove that the result lies inside the buffer. A pointer read from a buffer is a borrowed address, not a new ownership relationship.

Reads and writes validate the requested range. Ordinary writes require writable pages and do not change page protections. Page accessibility does not establish that a game object is still alive; revalidate borrowed addresses and avoid retaining them across session/lifetime changes.

Owned-buffer example ​

This example uses only memory allocated by the script:

lua
local buffer = memory.alloc(16)
buffer:write_u32(1234)
buffer:add(4):write_float(2.5)
buffer:add(8):write_bytes("Lua\0")

assert(buffer:read_u32() == 1234)
assert(buffer:add(4):read_float() == 2.5)
assert(buffer:add(8):read_string(4) == "Lua")

util.log("Owned buffer checks passed")

Tracked patch cleanup ​

memory.patch() allows up to 64 tracked patches per script. It records the original bytes and retains owned-buffer storage when applicable. It uses the shared memory writer for protection changes and instruction-cache handling. Failure raises an error.

Cleanup processes patches in reverse order. It restores an original range only when the current bytes still equal that script's replacement, avoiding overwriting a later owner. A failed or conflicting restore is reported; shutdown still proceeds and clears the patch records. Do not describe restoration as guaranteed, transactional rollback of all game state, or a mechanism that keeps retrying after unload. There is no public manual-unpatch function.