Fix bone count: model container is at this+0x30 not this+0x2C
Ghidra decompiler swapped the two fields. Assembly verification shows: +0x2C = animation_context_ptr (sync check at +0x10) +0x30 = model_container_ptr (+0x130 = M2 model header) Bone count chain: *(*(*(this+0x30) + 0x130) + 0x34)
This commit is contained in:
@@ -115,23 +115,36 @@ Marks transform as up to date.
|
||||
## Key Data Structures
|
||||
|
||||
### SceneObject (this pointer)
|
||||
|
||||
**WARNING**: Ghidra decompiler swaps +0x2C and +0x30 labels. Assembly is authoritative.
|
||||
|
||||
| Offset | Field | Type | Notes |
|
||||
|--------|-------|------|-------|
|
||||
| +0x10 | model_data_ptr | void* | NULL check for early bail |
|
||||
| +0x2C | ptr_at_2c | void* | -> context struct (NOT M2 header directly!) |
|
||||
| +0x30 | animation_context_ptr | void* | +0x0C=timestamp, +0x10=sync_value |
|
||||
| +0x40 | transform_sync_value | int | Compared with anim_ctx+0x10 |
|
||||
| +0x2C | animation_context_ptr | void* | +0x0C=timestamp, +0x10=sync_value |
|
||||
| +0x30 | model_container_ptr | void* | +0x130 = M2 model header |
|
||||
| +0x40 | transform_sync_value | int | Compared with *(anim_ctx+0x10) |
|
||||
| +0x80 | unknown_0x80 | uint | Bone runtime state array base |
|
||||
| +0x1CC | field_0x1cc | int* | Emitter/particle context |
|
||||
|
||||
### Context Struct (at *(this+0x2C))
|
||||
Assembly proof (0x714277-0x714293):
|
||||
```asm
|
||||
MOV EAX, [EBX + 0x2c] ; EAX = animation_context_ptr
|
||||
MOV ECX, [EBX + 0x40] ; ECX = sync_value
|
||||
CMP ECX, [EAX + 0x10] ; sync check: this+0x40 vs *(this+0x2C)+0x10
|
||||
...
|
||||
MOV EDX, [EBX + 0x30] ; EDX = model_container_ptr
|
||||
MOV EDI, [EDX + 0x130] ; EDI = M2 model header
|
||||
```
|
||||
|
||||
### Model Container (at *(this+0x30))
|
||||
| Offset | Field | Notes |
|
||||
|--------|-------|-------|
|
||||
| +0x14 | global sequence count | Loop bound for GS processing |
|
||||
| +0x18 | global sequence durations array | |
|
||||
| +0x130 | M2 model header pointer | **This is the actual model** |
|
||||
|
||||
**Pointer chain to bone count**: `*(*(*(this+0x2C) + 0x130) + 0x34)`
|
||||
**Pointer chain to bone count**: `*(*(*(this+0x30) + 0x130) + 0x34)`
|
||||
|
||||
### Bone Definition (0x6c = 108 bytes per bone in model)
|
||||
From `model+0x38` array (where model = `*(*(this+0x2C) + 0x130)`). Contains:
|
||||
|
||||
@@ -93,22 +93,25 @@ fn transformDetour(this: u32, edx: u32, mat1: u32, mat2: u32, mat3: u32, mat4: u
|
||||
const start = rdtsc();
|
||||
|
||||
// Check sync gate — predict early exit
|
||||
// Assembly truth (NOT Ghidra decompiler labels):
|
||||
// +0x2C = animation_context_ptr (sync check at +0x10, timestamp at +0x0C)
|
||||
// +0x30 = model_container_ptr (+0x130 = M2 model header)
|
||||
const model_data = hook.readMem(u32, this + 0x10);
|
||||
var is_early = false;
|
||||
var bone_count: u32 = 0;
|
||||
if (model_data == 0) {
|
||||
is_early = true;
|
||||
} else {
|
||||
const anim_ctx = hook.readMem(u32, this + 0x30);
|
||||
const anim_ctx = hook.readMem(u32, this + 0x2C);
|
||||
if (anim_ctx != 0) {
|
||||
const sync_val = hook.readMem(u32, this + 0x40);
|
||||
const anim_sync = hook.readMem(u32, anim_ctx + 0x10);
|
||||
if (sync_val == anim_sync) is_early = true;
|
||||
}
|
||||
// Model header is at *(*(this+0x2C) + 0x130), bone count at +0x34
|
||||
const ptr_2c = hook.readMem(u32, this + 0x2C);
|
||||
if (ptr_2c != 0) {
|
||||
const model_hdr = hook.readMem(u32, ptr_2c + 0x130);
|
||||
// Model header: *(*(this+0x30) + 0x130), bone count at +0x34
|
||||
const model_ctr = hook.readMem(u32, this + 0x30);
|
||||
if (model_ctr != 0) {
|
||||
const model_hdr = hook.readMem(u32, model_ctr + 0x130);
|
||||
if (model_hdr != 0) {
|
||||
bone_count = hook.readMem(u32, model_hdr + 0x34);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user