Files
viktorиClaude Opus 4.8 ae36c3b729 RT-4D: реверс прошивки, русификация, кастомный UI, флешеры
- Полный RE стока V3.25 (Cortex-M4F) + FM100B: карта памяти, протокол, codeplug, UI-архитектура
- Русификация: свой CP1251-шрифт + патч рендера, перевод меню и надписей, ребренд Ru-4D V3.25
- Блюпринт переделки UI + C-тулчейн (clang thumbv7em), доказан инъекцией
- Готовые флешеры: WebSerial .html и Windows .exe со вшитой прошивкой
- Дамп SPI рации, стоковая прошивка, инструменты сборки

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 15:47:22 +09:00

79 строки
3.6 KiB
Python

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}")