fix(cdb): A6.P1 — a6-probe.cdb v2 with PDB-verified offsets
Replaces v1's broken-offset BP actions with PDB-authoritative field
reads. All offsets extracted from `dt acclient!TYPENAME` against the
loaded PDB (output preserved at tools/cdb/a6-types-dump.txt).
Key offsets:
Plane.N at +0x00, .d at +0x0c
CSphere.center at +0x00, .radius at +0x0c
CPolygon.plane at +0x20
SPHEREPATH.collide +0x104, .walkable_allowance +0x1b8, .walk_interp +0x1bc
CTransition.sphere_path +0x020 (so e.g. CTransition+0x174 = insert_type)
Per-BP arg-read fixes (all use __thiscall: ecx=this, args at [esp+N]):
BP1: substeps from [esp+4], insertType from this+0x174
BP2: walkable_allowance from this+0x1d8, normal.z from *(arg+8)
BP3: normal.x/y/z from *arg
BP4: collide+insertType via *(arg2+0x124/0x174), walkAllow from arg3
BP5 (the over-correction suspect): full plane + sphere + walk_interp +
movement vector. 12 fields, all double-indirect for pointer args.
BP6 SYMBOL FIXED: CTransition::check_walkable (v1 had
validate_walkable which doesn't exist; check_walkable confirmed
in symbols.json and at decomp line 272811).
BP7: plane + isWater from *arg.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d0c8c54d96
commit
7b9b26f647
2 changed files with 2999 additions and 23 deletions
|
|
@ -1,14 +1,33 @@
|
|||
$$
|
||||
$$ Phase A6.P1 cdb probe spike — 2026-05-21
|
||||
$$ Phase A6.P1 cdb probe spike v2 — 2026-05-21
|
||||
$$
|
||||
$$ 7 breakpoints on retail's BSP collision response sites. Each BP fires
|
||||
$$ a non-blocking action (printf + gc), incrementing a per-BP counter.
|
||||
$$ Auto-detaches via 'qd' after 50,000 total hits to avoid retail lag.
|
||||
$$ v2 changes (from v1 dry-run lessons + type dumper offsets):
|
||||
$$ - All struct offsets verified against PDB via dt acclient!TYPENAME
|
||||
$$ (see tools/cdb/a6-types-dump.txt for the source).
|
||||
$$ - BP6 symbol fixed: CTransition::check_walkable (v1 had
|
||||
$$ validate_walkable which doesn't exist in the PDB).
|
||||
$$ - Stack-arg reads use [esp+N] with double-indirect (poi(...)+offset)
|
||||
$$ for pointer args. v1 used arbitrary registers (@edx, @edi) and
|
||||
$$ produced all-zero data fields.
|
||||
$$
|
||||
$$ Symbol lookup convention per CLAUDE.md "Retail debugger toolchain":
|
||||
$$ - snake_case for BSPTREE, CTransition, OBJECTINFO, COLLISIONINFO, SPHEREPATH
|
||||
$$ - PascalCase for CPhysicsObj
|
||||
$$ - Always 'x' first to confirm the actual name
|
||||
$$ Calling convention: all functions __thiscall. ecx = this; non-this
|
||||
$$ args at [esp+4], [esp+8], [esp+0xC] in order, after the return
|
||||
$$ address at [esp+0].
|
||||
$$
|
||||
$$ 7 breakpoints, each with non-blocking action (printf + gc).
|
||||
$$ Auto-detach via qd after 50,000 total hits (CLAUDE.md gotcha:
|
||||
$$ high BP rates lag retail enough to trigger ACE timeout).
|
||||
$$
|
||||
$$ Field offsets used (from PDB dt dump):
|
||||
$$ Plane: N.x +0x00, N.y +0x04, N.z +0x08, d +0x0c
|
||||
$$ CSphere: center.x +0x00, .y +0x04, .z +0x08, radius +0x0c
|
||||
$$ CPolygon: plane +0x20 (so plane.N.x = polygon+0x20, .d = +0x2c)
|
||||
$$ SPHEREPATH: collide +0x104, insert_type +0x154,
|
||||
$$ walkable_allowance +0x1b8, walk_interp +0x1bc
|
||||
$$ CTransition:object_info +0x000, sphere_path +0x020,
|
||||
$$ (so sphere_path.collide = trans+0x124,
|
||||
$$ sphere_path.insert_type = trans+0x174,
|
||||
$$ sphere_path.walkable_allowance = trans+0x1d8)
|
||||
$$
|
||||
|
||||
.logopen /t a6-probe-${ARG_LOG_TAG}.log
|
||||
|
|
@ -26,28 +45,50 @@ r $t5 = 0
|
|||
r $t6 = 0
|
||||
r $t7 = 0
|
||||
|
||||
$$ BP1: CTransition::transitional_insert
|
||||
bp acclient!CTransition::transitional_insert "r $t1 = @$t1 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP1] transitional_insert hit#%d eax_2=%d insertType=%d\\n\", @$t1, poi(@ecx+0x18), poi(@ecx+0x1c); .if (@$t0 >= 50000) { qd } .else { gc }"
|
||||
$$ BP1: CTransition::transitional_insert(this, sub_step_count)
|
||||
$$ ecx = CTransition*; [esp+4] = sub_step_count (int)
|
||||
$$ read: sub_step_count, sphere_path.insert_type (trans+0x174)
|
||||
bp acclient!CTransition::transitional_insert "r $t1 = @$t1 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP1] transitional_insert hit#%d substeps=%d insertType=%d\\n\", @$t1, dwo(@esp+4), dwo(@ecx+0x174); .if (@$t0 >= 50000) { qd } .else { gc }"
|
||||
|
||||
$$ BP2: CTransition::step_up
|
||||
bp acclient!CTransition::step_up "r $t2 = @$t2 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP2] step_up hit#%d walkable_allowance=%f\\n\", @$t2, poi(@ecx+0x60); .if (@$t0 >= 50000) { qd } .else { gc }"
|
||||
$$ BP2: CTransition::step_up(this, step_up_normal)
|
||||
$$ ecx = CTransition*; [esp+4] = Vector3* step_up_normal
|
||||
$$ read: walkable_allowance (trans+0x1d8), step_up_normal.z (arg+8)
|
||||
bp acclient!CTransition::step_up "r $t2 = @$t2 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP2] step_up hit#%d walkAllow=%f normalZ=%f\\n\", @$t2, dwo(@ecx+0x1d8), dwo(poi(@esp+4)+8); .if (@$t0 >= 50000) { qd } .else { gc }"
|
||||
|
||||
$$ BP3: SPHEREPATH::set_collide
|
||||
bp acclient!SPHEREPATH::set_collide "r $t3 = @$t3 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP3] set_collide hit#%d normalZ=%f\\n\", @$t3, dwo(@edx+0x8); .if (@$t0 >= 50000) { qd } .else { gc }"
|
||||
$$ BP3: SPHEREPATH::set_collide(this, collision_normal)
|
||||
$$ ecx = SPHEREPATH*; [esp+4] = Vector3* collision_normal
|
||||
$$ read: normal.x, .y, .z via *arg
|
||||
bp acclient!SPHEREPATH::set_collide "r $t3 = @$t3 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP3] set_collide hit#%d nx=%f ny=%f nz=%f\\n\", @$t3, dwo(poi(@esp+4)+0), dwo(poi(@esp+4)+4), dwo(poi(@esp+4)+8); .if (@$t0 >= 50000) { qd } .else { gc }"
|
||||
|
||||
$$ BP4: BSPTREE::find_collisions
|
||||
bp acclient!BSPTREE::find_collisions "r $t4 = @$t4 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP4] find_collisions hit#%d collide=%d insertType=%d\\n\", @$t4, poi(@ecx+0x40), poi(@ecx+0x1c); .if (@$t0 >= 50000) { qd } .else { gc }"
|
||||
$$ BP4: BSPTREE::find_collisions(this, transition, walkable_allowance)
|
||||
$$ ecx = BSPTREE*; [esp+4] = CTransition*; [esp+8] = float walkable_allowance
|
||||
$$ read: trans.sphere_path.collide (trans+0x124),
|
||||
$$ trans.sphere_path.insert_type (trans+0x174),
|
||||
$$ walkable_allowance from arg3 (float-by-value at esp+8)
|
||||
bp acclient!BSPTREE::find_collisions "r $t4 = @$t4 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP4] find_collisions hit#%d collide=%d insertType=%d walkAllow=%f\\n\", @$t4, dwo(poi(@esp+4)+0x124), dwo(poi(@esp+4)+0x174), dwo(@esp+8); .if (@$t0 >= 50000) { qd } .else { gc }"
|
||||
|
||||
$$ BP5: CPolygon::adjust_sphere_to_plane (the over-correction suspect)
|
||||
bp acclient!CPolygon::adjust_sphere_to_plane "r $t5 = @$t5 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP5] adjust_sphere hit#%d Nx=%f Ny=%f Nz=%f d=%f cx=%f cy=%f cz=%f r=%f winterp=%f\\n\", @$t5, dwo(@ecx+0x0), dwo(@ecx+0x4), dwo(@ecx+0x8), dwo(@ecx+0xc), dwo(@edx+0x0), dwo(@edx+0x4), dwo(@edx+0x8), dwo(@edx+0xc), dwo(@edi+0x40); .if (@$t0 >= 50000) { qd } .else { gc }"
|
||||
$$ BP5: CPolygon::adjust_sphere_to_plane(this, sphere_path, sphere, movement)
|
||||
$$ ecx = CPolygon*; [esp+4] = SPHEREPATH*; [esp+8] = CSphere*; [esp+0xC] = Vector3* movement
|
||||
$$ THE OVER-CORRECTION SUSPECT. Captures EVERY arg field for paired delta analysis.
|
||||
$$ read: plane.N.{x,y,z} + plane.d (poly+0x20..+0x2c),
|
||||
$$ sphere.center.{x,y,z} + sphere.radius (via arg2),
|
||||
$$ sphere_path.walk_interp (sphere_path+0x1bc),
|
||||
$$ movement.{x,y,z} (via arg4)
|
||||
bp acclient!CPolygon::adjust_sphere_to_plane "r $t5 = @$t5 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP5] adjust_sphere hit#%d Nx=%f Ny=%f Nz=%f d=%f cx=%f cy=%f cz=%f r=%f winterp=%f mvx=%f mvy=%f mvz=%f\\n\", @$t5, dwo(@ecx+0x20), dwo(@ecx+0x24), dwo(@ecx+0x28), dwo(@ecx+0x2c), dwo(poi(@esp+8)+0), dwo(poi(@esp+8)+4), dwo(poi(@esp+8)+8), dwo(poi(@esp+8)+0xc), dwo(poi(@esp+4)+0x1bc), dwo(poi(@esp+0xc)+0), dwo(poi(@esp+0xc)+4), dwo(poi(@esp+0xc)+8); .if (@$t0 >= 50000) { qd } .else { gc }"
|
||||
|
||||
$$ BP6: CTransition::validate_walkable
|
||||
bp acclient!CTransition::validate_walkable "r $t6 = @$t6 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP6] validate_walkable hit#%d arg2=%f\\n\", @$t6, dwo(@esp+0x4); .if (@$t0 >= 50000) { qd } .else { gc }"
|
||||
$$ BP6: CTransition::check_walkable(this, threshold)
|
||||
$$ ecx = CTransition*; [esp+4] = float threshold
|
||||
$$ v1 used "validate_walkable" — that symbol doesn't exist in the PDB.
|
||||
$$ The actual function is check_walkable (verified against symbols.json
|
||||
$$ and decomp at acclient_2013_pseudo_c.txt:272811).
|
||||
bp acclient!CTransition::check_walkable "r $t6 = @$t6 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP6] check_walkable hit#%d threshold=%f\\n\", @$t6, dwo(@esp+4); .if (@$t0 >= 50000) { qd } .else { gc }"
|
||||
|
||||
$$ BP7: COLLISIONINFO::set_contact_plane
|
||||
bp acclient!COLLISIONINFO::set_contact_plane "r $t7 = @$t7 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP7] set_contact_plane hit#%d isWater=%d\\n\", @$t7, dwo(@esp+0x8); .if (@$t0 >= 50000) { qd } .else { gc }"
|
||||
$$ BP7: COLLISIONINFO::set_contact_plane(this, plane, is_water)
|
||||
$$ ecx = COLLISIONINFO*; [esp+4] = Plane*; [esp+8] = int is_water
|
||||
$$ read: plane.N + plane.d (via arg2), is_water (via arg3)
|
||||
bp acclient!COLLISIONINFO::set_contact_plane "r $t7 = @$t7 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP7] set_contact_plane hit#%d Nx=%f Ny=%f Nz=%f d=%f isWater=%d\\n\", @$t7, dwo(poi(@esp+4)+0), dwo(poi(@esp+4)+4), dwo(poi(@esp+4)+8), dwo(poi(@esp+4)+0xc), dwo(@esp+8); .if (@$t0 >= 50000) { qd } .else { gc }"
|
||||
|
||||
.printf "a6-probe armed: BPs 1-7 set, threshold=50000 total hits, qd on threshold\n"
|
||||
.printf "a6-probe v2 armed: BPs 1-7 set with PDB-verified offsets, threshold=50000 total hits, qd on threshold\n"
|
||||
|
||||
$$ Continue execution
|
||||
g
|
||||
|
|
|
|||
2935
tools/cdb/a6-types-dump.txt
Normal file
2935
tools/cdb/a6-types-dump.txt
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue