#!/usr/bin/env python3
"""Поймать загрузчик модуля (HKLFBOOT) — ТАЙТОВЫЙ спам Connect (как DMR Upgrade Tool).

DMR Upgrade Tool: жмёшь Upgrade (спам Connect=01 E0 FC 01 00), затем MENU+включение —
загрузчик ловит connect в окне до старта приложения. Наш powershell-цикл со Start-Sleep был
слишком медленный и промахивался. Здесь — спам БЕЗ пауз (тысячи/сек), ловит короткое окно.

Процедура: запусти тул, затем зажми MENU и включи рацию. Успех = ответ загрузчика на link-check
(04 0E 05 ...) и приложение (0x68 06 02) НЕ стартует. Только connect — ни erase, ни write.
"""
import argparse, subprocess

PS = r'''
$p = New-Object System.IO.Ports.SerialPort "{port}",115200,"None",8,"one"
$p.ReadTimeout=1; $p.WriteTimeout=50; $p.ReadBufferSize=8192
try {{ $p.Open() }} catch {{ Write-Output "ERR:$($_.Exception.Message)"; exit }}
$lc=[byte[]]@(1,224,252,1,0)
$sw=[Diagnostics.Stopwatch]::StartNew()
$acc=New-Object System.Collections.Generic.List[byte]
$caught=$false
while($sw.Elapsed.TotalSeconds -lt {sec}){{
  if(-not $caught){{ try {{ $p.Write($lc,0,$lc.Length) }} catch {{}} }}
  $n=$p.BytesToRead
  if($n -gt 0){{
    $b=New-Object byte[] $n; $p.Read($b,0,$n)|Out-Null
    foreach($x in $b){{ $acc.Add($x) }}
    $hex=($b|ForEach-Object{{"{{0:x2}}"-f $_}}) -join ""
    Write-Output ("t"+[int]$sw.Elapsed.TotalSeconds+"|"+$hex)
    # ответ загрузчика на link-check: 04 0e 05 ...
    if($hex -match "040e05"){{ Write-Output "CAUGHT"; $caught=$true }}
  }}
}}
$p.Close(); Write-Output "END"
'''

def main():
    ap=argparse.ArgumentParser()
    ap.add_argument("--port", default="COM4"); ap.add_argument("--sec", type=int, default=30)
    a=ap.parse_args()
    print(f"[ТАЙТОВЫЙ спам 01 e0 fc 01 00 на {a.port}, {a.sec}с]")
    print(">>> ЗАЖМИ MENU и включи рацию СЕЙЧАС (как при прошивке модуля) <<<\n")
    r=subprocess.run(["powershell.exe","-NoProfile","-Command",PS.format(port=a.port,sec=a.sec)],
                     capture_output=True, text=True)
    banner=app=caught=False; n=0
    for line in r.stdout.replace("\r","").split("\n"):
        line=line.strip()
        if line.startswith("ERR:"): print(line); return
        if line=="CAUGHT": caught=True; continue
        if "|" not in line: continue
        t,hexs=line.split("|",1); n+=1
        try: asc=bytes.fromhex(hexs).decode("latin1")
        except: asc=""
        asc="".join(c if 32<=ord(c)<127 else "." for c in asc)
        print(f"  {t}: {asc[:48]}  [{hexs[:40]}]")
        if "HKLF" in asc: banner=True
        if "680602" in hexs: app=True
    print()
    if caught: print("✅✅✅ ЗАГРУЗЧИК ПОЙМАН (ответ 04 0e 05)! Держим сессию — можно читать флеш.")
    elif banner and app: print("⚠️ Баннер+приложение стартовало. Окно ещё промахнули — повтори (спам уже без пауз).")
    elif banner: print("✅ Баннер был, приложение не стартовало — возможно, поймали. Проверю ответ.")
    elif not n: print("Тишина — не попал во включение? Порт COM4? Повтори.")
    else: print("Данные без баннера — перезагрузка вне окна. Повтори.")

if __name__=="__main__":
    main()
