* OH1 walk-oracle capture: PARTS (2026-09-02). The high-hit-rate script -- * CPhysicsPart::Draw and RenderDeviceD3D::DrawMeshInternal fire far more * often per frame than DrawCells/DrawEnvCell/DrawBuilding (once per resident * part vs. once per cell/building). Frame count default 3 (substitute * ) to keep the trap cost bounded -- see * claude-memory/project_retail_debugger.md's "High hit rates lag the game" * watchout. Every bp action here is ONE .printf plus a handful of poi reads, * per the task's own instruction; no .while loops, no nested nested calls. * * AUTO-DETACH: same fall-through + top-level qd recipe as * oh-capture-walk.cdb.template / tools/cdb/pview-spiral2.cdb. Never qd * inside a bp action. * * LINE FORMATS: * F -- unchanged from FW0 * P -- unchanged from FW0 * PD gfx= did= force=<0|1> cell= -- NEW. * CPhysicsPart::Draw @0x0050D7A0 entry. gfx = the resolved * CGfxObj* for the part's current degrade level (this->gfxobj[deg_level]); * did = that GfxObj's DataID (see OPEN QUESTION below -- offset * unconfirmed, placeholder token); force = the arg2 force flag (0 = * ordinary CShadowPart::draw call, 1 = DrawBuilding's forced * portal-only pass); cell = the LAST cell DID a DrawPartCell breakpoint * saw (best-effort call-context correlation -- see note below, this can * be STALE for the first part drawn after a building's force pass and * before the frame's first ordinary object cell). * DM gfx= did= force=<0|1> bound=<0|1|2> cell= -- NEW. * RenderDeviceD3D::DrawMeshInternal @0x0059F360 entry. gfx/did are read * DIRECTLY from arg2 (no degrade-level resolution needed -- this is the * already-selected CGfxObj*, cheaper/more reliable than the PD line's * read); force = arg3; bound = arg4 (BoundingType: 0=OUTSIDE, * 1=PARTIALLY_INSIDE, 2=ENTIRELY_INSIDE per * oh1-built-mesh-view-contract.md's viewconeCheck table); cell = same * best-effort DrawPartCell correlation as PD. * There is no separate printed line for RenderDeviceD3D::DrawPartCell -- it * is instrumented SILENTLY (register store only, no .printf) purely to * supply the "cell" field above. DrawPartCell's own arg2 IS the cell DID * directly (acclient_2013_pseudo_c.txt:429177 -- DrawObjCell calls * "DrawPartCell(this, arg2->m_DID.id, &arg2->vtable, 0)"), so this is a * clean 1-instruction, zero-.printf hook cheap enough to run every part * draw's containing cell-walk turn. * * OFFSETS (VERIFY-IN-RECON -- see oh-recon.cdb's dt list): * CPhysicsPart+0x14 = deg_level, +0x1C = draw_state, +0x20 = gfxobj, * +0x24 = gfxobj_scale, +0x78 = draw_pos, +0xC0 = material, +0xC4 = * surfaces, +0xDC = m_current_render_frame_num, +0xE0 = physobj. * +0x1C/+0x20/+0xC0/+0xC4/+0xDC/+0xE0 are TRIPLE-confirmed: OH1's own * Ghidra table (oh1-built-mesh-view-contract.md "View state"/"Built * content and stamps") cites them independently, AND they fall out of * acclient.h:31151-31173's field order (CYpt float @0x00, viewer_heading * Vector3 @0x04, degrades ptr @0x10, deg_level uint @0x14, deg_mode int * @0x18, draw_state int @0x1C, gfxobj ptr @0x20, ...). +0x14 (deg_level) * is ONLY the acclient.h-order derivation -- OH1 never independently * Ghidra-cited that specific field, though its immediate neighbors * (+0x1C, +0x20) are both confirmed, which is reassuring but not proof. * Confirm with `dt acclient!CPhysicsPart` in oh-recon.cdb before trusting * the PD line's degrade-level GfxObj resolution. * CGfxObj+0x6C = constructed_mesh, +0x70 = use_built_mesh, +0x90 = * drawing_sphere, +0x94 = drawing_bsp -- same triple-confirmation * (OH1 table + acclient.h:31712-31731 field order, matched exactly). * Not directly read by this script, listed for completeness/cross-check. * RenderDeviceD3D::DrawMeshInternal(this@ecx, CGfxObj* arg2, uint8 arg3, * BoundingType arg4) -- arg2 @ esp+4, arg3 @ esp+8, arg4 @ esp+0xC. * Signature: acclient_2013_pseudo_c.txt:427965 ("this @ ecx" annotation, * same shape as the already-proven DrawBuilding/DrawEnvCell hooks). * CPhysicsPart::Draw(this, int32 arg2) -- plain __thiscall, this=ecx, * arg2 (force) @ esp+4. Signature: acclient_2013_pseudo_c.txt:274964. * RenderDeviceD3D::DrawPartCell(this@ecx, uint32 arg2, CPartCell* arg3, * uint32 arg4) -- arg2 (cell DID) @ esp+4. Signature: * acclient_2013_pseudo_c.txt:429198. * * RESOLVED 2026-09-02 (lead): DBObj::m_DID is at +0x28. DBObj : Interface lays out vfptr@0, m_dataCategory@4, m_bLoaded@8, m_timeStamp(8-byte)@0x10, m_pNext@0x18, m_pLast@0x1C, m_pMaintainer@0x20, m_numLinks@0x24, m_DID@0x28; DBObj is the FIRST base of CGfxObj and of CObjCell (via SerializeUsingPackDBObj), and CEnvCell+0x28 is the offset every FW0 capture already proved. oh-recon.cdb's dt acclient!DBObj re-confirms. Original note kept below for provenance: * GfxObj DataID offset. CGfxObj : DBObj (acclient.h:31712), and DBObj's * own m_DID field (acclient.h:27579) sits behind an "Interface" vtable * base, a `long double` timestamp, and several pointers whose EXACT * compiler-inserted padding this research could not hand-derive with * confidence (x86 MSVC alignment of the `long double`/double member under * DBObj's own __declspec(align(4)) is genuinely ambiguous from the header * text alone). DO NOT trust the placeholder below. Replace * 0x28 with the real hex offset oh-recon.cdb's * `dt acclient!CGfxObj` / `dt acclient!DBObj` output reports for m_DID * before running this script for real. There is no runner-substituted * default for this token (oh-run-capture.ps1 only fills /) -- * hand-edit the two `0x28` occurrences below. * * CALLING-CONVENTION NOTE: CPhysicsPart::Draw and DrawMeshInternal both use * the proven "this@ecx (or plain thiscall), remaining args on the stack * starting at esp+4" shape already exercised by the FW0 BLD/DI hooks, so * this script's stack reads carry the SAME confidence as those. This is * NOT true of the alpha-list functions in oh-capture-alpha-depth.cdb.template * -- see that file's own calling-convention note. * * BEFORE USE: replace (plain decimal digits), (output path), * and BOTH occurrences of 0x28 (see OPEN QUESTION above). * is pre-resolved to Render::viewer_pos @0x0081ef00, same as * oh-capture-walk.cdb.template. .logopen .sympath C:\Users\erikn\source\repos\acdream\refs .symopt+ 0x40 .reload /f acclient.exe r $t0 = 0 r $t5 = 0 bp 00453aa0 "r $t0 = @$t0 + 1; .printf \"F %d\\n\", @$t0; .printf \"P %08x %08x %08x %08x %08x %08x %08x %08x\\n\", poi(0081ef00+4), poi(0081ef00+0x3c), poi(0081ef00+0x40), poi(0081ef00+0x44), poi(0081ef00+8), poi(0081ef00+0xc), poi(0081ef00+0x10), poi(0081ef00+0x14); .if (@$t0 < 0n) { gc }" bp 005a07a0 "r $t5 = poi(@esp+4); gc" bp 0050d7a0 "r $t6 = poi(poi(@ecx+0x20) + poi(@ecx+0x14)*4); .printf \"PD gfx=%08x did=%08x force=%d cell=%08x\\n\", @$t6, poi(@$t6+0x28), poi(@esp+4), @$t5; gc" bp 0059f360 "r $t7 = poi(@esp+4); .printf \"DM gfx=%08x did=%08x force=%d bound=%d cell=%08x\\n\", @$t7, poi(@$t7+0x28), by(@esp+8), poi(@esp+0xc), @$t5; gc" g .echo ===DETACHING=== qd .logclose