import json with open(r'k:\ghidra\Crusader_Decomp\ne_reloc_fixups.json') as f: fixups = json.load(f) known_callf_addrs = { '0007:101c': 'entity_ai_update_loop call#1 (entity_slot_fetch)', '0007:1093': 'entity_ai_update_loop call#2 (entity_tick_dispatch)', '0007:2261': 'snap_entity_to_ground call (ground snap thunk)', '0007:27dc': 'anim_frame_update call#1 (completion_callback)', '0007:281e': 'anim_frame_update call#3 (notify_frame_progress)', '0007:2851': 'anim_frame_update call#4 (entity_sprite_advance)', '0007:8666': 'entity_sync_tile_aux thunk (tile_type_notify)', } def ghidra_to_file(addr_str): seg, off = addr_str.split(':') return (int(seg, 16) << 16) + int(off, 16) # Build a lookup dict by source_file_offset for speed by_offset = {} for f in fixups: by_offset[f['source_file_offset']] = f for addr, desc in sorted(known_callf_addrs.items()): callf_file = ghidra_to_file(addr) print(f"\n{addr} = {desc}") print(f" CALLF file offset: 0x{callf_file:X}") # The NE fixup offset points to where the patched value goes. # For CALLF (9A xx xx xx xx), the operand is at addr+1. # But the reloc chain offset is relative to segment start. # Let's search for any fixup within +/-2 of both callf_file and callf_file+1 for delta in range(0, 5): test_off = callf_file + delta if test_off in by_offset: m = by_offset[test_off] tgt = m.get('target', '?') tgt_g = m.get('target_ghidra', '?') print(f" FOUND at +{delta}: file=0x{test_off:X} seg{m['source_seg']:03d}+0x{m['source_offset_in_seg']:04X}") print(f" -> {tgt} (ghidra: {tgt_g})") break else: print(f" NOT FOUND in range [+0..+4]") # Show what segment this falls in for s in range(1, 146): entry = [x for x in fixups if x['source_seg'] == s] if entry: # not efficient but ok for debugging pass