import struct, 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() # 0x0D0000 key region: list all populated records, their index byte0-1 (LE u16) and names print("### 0x0D0000 key records: idx(le16)/name ###") recs=[] for i in range(24576//0x30): o=0x0d0000+i*0x30 if data[o]==0xff and data[o+1]==0xff: continue idx=data[o]|(data[o+1]<<8) name=data[o+2:o+16].split(b'\xff')[0].decode('latin1','ignore') tail=data[o+16:o+24].hex() recs.append((i,idx,name,tail)) print(f"total populated: {len(recs)}") print("first 3:", recs[:3]) print("records 252-258:", recs[252:258]) print("last 3:", recs[-3:]) # check monotonic idx idxs=[r[1] for r in recs] print("idx min",min(idxs),"max",max(idxs),"monotonic:",idxs==sorted(idxs)) print("count distinct idx:",len(set(idxs))) # main_settings region: is 0x010000 exactly channels backup? 0x010000 is inside zones region (0x01C000 starts later). # Actually 0x010000 is BEFORE channels end? channels=0x004000+0xC000=0x010000. So 0x010000 is the byte right AFTER channels! print("\n### boundary check ###") print("channels region: 0x004000 .. 0x010000 (end).") print("So 0x010000 is a SEPARATE region right after channels, holding a 2nd copy of first 2 channels.") # zones region 0x01C000; but DMRhub found at 0x01e004 (inside zones) and 0x03e004. # 0x01C000+0x20000=0x03C000 end of zones. 0x03E000 is AFTER zones. print("zones region: 0x01C000 .. 0x03C000. DMRhub at 0x01e004 is INSIDE zones region.") print("0x03E000 (after zones) also has DMRhub -> a backup bank of the 0x01E000 data.") print(" -> 0x01E000 and 0x03E000 are paired A/B banks, 0x20000 apart (ZONE_AB_BANK_OFFSET).") print(f" 0x03E000-0x01E000 = 0x{0x03E000-0x01E000:X}") # 0x010000: is there a matching bank 0x20000 later? 0x010000+0x20000=0x030000 print("\n### does 0x010000 have A/B twin at 0x030000? ###") same=sum(1 for i in range(0x1000) if data[0x010000+i]==data[0x030000+i]) print(f"0x010000 vs 0x030000 match: {same}/{0x1000}") # hexdump 0x030000 def hd(off,n=64): out=[] for i in range(0,n,16): c=data[off+i:off+i+16] 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'{off+i:08x} {h:<47} {a}') return '\n'.join(out) print(hd(0x030000,80)) # What region is 0x010000 really? It has channel records. channels region is 1024*48=0xC000 exactly. # 0x004000..0x010000 IS the channels region (0xC000). 0x010000 begins the NEXT thing. # But 0x010000 duplicates ch0/ch1. This is the beta41 "channels bank B"? No - stock. # More likely: 0x010000 = VFO/current-operating channel scratch (VFO A and VFO B saved as channel records) print("\n### 0x010000 interpretation: VFO A/B saved channels ###") print(hd(0x010000,96)) # 0x126000 records: confirm 32-byte call-log entries with timestamps. Count valid. print("\n### 0x126000 call-log-style: count records with valid date ###") valid=0 for i in range(20480//32): o=0x126000+i*32 yy=data[o+14] if 20<=yy<=40: # plausible year 2020-2040 valid+=1 print(f"records with plausible date byte (yy 20-40): {valid}") # show last few valid for i in range(20480//32): o=0x126000+i*32 if data[o+14]==0 and all(b==0 for b in data[o:o+20]): print(f"first all-zero record at idx {i} (@{o:08x})") break # 0x100000: bitmap? width guess. Non-zero starts 0x190. Try rendering as 1bpp. print("\n### 0x100000 as bitmap: nonzero span ###") nz=[i for i in range(0x1000) if data[0x100000+i]!=0] print(f"nonzero bytes: {len(nz)}, span 0x{nz[0]:x}..0x{nz[-1]:x}")