"""Cross-check acclient_function_map.md addresses against symbols.json. For each row, looks up the function name in the PDB symbols table and flags mismatches (mid-body addresses, wrong addresses, missing names). Run with: py tools/pdb-extract/check_function_map.py Used to close ISSUES.md #9 (address-correction sweep). """ import json from pathlib import Path REPO = Path(__file__).resolve().parent.parent.parent SYMBOLS = REPO / "docs" / "research" / "named-retail" / "symbols.json" # (claimed_address, qualified_name) tuples from acclient_function_map.md. # Format: lowercase hex no leading 0, then the demangled Class::Method. ENTRIES = [ # CPhysicsObj ("515020", "CPhysicsObj::update_object"), ("5111d0", "CPhysicsObj::UpdatePhysicsInternal"), ("511420", "CPhysicsObj::calc_acceleration"), ("511ec0", "CPhysicsObj::set_velocity"), ("511fa0", "CPhysicsObj::set_local_velocity"), ("511de0", "CPhysicsObj::set_on_walkable"), ("511560", "CPhysicsObj::report_collision_start"), ("513ac0", "CPhysicsObj::report_collision_end"), ("513b60", "CPhysicsObj::handle_obj_collision"), ("515280", "CPhysicsObj::handle_collision"), ("513730", "CPhysicsObj::UpdatePositionInternal"), ("50f940", "CPhysicsObj::calc_friction"), ("510080", "CPhysicsObj::check_contact_velocity"), # CMotionInterp ("5286b0", "CMotionInterp::get_jump_v_z"), ("528660", "CMotionInterp::jump_charge_is_allowed"), ("5285e0", "CMotionInterp::motion_allows_jump"), ("528ec0", "CMotionInterp::jump_is_allowed"), ("528cd0", "CMotionInterp::get_leave_ground_velocity"), ("529390", "CMotionInterp::jump"), ("529710", "CMotionInterp::LeaveGround"), ("5296d0", "CMotionInterp::HitGround"), ("528960", "CMotionInterp::get_state_velocity"), ("528a50", "CMotionInterp::StopCompletely"), ("5287f0", "CMotionInterp::adjust_motion"), ("5293f0", "CMotionInterp::apply_raw_movement"), ("529210", "CMotionInterp::apply_current_movement"), ("528dd0", "CMotionInterp::contact_allows_move"), ("528f70", "CMotionInterp::DoInterpretedMotion"), ("529080", "CMotionInterp::StopInterpretedMotion"), ("529140", "CMotionInterp::StopMotion"), ("529930", "CMotionInterp::DoMotion"), ("529a90", "CMotionInterp::PerformMovement"), # CLandBlockStruct ("531d10", "CLandBlockStruct::IsSWtoNECut"), ("532a50", "CLandBlockStruct::ConstructPolygons"), ("532eb0", "CLandBlockStruct::ConstructUVs"), ("532d10", "CLandBlockStruct::unpack"), ("531f10", "CLandBlockStruct::get_packed_size"), ("532440", "CLandBlockStruct::AdjustPlanes"), ("532290", "CLandBlockStruct::CalcCellWater"), # CLandBlock ("530690", "CLandBlock::Init"), ("5307e0", "CLandBlock::release_all"), ("531780", "CLandBlock::init_static_objs"), ("531000", "CLandBlock::release_visible_cells"), ("5301e0", "CLandBlock::grab_visible_cells"), ("530650", "CLandBlock::add_server_object"), # LandDefs ("5aaa30", "CLandDefs::get_vars"), ("5aabb0", "CLandDefs::get_outside_lcoord"), ("5aac70", "CLandDefs::AdjustToOutside"), ("5aab50", "CLandDefs::get_block_dir"), # Collision / Transition (some entries are CCylSphere / CSphere / CTransition / CPolygon) ("5384e0", "CSphere::SphereIntersectsRay"), ("539500", "CPolygon::sphere_intersects_poly"), ("539750", "CPolygon::sphere_intersects_solid"), ("539ba0", "CPolygon::find_time_of_collision"), ("539df0", "CPolygon::find_time_of_collision"), ("53a040", "CPolygon::find_walkable_collision"), ("539110", "CPolygon::calc_normal"), ("539060", "CPlane::ray_plane_intersect"), ("538eb0", "CSphere::slide_sphere"), ("538f50", "CSphere::land_on_sphere"), ("538180", "CTransition::collide_with_point"), ("5387c0", "CTransition::find_collisions"), ("53a230", "CPolygon::hits_walkable"), # PhysicsEngine ("452a10", "PhysicsEngine::update"), ] def main(): with open(SYMBOLS, "r", encoding="utf-8") as f: symbols = json.load(f) # Build name -> [addresses] (some names appear multiple times: overloads) by_name = {} for s in symbols: by_name.setdefault(s["name"], []).append(s["address"]) # Build address -> name (lowercase hex without 0x prefix) by_addr = {} for s in symbols: addr = s["address"][2:].lower().lstrip("0") or "0" by_addr[addr] = s["name"] confirmed = 0 addr_corrections = [] name_misses = [] for claimed_addr, claimed_name in ENTRIES: # Look up by name if claimed_name in by_name: pdb_addrs = by_name[claimed_name] normalized_claimed = claimed_addr.lower().lstrip("0") or "0" match = any( (a[2:].lower().lstrip("0") or "0") == normalized_claimed for a in pdb_addrs ) if match: confirmed += 1 else: addr_corrections.append((claimed_addr, claimed_name, pdb_addrs)) else: # Name not found exactly. Try the claimed address as fallback. normalized_claimed = claimed_addr.lower().lstrip("0") or "0" actual_name = by_addr.get(normalized_claimed) name_misses.append((claimed_addr, claimed_name, actual_name)) print(f"Confirmed (address + name match): {confirmed}/{len(ENTRIES)}") print() if addr_corrections: print("ADDRESS CORRECTIONS (name correct, address wrong — likely mid-body):") for claimed_addr, name, pdb_addrs in addr_corrections: print(f" 0x{claimed_addr.upper()} {name}") print(f" actual: {', '.join(pdb_addrs)}") print() if name_misses: print("NAME MISMATCHES (name not found by exact match in PDB):") for claimed_addr, name, actual_name in name_misses: print(f" 0x{claimed_addr.upper()} {name}") if actual_name: print(f" PDB at 0x{claimed_addr.upper()}: {actual_name}") else: print(f" PDB has no symbol at 0x{claimed_addr.upper()}") print() main()