acdream/tools/cdb/a6-probe.cdb
Erik 1b6d49ea57 fix(cdb): A6.P1 — a6-probe.cdb v3 with C++ float reads
v2 dry-run produced correct hit counts but all %f field values
printed as 0.000000 — including BP6 threshold which the decomp says
must be 0.0871556997f (cos 85°). Root cause: cdb's MASM evaluator
returns dwo(addr) as a 32-bit integer; .printf %f expects a 64-bit
double; passing the integer to %f produces formatted-zero garbage.

Fix: switch all float-reading expressions to @@c++(*(float*)addr).
The C++ evaluator dereferences memory as a float pointer, returning
a proper float that .printf %f formats correctly. Integer reads (%d)
still use MASM dwo() — that works.

For double-indirect (pointer args), the form is
  @@c++(*(float*)(*(unsigned int*)(@esp+N)+offset))
which reads the pointer at [esp+N], adds the offset, and treats the
result as a float pointer.

v2 capture preserved as retail-v2-zero-floats.log audit trail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 19:50:11 +02:00

90 lines
5.9 KiB
Text

$$
$$ Phase A6.P1 cdb probe spike v3 — 2026-05-21
$$
$$ v3 changes (from v2 dry-run lesson):
$$ - All float reads now use @@c++(*(float*)addr) instead of dwo(addr).
$$ cdb's MASM evaluator treats dwo() as a 32-bit integer, which then
$$ prints as 0.000000 when passed to .printf %f (which expects double).
$$ The @@c++ expression forces C++ interpretation, dereferencing the
$$ address as a float pointer, producing a proper float that printf %f
$$ can format. v2 hit counts were correct but all %f values were 0.
$$ - Integer reads (%d) still use dwo() — that works.
$$
$$ v2 changes (from v1 dry-run):
$$ - All struct offsets verified against PDB via dt acclient!TYPENAME.
$$ - BP6 symbol fixed: CTransition::check_walkable.
$$ - Stack-arg reads use [esp+N] with proper double-indirect.
$$
$$ 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].
$$
$$ 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
.sympath C:\Users\erikn\source\repos\acdream\refs
.symopt+ 0x40
.reload /f acclient.exe
$$ Counters: $t0 = total hits, $t1..$t7 = per-BP hits
r $t0 = 0
r $t1 = 0
r $t2 = 0
r $t3 = 0
r $t4 = 0
r $t5 = 0
r $t6 = 0
r $t7 = 0
$$ BP1: CTransition::transitional_insert(this, sub_step_count)
$$ ecx = CTransition*; [esp+4] = sub_step_count (int)
$$ read: sub_step_count (int), sphere_path.insert_type (int) at trans+0x174
$$ All integer fields; no @@c++ needed.
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(this, step_up_normal)
$$ ecx = CTransition*; [esp+4] = Vector3* step_up_normal
$$ read: walkable_allowance (float at trans+0x1d8), step_up_normal.z (float via arg)
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, @@c++(*(float*)(@ecx+0x1d8)), @@c++(*(float*)(*(unsigned int*)(@esp+4)+8)); .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 (floats 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, @@c++(*(float*)(*(unsigned int*)(@esp+4)+0)), @@c++(*(float*)(*(unsigned int*)(@esp+4)+4)), @@c++(*(float*)(*(unsigned int*)(@esp+4)+8)); .if (@$t0 >= 50000) { qd } .else { gc }"
$$ BP4: BSPTREE::find_collisions(this, transition, walkable_allowance)
$$ ecx = BSPTREE*; [esp+4] = CTransition*; [esp+8] = float walkable_allowance (by-value!)
$$ read: trans.sphere_path.collide (int via arg2+0x124),
$$ trans.sphere_path.insert_type (int via arg2+0x174),
$$ walkable_allowance (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), @@c++(*(float*)(@esp+8)); .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 as float for paired delta analysis.
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, @@c++(*(float*)(@ecx+0x20)), @@c++(*(float*)(@ecx+0x24)), @@c++(*(float*)(@ecx+0x28)), @@c++(*(float*)(@ecx+0x2c)), @@c++(*(float*)(*(unsigned int*)(@esp+8)+0)), @@c++(*(float*)(*(unsigned int*)(@esp+8)+4)), @@c++(*(float*)(*(unsigned int*)(@esp+8)+8)), @@c++(*(float*)(*(unsigned int*)(@esp+8)+0xc)), @@c++(*(float*)(*(unsigned int*)(@esp+4)+0x1bc)), @@c++(*(float*)(*(unsigned int*)(@esp+0xc)+0)), @@c++(*(float*)(*(unsigned int*)(@esp+0xc)+4)), @@c++(*(float*)(*(unsigned int*)(@esp+0xc)+8)); .if (@$t0 >= 50000) { qd } .else { gc }"
$$ BP6: CTransition::check_walkable(this, threshold)
$$ ecx = CTransition*; [esp+4] = float threshold (by-value)
$$ Expected: ~0.0871556997 (cos 85°) per decomp at line 273202.
bp acclient!CTransition::check_walkable "r $t6 = @$t6 + 1; r $t0 = @$t0 + 1; .printf /D \"[BP6] check_walkable hit#%d threshold=%f\\n\", @$t6, @@c++(*(float*)(@esp+4)); .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 (floats via arg2), is_water (int 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, @@c++(*(float*)(*(unsigned int*)(@esp+4)+0)), @@c++(*(float*)(*(unsigned int*)(@esp+4)+4)), @@c++(*(float*)(*(unsigned int*)(@esp+4)+8)), @@c++(*(float*)(*(unsigned int*)(@esp+4)+0xc)), dwo(@esp+8); .if (@$t0 >= 50000) { qd } .else { gc }"
.printf "a6-probe v3 armed: BPs 1-7 set with PDB-verified offsets + C++ float reads, threshold=50000 total hits, qd on threshold\n"
$$ Continue execution
g