Scope: UE5 patterns (research base: Gothic 1 Remake).
Not applicable as a default map for Dying Light 2 (Techlandgamedll/engine). DL2: game/DyingLight2/INDEX.md.
A distilled reference of how UE games typically organize character, stats, and inventory data in memory, based on Gothic 1 Remake (UE5) research.
Every UE game has an object hierarchy rooted at GEngine:
GEngine (singleton, registered as CE symbol)
→ GameInstance (persistent across levels)
→ LocalPlayers (TArray)
[0] → LocalPlayer
→ PlayerController (owns input, camera, HUD)
→ Character / Pawn (the player-controlled actor)
How to find Character:
- With CE75 loaded:
UEngine_findCharacter() - Manually:
readQword(GEngine + GameInstanceOffset + LocalPlayersOffset + 0 + PlayerControllerOffset + CharacterOffset) - Offsets vary per UE version. UE5 G1R: GI +0x30, LP TArray +0x38, PC +0x10, Character +0x2F0
The Character object has dozens of sub-components at offsets +0x700 through +0xA00. Each is a UObject* (TObjectPtr) pointing to a component. The typical layout:
Character
+0x7B0: CarryComponent / InventoryComponent (inventory holder)
+0x798: HealthBarComponent / StatusComponent
+0x808: BloodComponent / DamageComponent
+0x840: DataModule_BaseStats / AttributeComponent
+0x938: CharState / CharacterState (often has AbilitySystemComponent)
Discovery command:
local ch = UEngine_findCharacter()
for off = 0x780, 0x960, 0x8 do
local obj = readQword(ch + off)
if obj and obj > 0x10000 then
local cls = readQword(obj + 0x10)
local ok, n = pcall(UObject_getName, cls)
if ok then print(string.format("+0x%03X → %s", off, n)) end
end
endGAS is UE's primary stats system. The layout:
CharacterState (e.g., CharState_C)
+0x378: AbilitySystemComponent (ASC)
+0x1090: SpawnedAttributes (TArray<UAttributeSet*>)
Each AttributeSet is a UObject subclass containing FGameplayAttributeData struct properties:
AttributeSet_Health:
+0x40: Health (FGameplayAttributeData)
+0x50: MaxHealth (FGameplayAttributeData)
+0x60: DamageMultiplier
+0x70: RecoveryRate
FGameplayAttributeData:
+0x00: int64 Descriptor (static module address)
+0x08: float CurrentValue ← FIRST (NOT BaseValue!)
+0x0C: float BaseValue
G1R attribute sets discovered: [0] Health, [1] Armor, [2] LevelProgression, [3] Mana, [4] Movement, [5] Dexterity, [6] Strength, [7] Oxygen, [8] Sleep, [9] Fatigue, [10] Lockpicking, [11] Pickpocketing, [12] Alcohol, [13] Swampweed
Character → CarryComponent
→ DataModule_Container (or InventoryManager)
→ InventoryManager / Item Array
The inventory is stored as a dense array of 0xB8-byte entries:
ArrayBase (+0x378):
+0x378: ptr to entries
+0x37C: count of entries
Each entry (0xB8 bytes):
+0x00: metadata qword (sequential index)
+0x08: item UObject pointer (name via UObject_getName)
+0x10: item count (int32, lower 32 bits of qword)
+0x14: item type hash (upper 32 bits)
+0x18+: zeros/unused
Items are identified by their UObject Name. Common prefixes in G1R:
ItMi_— Misc items (Orenugget, Oldcoin, Smith_Iron...)ItAm_— Ammo (Arrow, Bolt)ItFo_— Food/plantsItMw_— Weapons (melee)ItRw_— Ranged weapons (bow)ItAr_— Armor/scrolls/runesItAt_— Accessories/trophiesItKe_— Keys/quest items
Every UE object (4-byte aligned, typically 64-bit pointers):
+0x00: vtable pointer (in module range, e.g., 0x7FF6XXXXXXXX)
+0x08: ObjectFlags (bitmask: RF_ marked)
+0x10: ClassPrivate → UClass pointer
+0x18: NameIndex (int32, index into FName pool)
+0x1C: NameNumber (int32)
+0x20: Outer → Outer UObject
+0x28+: Class-specific data
Verification: An address is a UObject if readQword(addr) is in the module range AND readQword(addr + 0x10) is non-null.
Getting the name:
- With CE75:
UObject_getName(ptr)→ string like"ItMi_Orenugget" - Manual:
UEngine_resolveFName(readInteger(ptr + 0x18))→ full name
+0x00: ptr to elements (allocated on heap)
+0x08: count (int32)
+0x0C: max (int32)
If at the end of an object (inline storage), allocator may skip the pointer and store inline. Always check: if ptr is in the game's object range (0x20XXXXXXXXXX) and count is sensible, it's heap-allocated.
- Find GEngine — symbol or scan for pointer-to-self pattern
- Walk to Character — via GI → LP → PC → Character
- Dump Character's sub-components — look for
CarryComponent,AttributeSet,Inventory,AbilitySystem - Find stats — walk CharacterState → ASC → SpawnedAttributes
- Find inventory — walk CarryComponent → DataModule_Container → InventoryManager → Array
- Verify — read known item/stat values and cross-reference with in-game display
AOBScan in CE's background thread:
- Full-process only (range params ignored in many CE versions)
- Small values (< 65536) produce thousands of false hits
- Float scanning more selective for large numbers
- int64 scanning much more selective than int32
- Pointer scanning requires a known address to scan from
- Delta scans (changed/unchanged) require game interaction
- TMap/TSet layouts vary per UE version and are hard to parse from raw memory
- Some inventory data uses serialized/packed formats not directly readable
- Some items are created lazily (nil pointers until first inventory open)
reallocinvalidates cached array pointers on level transitions- Plugin-specific functions (G1R plugin) must be loaded to access advanced CE75 helpers