from __future__ import annotations import struct from pathlib import Path MAGIC_DATA_OFF = 33000 OPCODE_NAMES = { 0x81: "set_unused_field53", 0x82: "clear_unused_field53", 0x84: "set_target_objid", 0x85: "anim_walk", 0x86: "anim_run", 0x87: "anim_retreat", 0x88: "turn_left_90", 0x89: "turn_right_90", 0x8A: "fire_small_if_clear", 0x8B: "fire_large_if_clear", 0x8C: "anim_stand", 0x8D: "pathfind_home", 0x8E: "pathfind_target", 0x8F: "pathfind_midpoint", 0x92: "anim_kneel_and_fire", 0x93: "sleep_scaled", 0x94: "loiter", 0x95: "face_target", 0x96: "set_activity", 0x97: "switch_tactic", 0x98: "teleport_home", 0x99: "terminate", 0x9A: "jump_if_dist_lt_481", 0x9B: "jump_if_dist_gt_160", 0x9C: "jump_if_shot_blocked", 0x9D: "jump_if_shot_clear", 0x9E: "random_jump_nonzero", 0x9F: "loop_begin", 0xA6: "pathfind_marker_frame", 0xA7: "face_north", 0xA8: "face_south", 0xA9: "face_east", 0xAA: "face_west", 0xAB: "face_northeast", 0xAC: "face_southwest", 0xAD: "face_southeast", 0xAE: "face_northwest", 0xAF: "var_set", 0xB0: "var_add", 0xB1: "var_sub", 0xB2: "var_mul", 0xB3: "var_div", 0xB4: "var_store_curdir", 0xB5: "set_dir_raw", 0xB6: "var_store_curdir_again", 0xB7: "anim_kneeling_retreat", 0xB8: "anim_kneeling_advance", 0xB9: "anim_kneeling_slow_retreat", 0xC0: "jump", 0xC1: "loop_end", 0xFF: "flip_to_block1_restart", } def format_word(value: int) -> str: if value >= MAGIC_DATA_OFF: return f"var[{value - MAGIC_DATA_OFF}]" return f"0x{value:04x} ({value})" def decode_block(block: bytes, start_offset: int) -> list[str]: pc = 0 lines: list[str] = [] def need_word(use_data: bool) -> int: nonlocal pc value = struct.unpack_from(" int: return struct.unpack_from(" int: return struct.unpack_from(" None: path = Path("STATIC/COMBAT.DAT") data = path.read_bytes() print(f"file={path} size={len(data)}") print(f"header_magic_fill={data[:0x56].hex()[:32]}...") entries = [] for index in range(64): off = 0x80 + index * 8 rec_off = read_u32(data, off) rec_len = read_u32(data, off + 4) if rec_off == 0 and rec_len == 0: continue entries.append((index, rec_off, rec_len)) print(f"entry_count={len(entries)}") for index, rec_off, rec_len in entries: rec = data[rec_off:rec_off + rec_len] name = rec[:16].split(b"\0", 1)[0].decode("ascii") block_offsets = [read_u16(rec, 16 + i * 2) for i in range(4)] valid_blocks = [value for value in block_offsets if value and value < rec_len] print( f"[{index:02d}] off=0x{rec_off:04x} len=0x{rec_len:04x}" f" name={name:<16} block_offsets={block_offsets}" f" body_len={rec_len - min(valid_blocks) if valid_blocks else 0}" ) for block_no, block_off in enumerate(block_offsets[:2]): if not block_off or block_off >= rec_len: continue next_offsets = sorted(value for value in block_offsets[:2] if value > block_off and value < rec_len) block_end = next_offsets[0] if next_offsets else rec_len block = rec[block_off:block_end] print( f" block{block_no}: start=0x{block_off:04x} end=0x{block_end:04x}" f" size={block_end - block_off:02d} bytes={block.hex(' ')}" ) for line in decode_block(block, block_off): print(f" {line}") trailing_offsets = block_offsets[2:] print(f" extra_offsets_unused={trailing_offsets}") if __name__ == "__main__": main()