---
title: Memory
nav_order: 11
---

# Memory

Low-level primitives for reading/writing the Roblox process by **raw numeric address** — not through Instance userdata. This is a reverse-engineering layer: there is no bounds checking, no type checking against a real property, and no safety net. A wrong or stale address can read garbage or crash the game.

`addrof(instance)` turns an Instance userdata into the raw address these functions expect; a number is also accepted directly (already-an-address, or computed with an offset).

## Read / write primitives

```lua
rdq(addr): number?      -- uint64
rdd(addr): number?      -- uint32
rdw(addr): number?      -- uint16
rdb(addr): number?      -- uint8
rdf(addr): number?      -- float

wrq(addr, value): boolean
wrd(addr, value): boolean
wrw(addr, value): boolean
wrb(addr, value): boolean
wrf(addr, value): boolean
```

Reads return `nil` on an implausible/unreadable address; writes return `true`/`false`.

## Strings

```lua
rdcstr(addr, max: number?): string?   -- raw C string, default max 64 bytes, capped at 512
rdstr(addr): string?                  -- std::string at addr (small-string-optimized, reads the real length)
wrstr(addr, text: string): boolean    -- writes a std::string at addr
wrcontent(addr, text: string): boolean -- writes a std::string for a Content field and resets its cached URI scheme
```

`wrstr` handles both the inline (`< 16` bytes) and heap-allocated `std::string` layouts — for long strings it allocates a fresh buffer in the game process via `gatecall` and repoints the string at it (the old buffer is intentionally not freed, since the engine may still be reading it for rendering).

`wrcontent` is `wrstr` plus clearing the scheme byte Roblox caches alongside a `Content` field (texture/sound/mesh id string). Content fields remember whether their string looked like `rbxasset://`, `rbxassetid://`, `http://`, etc.; without resetting that cache the engine keeps parsing your new string using the old scheme and the asset never loads. Used for skybox faces, decals and similar id fields — see `scripts/custom_skybox.lua` and `scripts/weather.lua`.

```lua
local at = addrof(sky)
wrcontent(at + 0xf8, "rbxassetid://15536110634")  -- Sky face texture, offset from reflect_probe.lua
```

## Reflection

```lua
refname(name: string): number?           -- interned RBX::Name* for a string, or nil
refcreator(name: string | number): number? -- class creator/factory pointer for a class name (or its RBX::Name* address)
classdesc(addrOrInstance): number?       -- class descriptor pointer of an instance
rbxbase(): number                        -- RobloxPlayerBeta.exe module base address
addrof(instanceOrNumber): number         -- numeric address of an Instance userdata (passthrough for numbers)
get_class(addr): string?                 -- class name of the instance at addr, without a userdata handle
get_name(addr): string?                  -- .Name of the instance at addr
```

`get_class` / `get_name` are also available as `mem.get_class` / `mem.get_name`.

## Scanning

```lua
scanptr(addr, needle: number, span: number?): { number }
```

Scans `span` bytes (default `0x400`, capped `0x10000`) starting at `addr` for 8-byte-aligned occurrences of the `needle` qword and returns the list of byte offsets where it was found. Useful for locating a pointer to a known object inside a struct you don't have offsets for yet.

## CallGate access

```lua
gatecall(fnAddr: number, a0?: number, a1?: number, a2?: number, a3?: number): number?
gatescratch(): number
```

`gatecall` invokes an arbitrary function pointer **on the real game thread** through CallGate — the same mechanism `Instance.new` and `.Parent =` use internally (see [Instance](instance.md) for how the hijack works). Arguments map to `rcx`/`rdx`/`r8`/`r9`; the return value comes from `rax`. On failure it returns `nil` plus a reason string (`"bad function address"`, `"no call gate"`, `"gate timeout"`).

`gatescratch()` returns the address of a small (≥ 256 byte) scratch buffer inside the game process, useful as an out-parameter buffer for calls that write their result through a pointer argument instead of returning it in `rax`.

**This is the sharpest edge of the API.** Calling the wrong address, or the right address with the wrong calling convention/argument count, can crash or corrupt the game. Prefer the higher-level `Instance.new` / property setters / `mem.wrcontent` etc. unless you specifically need to invoke engine code that isn't wrapped yet.

## `mem` table

All of the above are also grouped under a `mem` table — same functions, same behavior as the flat globals:

```lua
mem.rdq / mem.rdd / mem.rdw / mem.rdb / mem.rdf
mem.wrq / mem.wrd / mem.wrw / mem.wrb / mem.wrf
mem.rdcstr / mem.rdstr / mem.wrstr / mem.wrcontent
mem.gatecall / mem.gatescratch
mem.rbxbase / mem.addrof / mem.refname / mem.refcreator / mem.classdesc / mem.scanptr
mem.get_class / mem.get_name
```

The flat globals (`rdq`, `wrstr`, `gatecall`, `get_class`, …) are unchanged and keep working exactly as before — `mem.*` is an additional namespaced form, not a replacement.
