#!/usr/bin/env python3
"""First-look recon of the RT-4D 4MB SPI dump."""
import sys, string

DUMP = r"C:/Users/vikto/Documents/Claude/rt-4d/radio-spi-dump.bin"
data = open(DUMP, "rb").read()
N = len(data)
print(f"== FILE ==  {N} bytes (0x{N:X}) = {N/1024/1024:.2f} MB\n")

# Known map (from jcalado constants.py)
SPI_REGIONS = {
    "calibration":   (0x000000, 0x001000, 0x40),
    "main_settings": (0x002000, 0x001000, 0x90),
    "channels":      (0x004000, 0x00C000, 0x91),
    "zones":         (0x01C000, 0x020000, 0x92),
    "contacts":      (0x05C000, 0x010000, 0x93),
    "groups":        (0x07C000, 0x003000, 0x94),
    "dmr_keys":      (0x082000, 0x003000, 0x95),
    "call_log":      (0x088000, 0x00C000, 0x96),
    "default_sms":   (0x094000, 0x001000, 0x97),
    "msg_drafts":    (0x095000, 0x010000, None),
    "msg_inbox":     (0x0A5000, 0x010000, None),
    "msg_outbox":    (0x0B5000, 0x010000, None),
    "schedules":     (0x0C6000, 0x008000, 0x98),
    "dtmf_names":    (0x0C7000, 0x000100, 0x80),
    "fm_settings":   (0x0D6000, 0x001000, 0x99),
}

def classify(b):
    if all(x == 0xFF for x in b): return "empty(FF)"
    if all(x == 0x00 for x in b): return "zero(00)"
    return "DATA"

# ---- occupancy map at 4KB granularity ----
PAGE = 0x1000
pages = N // PAGE
kinds = [classify(data[i*PAGE:(i+1)*PAGE]) for i in range(pages)]
# collapse to runs
print("== OCCUPANCY (4KB pages) ==")
runs = []
s = 0
for i in range(1, pages+1):
    if i == pages or kinds[i] != kinds[s]:
        runs.append((s*PAGE, i*PAGE, kinds[s]))
        s = i
data_bytes = sum(1 for k in kinds if k == "DATA")*PAGE
for a, b, k in runs:
    tag = ""
    if k == "DATA":
        # which known regions overlap
        names = [n for n,(ra,rs,_) in SPI_REGIONS.items() if ra < b and ra+rs > a]
        tag = " <- " + (",".join(names) if names else "??? UNKNOWN")
    print(f"  0x{a:06X}-0x{b:06X}  {(b-a)//1024:>5}KB  {k}{tag}")
print(f"\n  DATA pages total: {data_bytes/1024:.0f}KB of {N/1024:.0f}KB")

# ---- known region occupancy check ----
print("\n== KNOWN REGIONS (populated?) ==")
for name,(addr,size,rid) in SPI_REGIONS.items():
    if addr+size > N:
        print(f"  {name:14} 0x{addr:06X} OUT OF RANGE"); continue
    reg = data[addr:addr+size]
    nonff = sum(1 for x in reg if x != 0xFF)
    first = reg[:12].hex(' ')
    rids = "None" if rid is None else hex(rid)
    print(f"  {name:14} 0x{addr:06X}+0x{size:05X} rid={rids:>4}  nonFF={nonff:>6}/{size:<6}  first={first}")

# ---- DTCN magic (settings bank detection) ----
print("\n== DTCN magic (beta bank markers) ==")
for off in (0x002FFC, 0x003FFC):
    print(f"  @0x{off:06X}: {data[off:off+4]!r}")
mcount = data.count(b'DTCN')
print(f"  total 'DTCN' occurrences in dump: {mcount}")

# ---- calibration hexdump ----
print("\n== CALIBRATION region 0x000000..0x000100 ==")
for r in range(0, 0x100, 16):
    chunk = data[r:r+16]
    hexs = ' '.join(f'{x:02X}' for x in chunk)
    asc = ''.join(chr(x) if 32<=x<127 else '.' for x in chunk)
    print(f"  {r:04X}  {hexs}  {asc}")

# ---- address book / high region probe ----
print("\n== HIGH REGION (0x0E0000+) probe ==")
for addr in (0x0D7000, 0x0E0000, 0x100000, 0x200000, 0x300000, 0x3F0000):
    reg = data[addr:addr+64]
    nonff = sum(1 for x in reg if x != 0xFF)
    asc = ''.join(chr(x) if 32<=x<127 else '.' for x in reg[:32])
    print(f"  0x{addr:06X} nonFF={nonff}/64  ascii={asc!r}")

# ---- strings ----
print("\n== STRINGS (printable runs >=4, first 60) ==")
printable = set(bytes(string.printable[:-6], 'ascii'))
runs_s = []
cur = bytearray(); start = 0
for i,x in enumerate(data):
    if 32 <= x < 127:
        if not cur: start = i
        cur.append(x)
    else:
        if len(cur) >= 4: runs_s.append((start, bytes(cur)))
        cur = bytearray()
if len(cur) >= 4: runs_s.append((start, bytes(cur)))
print(f"  total string runs: {len(runs_s)}")
for off, s in runs_s[:60]:
    try: txt = s.decode('ascii')
    except: txt = repr(s)
    print(f"    0x{off:06X}  {txt}")

# ---- first channel records ----
print("\n== CHANNEL records @0x004000 (48 bytes each) ==")
CH = 0x004000; SZ = 48
for i in range(6):
    rec = data[CH+i*SZ:CH+i*SZ+SZ]
    if all(x==0xFF for x in rec):
        print(f"  ch{i}: <empty>"); continue
    print(f"  ch{i}: {rec.hex(' ')}")
    asc = ''.join(chr(x) if 32<=x<127 else '.' for x in rec)
    print(f"        ascii: {asc}")
