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
co-authored by Claude Opus 4.8
Коммит ae36c3b729
72 изменённых файлов: 24124 добавлений и 0 удалений
+42
Просмотреть файл
@@ -0,0 +1,42 @@
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()
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
# Are 0x1C000 blocks packed 48B channels identical to main channels 0x4000?
print("Compare 0x1C000 first 96 bytes vs main channels 0x4000:")
print(" 0x1C000:", hx(data[0x1C000:0x1C000+96]))
print(" 0x04000:", hx(data[0x04000:0x04000+96]))
print(" match:", data[0x1C000:0x1C000+96]==data[0x04000:0x04000+96])
# Contacts - the two records
print("\nCONTACT records decode:")
for off in (0x05E000,0x05E020):
d=data[off:off+32]
print(f"@0x{off:06X}:", hx(d))
print(f" b0={d[0]:02X} b1(type)={d[1]:02X} dmr_id(BCD@2)={parse_bcd(d[2:6])} raw_le@2={struct.unpack('<I',d[2:6])[0]}")
print(f" name@0x10:", bytes(b for b in d[0x10:0x20] if b!=0xFF).decode('gbk','ignore'))
print(f" bytes 6-F:", hx(d[6:16]))
# The ct256 record: b1=0xAA (=170) type not <=2, name shows 'All Call'? re-decode name area
d=data[0x05E000:0x05E000+32]
print("\nct256 name @0x05..0x0D area (looks like 'All Call' at 0x05):", bytes(b for b in d[0x05:0x0D]).decode('latin1'))
# contact ct257 @0x5E020: name at 0x0F='TG366'? decode 0x0D..
d=data[0x05E020:0x05E020+32]
print("ct257 full decode: b0-F=",hx(d[:16]), " tail=", bytes(b for b in d[0x0B:0x14] if b!=0xFF).decode('latin1','ignore'))
# scan whole contacts region for the real contact layout - look at more contact entries near 0x5E000
print("\nHex dump 0x05E000..0x05E080:")
for r in range(0,0x80,16):
print(f" +{r:04X}:", hx(data[0x05E000+r:0x05E000+r+16]))