import struct, collections, sys, io
sys.stdout=io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
DUMP=r"C:/Users/vikto/Documents/Claude/rt-4d/radio-spi-dump.bin"
data=open(DUMP,'rb').read()

def hd(off,n=128,base=None):
    base=off if base is None else base
    out=[]
    for i in range(0,n,16):
        c=data[off+i:off+i+16]
        if not c:break
        h=' '.join(f'{b:02x}' for b in c)
        a=''.join(chr(b) if 32<=b<127 else '.' for b in c)
        out.append(f'{base+i:08x}  {h:<47}  {a}')
    return '\n'.join(out)

# 0x14C000: GBK char table. Decode as GBK 2-byte
print("### 0x14C000 GBK char list (first 40 chars) ###")
seg=data[0x14c000:0x14c000+80]
chars=[]
i=0
while i < len(seg)-1:
    b=seg[i]
    if b>=0x81:
        try:
            ch=bytes([seg[i],seg[i+1]]).decode('gbk')
            chars.append(ch); i+=2; continue
        except: pass
    i+=1
print(''.join(chars))
# Size and how it ends
end=0x14c000+0xB000
print("tail of 0x14C000 region:")
print(hd(end-64,64))

# Correlate: 0x14C000 is a GBK char table (34KB?) and 0x164000 pinyin (200KB).
# The pinyin region: are entries fixed 6 bytes each -> index maps to char in char table?
print("\n### relationship: char count vs pinyin count ###")
# count 2-byte gbk chars in 0x14C000..0x157000 (44KB used, then zeros)
charseg=data[0x14c000:0x157000]
nchars=0
i=0
while i<len(charseg)-1:
    if charseg[i]>=0x81 and charseg[i+1]!=0x00:
        nchars+=1; i+=2
    else:
        i+=1
print(f"approx gbk chars in 0x14C000-0x157000 (44KB): {nchars}")
# pinyin at 0x164000, 6 bytes each, over 200KB
print(f"pinyin entries if 6-byte: {0x32000//6}")
print(f"pinyin entries if 8-byte: {0x32000//8}")

# Check pinyin stride precisely: look for the pattern of fixed-width fields
print("\n### pinyin stride check: line up 'kao','shang','xia' ###")
# they appeared as 6-char fields: 'kao   ','shang ','xia   '
for w in range(4,10):
    # test if every field is exactly w and space padded
    ok=True
    for k in range(20):
        f=data[0x164000+k*w:0x164000+k*w+w]
        # must be ascii letters+spaces
        if not all(32<=b<127 for b in f): ok=False;break
    if ok:
        fields=[data[0x164000+k*w:0x164000+k*w+w] for k in range(12)]
        print(f"w={w}: {[f.decode() for f in fields]}")

# 0x100000 region: mostly zeros with some ascii. dump non-zero area
print("\n### 0x100000 non-zero content ###")
for i in range(0,0x1000,16):
    c=data[0x100000+i:0x100000+i+16]
    if any(b!=0 for b in c):
        h=' '.join(f'{b:02x}' for b in c)
        a=''.join(chr(b) if 32<=b<127 else '.' for b in c)
        print(f'{0x100000+i:08x}  {h:<47}  {a}')

# 0x03E000 DMRhub: bytes 0-3 = 00 00 00 00 then name. This resembles radio identity/owner?
# main_settings radio_name at cfg+76. Check cfg region for DMRhub
print("\n### search whole dump for 'DMRhub' ###")
o=0
while True:
    o=data.find(b'DMRhub',o)
    if o<0:break
    print(f"  @{o:08x}: {hd(o-8,32,o-8)}")
    o+=1

# 0x0D0000 keys: is this the 'groups'/grouplist backup? name 'Key N', b0=index, tail has index at +20
# Compare with grouplist format (b1==0x01 enabled, name @0x02). Here b0=idx,b1=00,name@2.
# Actually looks like encryption keys OR group lists. Check groups region 0x07C000
print("\n### groups region 0x07C000 ###")
print(hd(0x07c000,96))
# and encrypt region in .4rdmf is separate. Let's see full first key record 48 bytes across
print("\n### 0x0D0000 full stride verify: is it 0x30? show rec 100 ###")
print(hd(0x0d0000+100*0x30,48))
print("last populated:")
print(hd(0x0d0000+509*0x30,48))
