Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Example scripts

Basic overlay usage

-- An overlay is a special Lua type that allows access to the memory of a structure.
-- It has index and newindex metamethods that access to the members of the structure
-- seamlessly, with no magic numbers/offsets, completely externally.
local foo = regenny:overlay()
if not foo or foo:type():name() ~= "Foo" then
    print("Overlay is not a Foo, cannot use this script.")
    return
end

-- Dynamically read the structure memory from the target process.
print(foo.a) -- prints the value of foo.a
print(foo.b) -- prints the value of foo.b, will be a string

-- Setter demonstrations
-- These actually set the values within the target process's structure behind the scenes.
foo.c = 0x12345 -- sets foo.c to 0x12345
foo.e = 3.14159 -- sets foo.e to 3.14159

local bar = foo.bar -- gets the value of foo.bar

-- Because pointers can be null.
if bar ~= nil then
    bar.b = 0x1337 -- sets bar.b to 0x1337
    bar.c = bar.b + 0x100 -- sets bar.c to bar.b + 0x100

    print(bar.parent:ptr() == foo:address()) -- prints true if bar.parent is equal to foo
    if bar.parent:ptr() ~= foo:address() then
        -- sets bar.parent to foo
        -- pointers can be explicitly written to with a raw integer value
        bar.parent = foo:address()
    end
end

Standalone usage with sdkgenny.parse_file()

Outside of the ReGenny GUI, you can parse a .genny schema file directly and build overlays manually.

-- Parse the schema
local sdk = sdkgenny.parse_file("types.genny")
local ns = sdk:global_ns()

-- Resolve a type through namespaces
local my_struct = ns:namespace("engine"):struct("Player")

-- Build an overlay at a known address
local player = sdkgenny.StructOverlay(0xDEADBEEF, my_struct)
print(player.health)
print(player.position.x, player.position.y, player.position.z)

Resolving C++ type names to genny types

When working with RTTI strings like "class engine::Player", you can parse them into namespace and type lookups:

function resolve_type(typename)
    local ns = sdk:global_ns()
    -- Split on "::" and strip "class " prefix
    local pieces = {}
    for piece in typename:gmatch("([^:]+)") do
        piece = piece:gsub("^class ", "")
        table.insert(pieces, piece)
    end

    local result = nil
    for i = 1, #pieces do
        if i < #pieces then
            -- Intermediate pieces are namespaces
            if i == 1 then
                result = ns:find_namespace(pieces[i])
            elseif result then
                result = result:find_namespace(pieces[i])
            end
        else
            -- Last piece is the struct name
            if i == 1 then
                result = ns:find_struct(pieces[i])
            elseif result then
                result = result:find_struct(pieces[i])
            end
        end
    end

    return result
end

local t = resolve_type("class engine::Player")
if t then
    print("Found: " .. t:name()) -- prints "Player"
end

Nested struct fields and pointer fields are automatically wrapped in StructOverlay or PointerOverlay. You can chain through them with nil-guards for safety:

local entity = sdkgenny.StructOverlay(entity_addr, entity_type)

-- Chain through nested structs and pointers
-- Each field access returns an overlay if the field is a struct or pointer
local weapon_manager = entity.weapon_manager
local weapon_storage = weapon_manager and weapon_manager.weapon_storage
local weapon_slots = weapon_storage and weapon_storage.weapon_slots
local weapon = weapon_slots and weapon_slots.active_weapon

if weapon then
    print("Weapon: " .. weapon.name)
end

Pointer arrays and iteration

A common pattern for dynamic arrays in C++ uses T** data; uint32_t size;. Access the pointer-to-pointer field, then index and dereference:

local entity_list = world.entity_list

-- entity_list.data is a T** (pointer to pointer)
-- entity_list.data[i] indexes into the pointer array
-- :deref() follows the pointer to get the actual object
for i = 0, entity_list.size - 1 do
    local entity = entity_list.data[i]:deref()
    if entity then
        print(entity.name)
    end
end

Writing values through overlays

Overlay field writes go directly to the target process memory:

local entity = regenny:overlay()

-- Write primitive fields
entity.health = 100
entity.armor = 50

-- Write through nested struct overlays
local transform = entity.transform
if transform then
    transform.w.x = new_x
    transform.w.y = new_y
    transform.w.z = new_z
end

-- Write pointer values (set a pointer to a new address)
entity.target = some_address
entity.target = nil  -- null the pointer (sets to 0)

Pointer overlay methods

PointerOverlay provides several ways to interact with pointer-typed fields:

local ptr = entity.some_pointer

-- Get the address of the pointer itself (where the pointer is stored)
local ptr_storage_addr = ptr:address()

-- Get the address the pointer points to
local pointed_to_addr = ptr:ptr()
-- ptr:p() is an alias for ptr:ptr()

-- Dereference the pointer to get the pointed-to value
local value = ptr:deref()
-- ptr:d() and ptr:dereference() are aliases for ptr:deref()

-- Array-style access through pointer arithmetic
local third_element = ptr[2]  -- *(ptr + 2)

Using process read/write with overlay addresses

For operations the overlay system doesn’t handle directly, combine overlay addresses with process read/write methods:

local process = regenny:process()

-- Read raw memory at an overlay field's address
local some_overlay = regenny:overlay()
local raw_value = process:read_uint64(some_overlay.some_field:address())

-- Pointer arithmetic from overlay addresses
local base = some_overlay:address()
local custom_offset_value = process:read_float(base + 0x30)

-- Write typed values at specific addresses
process:write_float(some_overlay.health:address(), 100.0)
process:write_uint32(some_overlay.flags:address(), 0xFF)

Module enumeration

local process = regenny:process()
for _, mod in ipairs(process:modules()) do
    print(string.format("%s: 0x%X - 0x%X (size: 0x%X)",
        mod.name, mod.start, mod["end"], mod.size))
end