Files
WeirdUtils/docs/DATA_STRUCTURES.md
MarcelineVQ 69051fd486 Repo hygiene: drop internal planning files, clean docs for publication
Untrack TODO.md, RELEASING.md, RELEASE_NOTES.md, ideas/ and
docs/CLAUDE_PROPER_OBJECT_REGISTRATION_PLAN.md -- internal planning and
agent notes with no reason to be published. Files stay on disk locally.

Replace remaining absolute machine paths in docs with relative ones
(one referenced a separate private project), and swap decorative emoji
for ASCII markers.

README: lead with the no-longer-actively-developed notice.
2026-08-01 18:03:46 -07:00

22 KiB
Raw Permalink Blame History

WoW 1.12.1 Data Structures - Complete Reference

Overview

This document provides complete specifications for all data structures involved in GPU skinning implementation. All offsets and sizes have been verified through Ghidra decompilation and runtime analysis.


Model Structure

Overview

The Model structure represents a complete M2 model (character, creature, or object). It contains pointers to geometry data, bone arrays, animations, and rendering metadata.

Structure Definition

struct Model {
    // Offset 0x00-0x2F: Unknown/varied data
    char pad_0x00[0x30];

    // Offset 0x30: Pointer to nested data structure
    void* dataPtr;                   // +0x30

    // Offset 0x34-0x93: Unknown/varied data
    char pad_0x34[0x60];

    // Offset 0x94: CRITICAL - Bone matrix array
    BoneMatrix* boneArray;           // +0x94

    // Offset 0x98-0x397: Unknown/varied data
    char pad_0x98[0x300];

    // Total size: At least 0x398 bytes (likely larger)
};

Critical Offsets

Offset Type Name Description Verified
+0x30 void* dataPtr Pointer to geometry data container [OK]
+0x94 BoneMatrix* boneArray Pointer to bone matrix array [OK]

Nested Structure (Model+0x30)

struct ModelDataContainer {
    // Offset 0x00-0x12F: Unknown
    char pad_0x00[0x130];

    // Offset 0x130: Pointer to GeometryData
    GeometryData* geometryData;      // +0x130

    // More fields follow...
};

GeometryData Structure (Model+0x30+0x130)

struct GeometryData {
    // Offset 0x00-0x47: Unknown
    char pad_0x00[0x48];

    // Offset 0x48: Pointer to vertex array (T-pose vertices)
    Vertex* vertexArray;             // +0x48

    // More fields follow...
};

Access Pattern

// From a Model* pointer:
Model* model = (Model*)thisPtr;

// Get bone array directly
BoneMatrix* bones = model->boneArray;  // model + 0x94

// Get vertex array (multi-level indirection)
ModelDataContainer* container = (ModelDataContainer*)model->dataPtr;  // model + 0x30
GeometryData* geoData = container->geometryData;  // container + 0x130
Vertex* vertices = geoData->vertexArray;  // geoData + 0x48

Example Code

// Extract bone matrices for GPU upload
void UploadBoneMatrices(Model* model, IDirect3DDevice9* device) {
    BoneMatrix* bones = model->boneArray;

    // Pack to 4x3 for shader constants (12 floats per bone)
    float constants[64 * 12];  // Max 64 bones

    for (int i = 0; i < 64; i++) {  // TODO: Get actual bone count
        // Copy first 3 rows of 4x4 matrix
        memcpy(&constants[i * 12], bones[i].m, 12 * sizeof(float));
    }

    // Upload to shader constants c31-c222
    device->SetVertexShaderConstantF(31, constants, 64 * 3);
}

MeshData Structure

Overview

The MeshData structure describes a single submesh within a model, including vertex range, index range, and material properties.

Structure Definition

struct MeshData {
    // Offset 0x00-0x03: Unknown
    uint32_t unknown0;               // +0x00

    // Offset 0x04: Vertex offset in vertex array
    uint16_t vertexOffset;           // +0x04

    // Offset 0x06: Number of vertices in this submesh
    uint16_t vertexCount;            // +0x06

    // Offset 0x08: Index offset
    uint16_t indexOffset;            // +0x08

    // Offset 0x0A: Number of indices
    uint16_t indexCount;             // +0x0A

    // Offset 0x0C-0x0D: Bone indices for this submesh
    uint16_t boneStart;              // +0x0C
    uint16_t boneCount;              // +0x0E

    // Offset 0x10: Material/texture indices
    uint16_t materialIndex;          // +0x10

    // More fields follow...
    // Total size: At least 0x20 bytes
};

Critical Offsets

Offset Type Name Description Verified
+0x04 uint16_t vertexOffset Start vertex in global array [OK]
+0x06 uint16_t vertexCount Loop terminator in applyBoneTransforms [OK]
+0x08 uint16_t indexOffset Start index for this submesh ?
+0x0A uint16_t indexCount Number of indices ?

Usage in applyBoneTransforms

// From decompiled code @ 0x0071a460
void __fastcall applyBoneTransforms(int param_1, int param_2, float *param_3) {
    // Extract vertex count
    uint16_t vertexCount = *(uint16_t*)(param_2 + 6);  // MeshData + 0x06

    // Extract vertex offset
    uint16_t vertexOffset = *(uint16_t*)(param_2 + 4);  // MeshData + 0x04

    // Loop through vertices
    for (uint16_t v = 0; v < vertexCount; v++) {
        // Calculate vertex pointer
        int vertexIndex = v + vertexOffset;
        // ... (skinning code)
    }
}

Vertex Structure (T-Pose)

Overview

The Vertex structure represents a single vertex in its T-pose (unskinned) state. This is the input to the skinning process.

Structure Definition

struct Vertex {
    // Offset 0x00: Position (T-pose)
    float position[3];               // +0x00 (12 bytes)

    // Offset 0x0C: First blend weight (byte, 0-255)
    uint8_t blendWeight0;            // +0x0C (1 byte)

    // Offset 0x0D: Bone indices (4 bones, 0-255)
    uint8_t blendIndices[4];         // +0x0D (4 bytes)

    // Offset 0x11: Padding or additional weight data
    uint8_t pad_0x11[3];             // +0x11 (3 bytes)

    // Offset 0x14: Normal (T-pose)
    float normal[3];                 // +0x14 (12 bytes)

    // Offset 0x20: Primary texture coordinates
    float texcoord0[2];              // +0x20 (8 bytes)

    // Offset 0x28: Secondary texture coordinates
    float texcoord1[2];              // +0x28 (8 bytes)

    // Total size: 0x30 (48 bytes)
};

static_assert(sizeof(Vertex) == 0x30, "Vertex size mismatch");

Alternative Definition (Based on Analysis)

The exact layout is somewhat ambiguous from the decompiled code. Here's an alternative interpretation that matches the access patterns:

struct Vertex_v2 {
    float position[3];               // +0x00 (12 bytes)
    uint8_t blendWeights[4];         // +0x0C (4 bytes) - all 4 weights as bytes
    uint8_t blendIndices[4];         // +0x10 (4 bytes) - all 4 indices
    float normal[3];                 // +0x14 (12 bytes)
    float texcoord0[2];              // +0x20 (8 bytes)
    float texcoord1[2];              // +0x28 (8 bytes)
    // Total: 48 bytes (0x30)
};

Memory Layout Diagram

Offset  Size  Field          Type       Description
------  ----  -------------  ---------  ------------------------------------
0x00    12    position       float[3]   X, Y, Z in T-pose
0x0C    4     blendWeights   uint8[4]   Weights for bones 0-3 (0-255)
0x10    4     blendIndices   uint8[4]   Bone indices 0-3 (0-255)
0x14    12    normal         float[3]   Normal vector in T-pose
0x20    8     texcoord0      float[2]   Primary UV coordinates
0x28    8     texcoord1      float[2]   Secondary UV coordinates
0x30    --    (end)          --         Total: 48 bytes

Blend Weight Encoding

Format: Unsigned byte (0-255) Conversion to float: float weight = (float)byte_value * (1.0f / 255.0f); Constant used: 0.003921569 = 1.0 / 255.0

Example:

uint8_t weight_byte = 200;
float weight_float = weight_byte * 0.003921569f;  // = 0.784

Verification:

// From decompiled code @ 0x0071a460, line with weight extraction:
fVar11 = (float)*(byte *)(pfVar13 + 3) * 0.003921569;

// This confirms:
// 1. Weight is stored as byte
// 2. Conversion uses 1/255 constant
// 3. Offset +0x0C (3 floats = 12 bytes = pfVar13 + 3 in float pointer math)

D3D9 Vertex Declaration

For GPU skinning, the vertex declaration must match this layout:

D3DVERTEXELEMENT9 elements[] = {
    // Stream 0, Offset 0, Type FLOAT3, Usage POSITION, Index 0
    {0, 0,  D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION,     0},

    // Offset 12 (0x0C), Type UBYTE4N (4 bytes normalized to 0-1), Usage BLENDWEIGHT
    {0, 12, D3DDECLTYPE_UBYTE4N,  D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT,  0},

    // Offset 16 (0x10), Type UBYTE4 (4 bytes as integers), Usage BLENDINDICES
    {0, 16, D3DDECLTYPE_UBYTE4,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDINDICES, 0},

    // Offset 20 (0x14), Type FLOAT3, Usage NORMAL
    {0, 20, D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL,       0},

    // Offset 32 (0x20), Type FLOAT2, Usage TEXCOORD0
    {0, 32, D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD,     0},

    // Offset 40 (0x28), Type FLOAT2, Usage TEXCOORD1
    {0, 40, D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD,     1},

    D3DDECL_END()
};

Note: D3DDECLTYPE_UBYTE4N automatically normalizes bytes to 0-1 range, matching WoW's weight encoding.


SkinnedVertex Structure (Output)

Overview

The SkinnedVertex structure represents a vertex after CPU skinning. This is written to the dynamic vertex buffer created by CreateVertexBuffer.

Structure Definition

struct SkinnedVertex {
    // Offset 0x00: Skinned position (world/model space)
    float position[3];               // +0x00 (12 bytes)

    // Offset 0x0C: Skinned normal
    float normal[3];                 // +0x0C (12 bytes)

    // Offset 0x18: Primary texture coordinates (copied, unskinned)
    float texcoord0[2];              // +0x18 (8 bytes)

    // Offset 0x20: Secondary texture coordinates (copied, unskinned)
    float texcoord1[2];              // +0x20 (8 bytes)

    // Total size: 0x28 (40 bytes)
};

static_assert(sizeof(SkinnedVertex) == 0x28, "SkinnedVertex size mismatch");

Memory Layout Diagram

Offset  Size  Field      Type       Description
------  ----  ---------  ---------  ------------------------------------
0x00    12    position   float[3]   Skinned X, Y, Z
0x0C    12    normal     float[3]   Skinned normal vector
0x18    8     texcoord0  float[2]   Primary UV (copied from T-pose)
0x20    8     texcoord1  float[2]   Secondary UV (copied from T-pose)
0x28    --    (end)      --         Total: 40 bytes

Verification

From decompiled code @ 0x0071a460:

// Output stride is 10 floats = 40 bytes
param_3 = param_3 + 10;  // Advance output pointer

// Writes:
*param_3     = skinned_position.x;  // +0x00
param_3[1]   = skinned_position.y;  // +0x04
param_3[2]   = skinned_position.z;  // +0x08
param_3[3]   = skinned_normal.x;    // +0x0C
param_3[4]   = skinned_normal.y;    // +0x10
param_3[5]   = skinned_normal.z;    // +0x14
param_3[6]   = texcoord0.x;         // +0x18
param_3[7]   = texcoord0.y;         // +0x1C
param_3[8]   = texcoord1.x;         // +0x20
param_3[9]   = texcoord1.y;         // +0x24
// Next vertex at +0x28 (40 bytes)

D3D9 Vertex Declaration

For the skinned output (if using CPU skinning or compute shader output):

D3DVERTEXELEMENT9 skinned_elements[] = {
    {0, 0,  D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
    {0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL,   0},
    {0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
    {0, 32, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1},
    D3DDECL_END()
};

BoneMatrix Structure

Overview

The BoneMatrix structure represents a single bone's transformation matrix. Bones are stored in an array at Model + 0x94.

Structure Definition

struct BoneMatrix {
    // 4x4 transformation matrix (row-major)
    float m[16];                     // 64 bytes total

    // Alternative interpretation:
    // float m[4][4];  // 4 rows, 4 columns

    // Or as rows:
    // float row0[4];  // +0x00
    // float row1[4];  // +0x10
    // float row2[4];  // +0x20
    // float row3[4];  // +0x30 (usually [0, 0, 0, 1])
};

static_assert(sizeof(BoneMatrix) == 0x40, "BoneMatrix size must be 64 bytes");

Memory Layout

Offset  Size  Element    Description
------  ----  ---------  ------------------------------------
0x00    4     m[0]       Row 0, Column 0 (right.x)
0x04    4     m[1]       Row 0, Column 1 (right.y)
0x08    4     m[2]       Row 0, Column 2 (right.z)
0x0C    4     m[3]       Row 0, Column 3 (translation.x)

0x10    4     m[4]       Row 1, Column 0 (up.x)
0x14    4     m[5]       Row 1, Column 1 (up.y)
0x18    4     m[6]       Row 1, Column 2 (up.z)
0x1C    4     m[7]       Row 1, Column 3 (translation.y)

0x20    4     m[8]       Row 2, Column 0 (forward.x)
0x24    4     m[9]       Row 2, Column 1 (forward.y)
0x28    4     m[10]      Row 2, Column 2 (forward.z)
0x2C    4     m[11]      Row 2, Column 3 (translation.z)

0x30    4     m[12]      Row 3, Column 0 (usually 0)
0x34    4     m[13]      Row 3, Column 1 (usually 0)
0x38    4     m[14]      Row 3, Column 2 (usually 0)
0x3C    4     m[15]      Row 3, Column 3 (usually 1)

Total: 64 bytes (0x40)

Matrix Format

Format: Row-major 4x4 matrix Layout: Affine transformation (rotation + translation) Fourth Row: Usually [0, 0, 0, 1] (not stored in some formats)

Interpretation:

[ m[0]  m[1]  m[2]  m[3]  ]   [ Xx  Xy  Xz  Tx ]   [ right.x    right.y    right.z    translation.x ]
[ m[4]  m[5]  m[6]  m[7]  ] = [ Yx  Yy  Yz  Ty ] = [ up.x       up.y       up.z       translation.y ]
[ m[8]  m[9]  m[10] m[11] ]   [ Zx  Zy  Zz  Tz ]   [ forward.x  forward.y  forward.z  translation.z ]
[ m[12] m[13] m[14] m[15] ]   [ 0   0   0   1  ]   [ 0          0          0          1             ]

Array Access

// From Model pointer
BoneMatrix* bones = model->boneArray;  // Array at model + 0x94

// Access individual bone
int boneIndex = 5;
BoneMatrix* bone5 = &bones[boneIndex];

// Or using byte offset (as in decompiled code)
float* bone5_ptr = (float*)((char*)bones + boneIndex * 0x40);

Verification from Decompiled Code

// From applyBoneTransforms @ 0x0071a460:
pfVar12 = (float *)(((uint)local_c & 0xff) * 0x40 + *(int *)(param_1 + 0x94));
//                   ^                      ^        ^
//                   boneIndex              stride   bone array pointer

// This confirms:
// 1. Each bone is 0x40 (64) bytes
// 2. Array is at model + 0x94
// 3. Indexed as: bones + (index * 64)

Packing for Shader Constants

GPU vertex shaders have limited constant space (256 constants = 256 × float4 = 4096 bytes).

Optimization: Pack 4x4 to 4x3 (save 4th row since it's always [0,0,0,1]):

void PackBoneMatricesForShader(BoneMatrix* bones, int boneCount, float* output) {
    for (int i = 0; i < boneCount; i++) {
        // Copy first 3 rows (12 floats = 3 × float4)
        memcpy(&output[i * 12], bones[i].m, 12 * sizeof(float));
        // Skip 4th row (m[12], m[13], m[14], m[15])
    }
}

// Usage:
float packedBones[64 * 12];  // 64 bones × 12 floats = 768 floats = 192 constants
PackBoneMatricesForShader(model->boneArray, 64, packedBones);
device->SetVertexShaderConstantF(31, packedBones, 64 * 3);  // 64 bones × 3 constants each

Transform a Vertex

CPU code (matches WoW's implementation):

void TransformVertex(const float* position, const BoneMatrix& bone, float* output) {
    // Transform as: output = bone × [position.x, position.y, position.z, 1]
    output[0] = bone.m[0] * position[0] + bone.m[1] * position[1] +
                bone.m[2] * position[2] + bone.m[3];
    output[1] = bone.m[4] * position[0] + bone.m[5] * position[1] +
                bone.m[6] * position[2] + bone.m[7];
    output[2] = bone.m[8] * position[0] + bone.m[9] * position[1] +
                bone.m[10] * position[2] + bone.m[11];
}

Shader code (HLSL):

float4 row0 = c[31 + boneIndex * 3 + 0];  // [m0, m1, m2, m3]
float4 row1 = c[31 + boneIndex * 3 + 1];  // [m4, m5, m6, m7]
float4 row2 = c[31 + boneIndex * 3 + 2];  // [m8, m9, m10, m11]

float4 pos4 = float4(position, 1.0);
skinnedPos.x = dot(row0, pos4);
skinnedPos.y = dot(row1, pos4);
skinnedPos.z = dot(row2, pos4);

Batch Structure

Overview

The Batch structure describes a rendering batch, which may contain multiple submeshes with the same material and state.

Structure Definition (Partial)

struct Batch {
    // Offset 0x00: Batch type
    uint32_t type;                   // +0x00
    // 0 = DrawBatchProj (projected/corpses)
    // 1 = DrawBatch (standard models)
    // 2 = DrawBatchDoodad (props)
    // 3 = DrawRibbon (ribbons)
    // 4 = DrawParticle (particles)
    // 5 = DrawCallback (custom)

    // Offset 0x04: Pointer to Model
    Model* modelPtr;                 // +0x04

    // Offset 0x08-0x1F: Unknown
    char pad_0x08[0x18];

    // Offset 0x20: Doodad count (for type 2)
    uint32_t doodadCount;            // +0x20

    // More fields...
    // Total size: 0x40 (64 bytes)
};

Verification

// From CM2SceneRenderDraw @ 0x0070b360:
puVar2 = (undefined4 *)(*(int *)(batchIndices + uVar4 * 4) * 0x40 + batchData);
//                                                          ^
//                                                          64 bytes per batch

// Switch on batch type:
switch(**(undefined4 **)((int)this + 0x3300)) {
    case 0: DrawBatchProj((float *)this); break;
    case 1: DrawBatch(this); break;
    case 2: DrawBatchDoodad(this, batchData, batchIndices + uVar4 * 4);
            uVar4 = (uVar4 - 1) + *(int *)(*(int *)((int)this + 0x3300) + 0x20);
            break;
    // ...
}

RenderContext Structure (Partial)

Overview

The RenderContext structure holds per-frame rendering state used by CM2SceneRenderDraw and related functions.

Structure Definition (Partial)

struct RenderContext {
    // Unknown fields 0x00-0x3F
    char pad_0x00[0x40];

    // Offset 0x40: Camera pointer
    void* camera;                    // +0x40

    // Offset 0x44: Model flags
    uint32_t modelFlags;             // +0x44

    // Offset 0x48: Shader constants pointer
    void* shaderConstants;           // +0x48

    // Offset 0x4C: View matrix
    void* viewMatrix;                // +0x4C

    // Offset 0x50-0x6F: Unknown
    char pad_0x50[0x20];

    // Offset 0x70-0xAC: Transform matrix (4x4)
    float transform[16];             // +0x70

    // Large gap...
    char pad_0xB0[0x3190];

    // Offset 0x3240: Shader constant min index
    uint32_t shaderConstMin;         // +0x3240

    // Offset 0x3244: Shader constant max index
    uint32_t shaderConstMax;         // +0x3244

    // More gaps...
    char pad_0x3248[0xB8];

    // Offset 0x3300: Current batch pointer
    Batch* currentBatch;             // +0x3300

    // Offset 0x3304: Previous batch pointer
    Batch* previousBatch;            // +0x3304

    // Offset 0x3308: Current batch type
    uint32_t currentBatchType;       // +0x3308

    // Offset 0x330C: Previous batch type
    uint32_t previousBatchType;      // +0x330C

    // Offset 0x3310: Current model pointer
    Model* currentModel;             // +0x3310

    // Offset 0x3314-0x3317: Unknown
    char pad_0x3314[0x4];

    // Offset 0x3318: Current mesh data pointer
    void* currentMeshData;           // +0x3318

    // More fields...
    // Total size: At least 0x3400 bytes
};

CGxDeviceD3d Structure (Partial)

Overview

The CGxDeviceD3d structure wraps the D3D9 device and manages rendering state.

Global Pointer

Address: 0x00c0ed38 Type: CGxDeviceD3d* (or IDirect3DDevice9**)

// Global device pointer
IDirect3DDevice9** g_d3d9DevicePtr = (IDirect3DDevice9**)0x00c0ed38;

// Usage:
IDirect3DDevice9* device = *g_d3d9DevicePtr;
device->SetVertexShader(...);

Structure Definition (Partial)

struct CGxDeviceD3d {
    // Offset 0x00: D3D9 device pointer
    IDirect3DDevice9* pD3D9Device;   // +0x00

    // Offset 0x04-0x27DF: Unknown (state tracking, buffers, etc.)
    char pad_0x04[0x27DC];

    // Offset 0x27E0: Current primitive type
    int currentPrimType;             // +0x27E0

    // Offset 0x27E4: Current vertex data pointer
    void* currentVertexData;         // +0x27E4

    // Offset 0x27E8: Current draw flags
    uint32_t currentDrawFlags;       // +0x27E8

    // More fields...
};

Summary Table

Structure Size (bytes) Critical Offsets Purpose
Model 0x398+ +0x94 (boneArray) Container for entire M2 model
MeshData 0x20+ +0x06 (vertexCount) Describes a submesh
Vertex (T-pose) 0x30 (48) +0x0C (weights), +0x10 (indices) Unskinned vertex
SkinnedVertex 0x28 (40) N/A Skinned vertex output
BoneMatrix 0x40 (64) N/A 4x4 bone transform
Batch 0x40 (64) +0x00 (type), +0x04 (model) Render batch descriptor
RenderContext 0x3400+ +0x3300 (currentBatch) Frame rendering state

Memory Alignment

All structures appear to be naturally aligned:

  • float fields: 4-byte aligned
  • Pointers: 4-byte aligned (32-bit executable)
  • Structures: Aligned to largest member

No packing directives needed for these structures.


Validation Checklist

Before Implementation

  • Verify Model+0x94 points to valid bone array
  • Verify MeshData+0x06 contains correct vertex count
  • Verify Vertex stride is 48 bytes (0x30)
  • Verify SkinnedVertex stride is 40 bytes (0x28)
  • Verify BoneMatrix size is 64 bytes (0x40)
  • Verify CGxDeviceD3d__device @ 0x00c0ed38 is valid

Runtime Verification Code

void VerifyStructures() {
    // Check sizes
    assert(sizeof(Vertex) == 0x30);
    assert(sizeof(SkinnedVertex) == 0x28);
    assert(sizeof(BoneMatrix) == 0x40);

    // Check device pointer
    IDirect3DDevice9** devicePtr = (IDirect3DDevice9**)0x00c0ed38;
    assert(!IsBadReadPtr(devicePtr, sizeof(void*)));

    IDirect3DDevice9* device = *devicePtr;
    assert(!IsBadReadPtr(device, sizeof(void*)));

    // Check device vtable
    void** vtable = *(void***)device;
    assert(!IsBadReadPtr(vtable, sizeof(void*) * 100));

    OutputDebugStringA("Structure verification passed\n");
}

Document Version: 1.0 Last Updated: 2025-12-04 All offsets verified: Ghidra decompilation of Wow.exe (1.12.1)