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>
Этот коммит содержится в:
@@ -0,0 +1,87 @@
|
||||
import struct, sys
|
||||
sys.path.insert(0, r"C:/Users/vikto/Documents/Claude/rt-4d/rt4d-cps")
|
||||
from rt4d_codeplug.tones import decode_subaudio_bytes
|
||||
|
||||
DUMP = r"C:/Users/vikto/Documents/Claude/rt-4d/radio-spi-dump.bin"
|
||||
data = open(DUMP, "rb").read()
|
||||
print("size", len(data))
|
||||
|
||||
CH_BASE = 0x004000
|
||||
ZN_BASE = 0x01C000
|
||||
CT_BASE = 0x05C000
|
||||
|
||||
def hx(b): return " ".join(f"{x:02X}" for x in b)
|
||||
|
||||
def parse_bcd(bcd):
|
||||
if all(b==0xFF for b in bcd): return 0
|
||||
r=0
|
||||
for bv in reversed(bcd):
|
||||
hi=(bv>>4)&0xF; lo=bv&0xF
|
||||
if hi==0xF: hi=0
|
||||
if lo==0xF: lo=0
|
||||
r=r*100+hi*10+lo
|
||||
return r
|
||||
|
||||
print("="*70)
|
||||
print("CHANNELS")
|
||||
for i in range(6):
|
||||
off = CH_BASE + i*48
|
||||
d = data[off:off+48]
|
||||
if all(b==0xFF for b in d):
|
||||
print(f"ch{i}: EMPTY"); continue
|
||||
rx = struct.unpack('<I', d[5:9])[0]
|
||||
tx = struct.unpack('<I', d[9:13])[0]
|
||||
name = bytes(b for b in d[0x20:0x30] if b!=0xFF).decode('gbk','ignore').strip()
|
||||
b0,b1,b2,b3,b4 = d[0],d[1],d[2],d[3],d[4]
|
||||
print(f"\nch{i} @0x{off:06X} name={name!r}")
|
||||
print(" raw:", hx(d))
|
||||
print(f" RX={rx/1e5:.5f} TX={tx/1e5:.5f}")
|
||||
print(f" b0=0x{b0:02X} mode={'ANALOG' if b0&0x40 else 'DIGITAL'} monitor={b0&1} ts={(b0>>1)&1} dmrmode={(b0>>2)&1} useChID={(b0>>3)&1} rxtx={(b0>>4)&3}")
|
||||
print(f" b1=0x{b1:02X} scramble={b1&0xF} colorcode={(b1>>4)&0xF}")
|
||||
print(f" b2=0x{b2:02X} tot={b2&0x3F} power={'HIGH' if b2&0x40 else 'LOW'}")
|
||||
print(f" b3=0x{b3:02X} tailtone={b3&7} anaBusyLock={(b3>>3)&3} dmrBusyLock={(b3>>5)&3} scan={'REMOVE' if b3&0x80 else 'ADD'}")
|
||||
print(f" b4=0x{b4:02X} ctdcs_sel={(b4>>1)&7} mod={(b4>>4)&3} bw={'Narrow' if (b4>>6)&1 else 'Wide'}")
|
||||
print(f" rx_tone={decode_subaudio_bytes(d[0x0D:0x0F])} tx_tone={decode_subaudio_bytes(d[0x0F:0x11])}")
|
||||
cslot=struct.unpack('<H',d[0x11:0x13])[0]
|
||||
print(f" contact_slot(0x11)={cslot} (->idx {0 if cslot==0xFFFF else cslot+1}) grouplist(0x13)={d[0x13]} encrypt(0x14)={struct.unpack('<H',d[0x14:0x16])[0]}")
|
||||
print(f" dmr_id(0x16 BCD)={parse_bcd(d[0x16:0x1A])} mute_code(0x1A)={struct.unpack('<I',d[0x1A:0x1E])[0]:08X}")
|
||||
print(f" bytes 0x1E-0x1F: {hx(d[0x1E:0x20])}")
|
||||
|
||||
print("="*70)
|
||||
print("CONTACTS")
|
||||
cnt=0
|
||||
for i in range(2048):
|
||||
off = CT_BASE + i*32
|
||||
d = data[off:off+32]
|
||||
if d[1] > 2: # empty per parser
|
||||
continue
|
||||
name = bytes(b for b in d[0x10:0x20] if b!=0xFF).decode('gbk','ignore').strip()
|
||||
dmr = parse_bcd(d[0x02:0x06])
|
||||
print(f"\nct{i} @0x{off:06X}")
|
||||
print(" raw:", hx(d))
|
||||
print(f" type(b1)={d[1]} name={name!r} dmr_id(BCD 0x2)={dmr}")
|
||||
print(f" b0=0x{d[0]:02X} bytes6-F: {hx(d[6:16])}")
|
||||
cnt+=1
|
||||
if cnt>=6: break
|
||||
|
||||
print("="*70)
|
||||
print("ZONES")
|
||||
zc=0
|
||||
for i in range(256):
|
||||
off = ZN_BASE + i*512
|
||||
d = data[off:off+512]
|
||||
if d[0]==0xFF: continue
|
||||
name = bytes(b for b in d[0x04:0x14] if b!=0xFF).decode('gbk','ignore').strip()
|
||||
count = d[0] | (d[1]<<8)
|
||||
print(f"\nzone{i} @0x{off:06X} name={name!r} count(b0-1)={count} curA(b2)={d[2]} curB(b3)={d[3]}")
|
||||
print(" head 0x00-0x13:", hx(d[0:0x14]))
|
||||
chans=[]
|
||||
for k in range(min(count,200)):
|
||||
o=0x14+k*2
|
||||
ci=d[o]|(d[o+1]<<8)
|
||||
chans.append(ci)
|
||||
print(" channel idx list:", chans[:30])
|
||||
scan=d[0x1A4:0x1A4+25]
|
||||
print(" scan bitmap @0x1A4:", hx(scan))
|
||||
zc+=1
|
||||
if zc>=4: break
|
||||
Ссылка в новой задаче
Block a user