Architecture of the Pointers System for Game Memory Offsets in YimMenuV2

The Pointers subsystem in YimMenuV2 employs a three-layer architecture—combining compile-time pattern scanning, strongly-typed pointer storage, and a global singleton interface—to dynamically resolve and cache critical GTA V memory offsets at runtime.

The Pointers system serves as the central nervous system of YimMenuV2, eliminating the fragility of static offset tables by scanning the running process on every launch. According to the YimMenuV2 source code, this architecture enables the menu to survive game updates that shift memory layouts while providing type-safe access to functions, data structures, and hook points across the codebase.

Overview of the Three-Layer Architecture

The implementation organizes memory resolution into distinct responsibilities, from raw byte matching to high-level typed access.

Layer 1: Pattern Scanning and Resolution

The foundation relies on the PatternScanner class defined in src/core/memory/PatternScanner.hpp. This engine performs runtime signature matching against loaded modules such as GTA5_Enhanced.exe and socialclub.dll.

Patterns are declared as compile-time literals using the Pattern<> template. For example:

constexpr auto swapChainPtrn = Pattern<"72 C7 EB 02 31 C0 8B 0D">("IDXGISwapChain");

The scanner walks the target module's memory range, identifies these byte sequences, and hands the match to a PointerCalculator (from src/core/memory/PointerCalculator.hpp). This helper performs RIP-relative arithmetic via methods like Add(), Sub(), and Rip() to convert raw matches into absolute addresses.

Layer 2: Typed Pointer Repository

Once resolved, addresses populate the PointerData struct declared in src/game/pointers/Pointers.hpp. This struct declares strongly-typed members such as:

  • ID3D12CommandQueue** CommandQueue
  • rage::atArray<rage::scrThread*>* ScriptThreads
  • Functions::GetNetObjectById GetNetObjectById

The Pointers class inherits from PointerData and implements Init() for the main executable and LateInit() for auxiliary modules. This two-phase approach ensures dependencies like socialclub.dll are fully loaded before their offsets are resolved.

Layer 3: Public Singleton Interface

The system exposes a global instance via inline YimMenu::Pointers Pointers; defined in the header. After initialization returns true, code throughout the menu accesses resolved offsets directly:

if (YimMenu::Pointers.QueuePacket)
{
    YimMenu::Pointers.QueuePacket(
        connectionMgr,
        0x2A,
        data, sizeof(data),
        0, nullptr);
}

Initialization Flow: From Module to Memory Address

Understanding the exact sequence of resolution clarifies how the system maintains reliability across game versions.

Module Discovery and Scanner Setup

Initialization begins in Pointers::Init() within src/game/pointers/Pointers.cpp. The method first obtains a module handle through ModuleMgr.Get("GTA5_Enhanced.exe"_J), then constructs a PatternScanner instance wrapping the module's memory range.

Pattern Registration and Callbacks

For each required offset, the code registers a pattern-callback pair:

scanner.Add(swapChainPtrn, [](PointerCalculator ptr) {
    Pointers.SwapChain = ptr.Add(3).Rip().As<IDXGISwapChain**>();
});

The lambda receives a PointerCalculator positioned at the pattern match, performs offset arithmetic, and stores the result in the appropriate PointerData member using .As<T>() for type safety.

RIP-Relative Address Calculation

Modern compilers heavily utilize RIP-relative addressing for position-independent code. The PointerCalculator abstracts this complexity:

  • Add(n) advances the pointer by n bytes
  • Sub(n) retreats the pointer by n bytes
  • Rip() reads the 32-bit displacement at the current position and calculates the absolute target address

This arithmetic is essential for resolving pointers in .text sections where absolute addresses are encoded as relative offsets.

Late Initialization for Auxiliary Modules

Certain features depend on socialclub.dll, which loads asynchronously. The LateInit() method handles this through:

bool Pointers::LateInit()
{
    if (IsSocialClubNeverGoingToLoad())
        return false;
    
    auto socialClub = ModuleMgr.Get("socialclub.dll"_J);
    PatternScanner scanner(socialClub);
    // ... pattern registration for Social Club offsets
    return scanner.Scan();
}

This separation prevents race conditions while ensuring network-related pointers are available when needed.

Byte-Patch Integration and Runtime Modifications

The architecture extends beyond read-only pointers to support runtime code modification. Within the same registration lambdas, the system creates BytePatch objects (from src/core/memory/BytePatches.hpp) when a pattern identifies a region requiring modification.

Examples include SpectatePatch and ModelSpawnBypass. These patches are stored as members in PointerData, allowing other modules to enable or disable them via methods like Apply() and Restore() without re-scanning memory.

Practical Usage Examples

The repository demonstrates several access patterns across subsystems:

Reading game state:

auto width  = *YimMenu::Pointers.ScreenResX;
auto height = *YimMenu::Pointers.ScreenResY;

Accessing entity pools:

auto pedPool = YimMenu::Pointers.PedPool;
for (int i = 0; i < pedPool->m_Size; i++)
{
    auto ped = pedPool->GetAt(i);
    // process ped
}

Network function invocation:

YimMenu::Pointers.JoinSessionByInfo(
    sessionInfo,
    1, 0, 0, 0, 0, 0, 0);

Summary

  • Dynamic Resolution: The PatternScanner eliminates hardcoded offsets by matching byte signatures at runtime against GTA5_Enhanced.exe and socialclub.dll.
  • Type Safety: The PointerData struct stores resolved addresses as strongly-typed pointers and function pointers, eliminating dangerous casts throughout the codebase.
  • Two-Phase Init: Init() handles the main executable immediately, while LateInit() waits for Social Club dependencies, preventing initialization races.
  • Global Access: The YimMenu::Pointers singleton provides zero-overhead access to game memory after successful initialization.
  • Integrated Patching: BytePatch objects stored alongside pointers enable runtime code modification without separate memory management.

Frequently Asked Questions

How does YimMenuV2 handle game updates that change memory offsets?

The menu uses signature scanning rather than static offset tables. When Rockstar updates GTA V, the byte patterns around critical functions typically remain stable even if absolute addresses shift. The PatternScanner locates these signatures dynamically on every launch, allowing the menu to function without manual offset updates for minor patches.

What is the difference between Init() and LateInit() in the Pointers system?

Init() resolves pointers within the main game executable immediately upon injection, providing access to core engine functions and rendering interfaces. LateInit() executes subsequently to scan socialclub.dll, which loads asynchronously during the initial connection phase. This separation ensures network-related pointers like NetworkSession are valid when the player actually connects to Rockstar's servers.

Why does the Pointers system use RIP-relative addressing?

RIP-relative addressing is the standard for x64 position-independent code. Game executables encode jump targets and data references as 32-bit offsets from the instruction pointer rather than 64-bit absolute addresses. The PointerCalculator's Rip() method decodes these displacements to calculate the true virtual address, which is necessary for resolving pointers in the .text section where the game's logic resides.

How are function pointers type-safely stored and called?

The PointerData struct declares function pointer members using typed aliases from the Functions namespace (e.g., Functions::QueuePacket). During scanning, the .As<T>() template method casts the resolved void* to the specific function pointer type. This allows direct invocation with compiler-enforced parameter checking, preventing calling convention mismatches that would occur with raw address casts.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →