-
Notifications
You must be signed in to change notification settings - Fork 0
Memory Layout
This is a reference of the memory layouts ZeusMod relies on. Anything here that isn't resolved through UE reflection is a bake-in — it would need re-validation if Icarus moved to UE5 or changed a specific Blueprint structure.
Everything is x64, little-endian.
| Offset | Field | Notes |
|---|---|---|
| +0x00 | vtable |
Standard C++ vtable pointer |
| +0x08 |
ObjectFlags (EObjectFlags) |
bitset |
| +0x0C | InternalIndex |
Index into GObjects
|
| +0x10 |
ClassPrivate (UClass*) |
Points at the class reflection object |
| +0x18 |
NamePrivate (FName) |
ComparisonIndex + Number |
| +0x20 |
OuterPrivate (UObject*) |
Outer object chain |
| Offset | Field |
|---|---|
| +0x00 |
Object (UObject*) |
| +0x08 |
Flags (int32) |
| +0x0C |
ClusterRootIndex (int32) |
| +0x14 |
SerialNumber (int32) |
SerialNumber at +0x14 is a ZeusMod-baked assumption. It is used
when we synthesise a FWeakObjectPtr {ObjectIndex, SerialNumber} to
inject into the DeployableTickSubsystem active-processor TArray.
| Offset | Field | Notes |
|---|---|---|
| +0x30 | SuperStruct |
Parent class/struct |
| +0x40 |
Children (UField*) |
Linked list head of properties/functions |
| Offset | Field |
|---|---|
| +0x00 | vtable |
| +0x08 |
Class (FFieldClass*) |
| +0x18 |
Name (FName) |
| +0x20 |
Next (FField*) |
| Offset | Field |
|---|---|
| +0x44 | Offset_Internal |
This is the field FindPropertyOffset reads after matching the name.
The field we care about for hooking:
| Offset | Field | Notes |
|---|---|---|
| +0xB0 |
Func (thunk ptr) |
Points at the Kismet-generated thunk. We walk it to the C++ exec body via WalkThunkToImpl. |
(Exact offsets here are UE 4.27-specific and are read through UE's
own reflection headers at build time via UE4.h.)
struct FName {
int32 ComparisonIndex; // +0x00 — key into GNames
int32 Number; // +0x04 — de-dup suffix (unused for most names)
};Number is ignored by ResolveFNameByIndex — collisions are
vanishingly rare in Icarus-relevant FNames.
Used as an argument to IcarusFunctionLibrary::AddModifierState.
Our No Weight detour reads the middle 8 bytes to check the row name.
| Offset | Field | Notes |
|---|---|---|
| +0x00 |
DataTable (UDataTable*) |
D_ModifierStates |
| +0x08 |
RowName (FName) |
The row the modifier refers to |
| +0x10 |
DataTableName (FName) |
Debug name of the data table |
The detour reads +0x08's ComparisonIndex and resolves it to a
string; the No Weight kill-switch compares that string to
"Overburdened".
| Offset | UPROPERTY | Type | Notes |
|---|---|---|---|
| +0x00C8 | OnInventoryItemChanged |
delegate | UE multicast delegate header |
| +0x00E8 | CurrentWeight |
float32 | Cached display weight |
| +0x00F0 |
Slots (TArray<FItemData>) |
{ptr, num, max} |
The item grid |
| +0x0250 | OverflowSpawnTransform |
FTransform |
Drop spot for overflow |
| +0x0338 | SpoilTickRate |
float32 | Spoil cadence |
These offsets are resolved at runtime via reflection, but we list
them here because they come up often in inspect.py sessions.
| Offset | UPROPERTY | Type | Notes |
|---|---|---|---|
| +0x1010 | CurrentWeight |
float32 | Character's encumbrance reading |
| Offset | UPROPERTY | Type | Notes |
|---|---|---|---|
| +0x0304 | PlayerWeight |
float32 | Widget-cached encumbrance |
| +0x0868 | CurrentEncumbrance |
float32 | Widget-cached value for the bar fill |
| +0x086D | NeedsUpdate |
bool (1 byte) | Widget redraw gate |
These resolve correctly only because FindClassByName was extended
in 1.x to accept UWidgetBlueprintGeneratedClass in addition to
UBlueprintGeneratedClass.
| Offset | Field |
|---|---|
| +0x0758 |
InventoryComponent* on the player pawn |
Off::Player_InventoryComp in the DLL. Reported by character in
inspect.py:
OK character=0x28F2A3AC010 Off::Player_InventoryComp=0x758
The active-processor TArray<FWeakObjectPtr> that Free Craft injects
into. It is unreflected — not exposed as a UPROPERTY. This
offset is a hard bake-in; a schema change on the subsystem would
require a re-audit.
Free Craft's pre-populator writes the queued slot here so the
internal Process tick advances progress from frame 1.
If you discover a new offset while working in inspect.py:
- Use
propoff <class> <prop>if it's a UPROPERTY — that's reflected, you don't need to write it down. - Use
label+bookmarkto annotate the address locally. - If it ends up in a hook, add it to this page and to the matching
Off::namespace insideTrainerResolve.cpp.
Every reflected offset lives inside UObjectLookup. Every
non-reflected offset lives here. There is no third pool.
- Reflection Internals — how the reflected offsets get resolved.
- Hook Catalog — who reads which offset.
- Debug Client — tools to inspect these layouts live.