Crusader_Decomp/scripts/_tmp_parse_combat_dat.py
2026-04-09 00:32:12 +02:00

166 lines
No EOL
5.1 KiB
Python

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("<H", block, pc)[0]
pc += 2
return value
while pc < len(block):
opcode_offset = start_offset + pc
opcode = block[pc]
pc += 1
opname = OPCODE_NAMES.get(opcode, f"opcode_{opcode:02x}")
operands: list[str] = []
if opcode in {0x81, 0x84, 0x93, 0x94, 0x96, 0x97, 0x9A, 0x9B, 0x9C, 0x9D, 0xB5, 0xC0, 0x9F}:
operands.append(format_word(need_word(True)))
elif opcode == 0x9E:
operands.append(format_word(need_word(True)))
operands.append(format_word(need_word(True)))
elif opcode == 0xA6:
operands.append(f"frame={format_word(need_word(True))}")
elif opcode in {0xAF}:
operands.append(format_word(need_word(True)))
operands.append(format_word(need_word(False)))
elif opcode in {0xB0, 0xB1, 0xB2, 0xB3}:
operands.append(format_word(need_word(False)))
operands.append(format_word(need_word(True)))
elif opcode in {0xB4, 0xB6}:
operands.append(format_word(need_word(False)))
text = opname
if operands:
text += " " + ", ".join(operands)
lines.append(f"0x{opcode_offset:04x}: {text}")
return lines
def read_u32(data: bytes, offset: int) -> int:
return struct.unpack_from("<I", data, offset)[0]
def read_u16(data: bytes, offset: int) -> int:
return struct.unpack_from("<H", data, offset)[0]
def main() -> 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()