#!/usr/bin/env python3
"""
方案B: Patch nm_mgr 调用后的 NULL 检查，让系统在 CAN 初始化失败时
继续执行后续初始化，而不是直接跳过。

Patch 目标:
  0x124E8: b118 (cbz r0, 0x124f2) -> bf00 (NOP)

原理:
  - nm_mgr init (0x125B4) 在 CAN 缺失时会失败，导致 [r5+0x79c] 为 NULL
  - 调用者 (0x124E0) 在 init 后检查该指针，为 NULL 则跳到 0x124F2 跳过关键初始化
  - 改为 NOP 后，程序继续执行 bl 0x2b8bc，该函数有 r0==0 保护会安全返回
  - 后续 0x124EE 会将该指针清零，0x124F4 继续执行其他初始化
"""

import subprocess
import time
import sys

JLINK_EXE = "/Applications/SEGGER/JLink_V942/JLinkExe"
DEVICE = "HC32A4A8xI"

# Patch: (addr, original, new, description)
PATCHES = [
    (0x124E8, 0xB118, 0xBF00, "nm_mgr调用后NULL检查: cbz -> NOP"),
]

def jlink_cmd(cmds: str, timeout=15):
    full = f"device {DEVICE}\nsi SWD\nspeed 4000\nconnect\nh\n{cmds}\ng\nq\n"
    result = subprocess.run(
        [JLINK_EXE],
        input=full,
        capture_output=True,
        text=True,
        timeout=timeout
    )
    return result.stdout + result.stderr

def read16(addr):
    out = jlink_cmd(f"mem16 0x{addr:08X},1")
    for line in out.splitlines():
        if line.startswith(f"{addr:08X} ="):
            val = int(line.split("=")[1].strip(), 16)
            return val
    return None

def write16(addr, val):
    out = jlink_cmd(f"w2 0x{addr:08X},0x{val:04X}")
    return out

def main():
    print("=" * 60)
    print("方案B: Patch nm_mgr 调用者 (0x124E8)")
    print("=" * 60)

    for addr, orig, new, desc in PATCHES:
        print(f"\n[+] {desc}")
        current = read16(addr)
        if current is None:
            print(f"    ERROR: 无法读取 0x{addr:08X}")
            sys.exit(1)
        print(f"    当前值: 0x{current:04X}")
        if current != orig:
            print(f"    WARNING: 期望值 0x{orig:04X}，实际 0x{current:04X}")
            if current == new:
                print(f"    已经是 patch 后的值，跳过")
                continue
            # 仍然继续 patch，因为可能固件版本不同
        print(f"    写入: 0x{new:04X}")
        out = write16(addr, new)
        if "error" in out.lower() or "fail" in out.lower():
            print(f"    ERROR: {out}")
            sys.exit(1)
        # 验证
        verify = read16(addr)
        print(f"    验证: 0x{verify:04X}")
        if verify != new:
            print(f"    ERROR: 验证失败!")
            sys.exit(1)
        print(f"    OK")

    print("\n" + "=" * 60)
    print("Patch 完成! MCU 继续运行...")
    print("=" * 60)

if __name__ == "__main__":
    main()
