#!/usr/bin/env python3
"""Dump executable data tables with pointer/string classification."""
from __future__ import annotations

import argparse
import struct
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent))

from probe_exe_scene_tables import find_cns_strings, read_sections, va_to_offset


def parse_int(text: str) -> int:
    return int(text, 0)


def section_for_va(sections: list[dict], va: int) -> dict | None:
    for section in sections:
        start = section["va"]
        end = section["va"] + section["raw_size"]
        if start <= va < end:
            return section
    return None


def classify_value(value: int, sections: list[dict], strings: dict[int, str]) -> str:
    if value in strings:
        return strings[value]
    section = section_for_va(sections, value)
    if section:
        return f"{section['name']}+0x{value - section['va']:x}"
    high = value >> 16
    low = value & 0xFFFF
    if high <= 0x200 and low <= 0x200:
        return f"pair({low},{high})"
    return ""


def dump_dwords(data: bytes, sections: list[dict], strings: dict[int, str], va: int, count: int) -> str:
    offset = va_to_offset(sections, va)
    if offset is None:
        raise ValueError(f"VA 0x{va:08x} is not in a raw section")
    lines = [
        "# EXE Data Table Dump",
        "",
        f"Start VA: `0x{va:08x}`",
        "",
        "| index | VA | raw | u32 | u16 lo | u16 hi | classification |",
        "| ---: | --- | --- | --- | ---: | ---: | --- |",
    ]
    for index in range(count):
        item_offset = offset + index * 4
        item_va = va + index * 4
        raw = data[item_offset : item_offset + 4]
        if len(raw) < 4:
            break
        value = struct.unpack("<I", raw)[0]
        lo, hi = struct.unpack("<HH", raw)
        lines.append(
            "| {index} | `{va}` | `{raw}` | `{value}` | {lo} | {hi} | {classification} |".format(
                index=index,
                va=f"0x{item_va:08x}",
                raw=raw.hex(" "),
                value=f"0x{value:08x}",
                lo=lo,
                hi=hi,
                classification=classify_value(value, sections, strings) or "-",
            )
        )
    lines.append("")
    return "\n".join(lines)


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--exe", type=Path, default=Path("Hwanse2.exe"))
    parser.add_argument("--va", type=parse_int, required=True)
    parser.add_argument("--count", type=int, default=64)
    parser.add_argument("--out", type=Path)
    args = parser.parse_args()

    data = args.exe.read_bytes()
    sections = read_sections(data)
    strings = find_cns_strings(data, sections)
    text = dump_dwords(data, sections, strings, args.va, args.count)
    if args.out:
        args.out.parent.mkdir(parents=True, exist_ok=True)
        args.out.write_text(text, encoding="utf-8")
        print(f"wrote table dump -> {args.out}")
    else:
        print(text)


if __name__ == "__main__":
    main()
