#!/usr/bin/env python3 """Напряжение батареи вместо штатной иконки + уборка полосы у всплывающего окна. Патчи: 1. Свой код в свободном месте после конца образа (0x08029000): stub — пустышка `bx lr` batt_draw — рисует "8.4V" мелким шрифтом 5x8 на странице 0 tick_router — вызывает оригинал, затем batt_draw (только на главном экране) 2. `bl 0x08008300` @0x080148E2 — отрисовка штатной шкалы -> stub (иконки больше нет). 3. `bl 0x08010A00` @0x0801E2A4 — «напряжение -> индекс 0..3» убрано (2 x nop), чтобы флаг перерисовки взводился на каждые 0.1 В, а не только на порогах. 4. `bl 0x0800842C` @0x080085DE — верхняя полоса рамки всплывающего окна убрана. 5. `bl 0x0801DFD0` @0x08020806 — последний вызов цепочки отрисовки в ui_tick_normal перенаправлен на tick_router. Почему через конец цикла: раньше мы рисовали из середины кадра (из цепочки обновления батареи), и любой более поздний вывод затирал цифры — при входящем вызове «7» теряла перекладину и читалась как «1». Теперь наш текст ложится последним и перекрывает всё. """ import struct from keystone import Ks, KS_ARCH_ARM, KS_MODE_THUMB BASE = 0x08002800 SRC = "stock-fw/rt4d_ru_full.bin" OUT = "stock-fw/rt4d_ru_batt.bin" SMALL_CHAR = 0x080089AC # void small_char(u8 page, u8 x, u8 ch, u8 dim) BATT_VOLT = 0x200008B0 # u16, напряжение * 10 G_SCREEN = 0x200008B3 # u8, текущий экран (0 = главный) GAUGE_CALL = 0x080148E2 # bl 0x08008300 — штатная шкала батареи GAUGE_FUNC = 0x08008300 LEVEL_CALL = 0x0801E2A4 # bl 0x08010A00 — напряжение -> индекс LEVEL_FUNC = 0x08010A00 TOPBAR_CALL = 0x080085DE # bl fill_col_run(page=1) — верхняя полоса рамки FILL_RUN = 0x0800842C TICK_CALL = 0x08020806 # bl 0x0801DFD0 — последний вызов отрисовки за кадр TICK_FUNC = 0x0801DFD0 MYCODE = 0x08029000 X0, X1, X2, X3 = 108, 113, 118, 123 # позиции четырёх символов def thumb_bl(src: int, dst: int) -> bytes: off = dst - (src + 4) assert -(1 << 24) <= off < (1 << 24), "цель BL слишком далеко" off >>= 1 s = (off >> 23) & 1 i1 = (off >> 22) & 1 i2 = (off >> 21) & 1 imm10 = (off >> 11) & 0x3FF imm11 = off & 0x7FF j1 = (~i1 ^ s) & 1 j2 = (~i2 ^ s) & 1 return struct.pack("индекс") o_bar = check(TOPBAR_CALL, FILL_RUN, "верхняя полоса рамки") o_tick = check(TICK_CALL, TICK_FUNC, "хвост отрисовки") print("все четыре точки патча подтверждены") # ---- раскладка своего кода ---- stub_addr = MYCODE stub = bytes(ks.asm("bx lr", stub_addr)[0]) batt_addr = stub_addr + len(stub) batt_asm = f""" push {{r4, r5, r6, lr}} movw r4, #{BATT_VOLT & 0xFFFF} movt r4, #{(BATT_VOLT >> 16) & 0xFFFF} ldrh r4, [r4] uxtb r4, r4 movs r1, #205 muls r1, r4, r1 lsrs r5, r1, #11 movs r1, #10 muls r1, r5, r1 subs r4, r4, r1 movs r0, #0 movs r1, #{X0} mov r2, r5 add r2, #0x30 movs r3, #0 bl #{SMALL_CHAR} movs r0, #0 movs r1, #{X1} movs r2, #0x2E movs r3, #0 bl #{SMALL_CHAR} movs r0, #0 movs r1, #{X2} mov r2, r4 add r2, #0x30 movs r3, #0 bl #{SMALL_CHAR} movs r0, #0 movs r1, #{X3} movs r2, #0x56 movs r3, #0 bl #{SMALL_CHAR} pop {{r4, r5, r6, pc}} """ batt = bytes(ks.asm(batt_asm, batt_addr)[0]) # tick_router: оригинал -> проверка экрана -> batt_draw. # keystone не умеет метки, поэтому условный переход кодируем вручную. tick_addr = batt_addr + len(batt) partA = bytes(ks.asm(f""" push {{r4, lr}} bl #{TICK_FUNC} movw r4, #{G_SCREEN & 0xFFFF} movt r4, #{(G_SCREEN >> 16) & 0xFFFF} ldrb r4, [r4] cmp r4, #0 """, tick_addr)[0]) bne_addr = tick_addr + len(partA) partB = bytes(ks.asm(f"bl #{batt_addr}", bne_addr + 2)[0]) bne = struct.pack(" за partB partC = bytes(ks.asm("pop {r4, pc}", bne_addr + 2 + len(partB))[0]) tick = partA + bne + partB + partC code = stub + batt + tick print(f"свой код: {len(code)} Б (stub 0x{stub_addr:08X}, " f"batt_draw 0x{batt_addr:08X}, tick_router 0x{tick_addr:08X})") img += code while len(img) % 4: img.append(0xFF) # ---- патчи ---- img[o_gauge:o_gauge + 4] = thumb_bl(GAUGE_CALL, stub_addr) print("штатная шкала батареи отключена") img[o_level:o_level + 4] = b"\x00\xbf\x00\xbf" print("преобразование в индекс убрано — перерисовка на каждые 0.1 В") img[o_bar:o_bar + 4] = b"\x00\xbf\x00\xbf" print("верхняя полоса рамки убрана") img[o_tick:o_tick + 4] = thumb_bl(TICK_CALL, tick_addr) print("вывод напряжения перевешен на конец кадра") open(OUT, "wb").write(bytes(img)) print(f"готово: {OUT}, {len(img)} Б") if __name__ == "__main__": main()