"""Decifra e inspeciona uma tabela .lod do client.

Uso:
    python ler_lod.py npcInfo            -- resumo + primeiros registros
    python ler_lod.py npcInfo --buscar "Aranha pequena"
    python ler_lod.py --listar           -- todas as tabelas disponiveis

As 157 tabelas em ResUnpak/Interface/IE_Lod sao as "tabelas" do client: ele nao consulta
banco nenhum, le esses arquivos. A cifra foi quebrada em 22/08 (ver docs/findings.md).
"""
import argparse
import os
import re
import struct
import sys

sys.path.insert(0, r"D:\RoshEmulator\server")
import lod_crypto

BASE = r"C:\Games\Karos\ResUnpak\Interface\IE_Lod"


def tabelas():
    nomes = set()
    for f in os.listdir(BASE):
        if ".lod" in f and "backup" not in f and "decoded" not in f:
            nomes.add(f.split(".lod")[0])
    return sorted(nomes)


def abrir(nome: str) -> bytes:
    for suf in ("_pt", "_en", ""):
        cam = os.path.join(BASE, nome + ".lod" + suf)
        if os.path.exists(cam):
            return lod_crypto.decrypt_lod_stream(open(cam, "rb").read())
    raise SystemExit(f"nao achei a tabela '{nome}'")


def main() -> None:
    p = argparse.ArgumentParser()
    p.add_argument("tabela", nargs="?")
    p.add_argument("--listar", action="store_true")
    p.add_argument("--buscar")
    p.add_argument("--registro", type=int, help="tamanho do registro, se conhecido")
    a = p.parse_args()

    if a.listar or not a.tabela:
        for t in tabelas():
            print(" ", t)
        return

    dados = abrir(a.tabela)
    cab = struct.unpack_from("<I", dados, 0)[0]
    txts = re.findall(rb"[\x20-\x7e]{4,}", dados)
    print(f"{a.tabela}: {len(dados)} bytes decifrados")
    print(f"  primeiro u32 = {cab}"
          + ("  (parece um carimbo de data, nao contagem)" if cab > 20000000 else "  (contagem?)"))
    print(f"  {len(txts)} strings ASCII")
    print(f"  exemplos: {[t.decode('latin-1')[:30] for t in txts[:6]]}")

    if a.buscar:
        alvo = a.buscar.encode("latin-1")
        pos, achados = 0, []
        while True:
            pos = dados.find(alvo, pos)
            if pos < 0:
                break
            achados.append(pos)
            pos += 1
        print(f"  '{a.buscar}' em {len(achados)} offsets: {achados[:10]}")
        if len(achados) >= 2:
            passos = {achados[i+1] - achados[i] for i in range(len(achados)-1)}
            print(f"  distancias entre ocorrencias: {sorted(passos)[:5]}"
                  "   <- se for constante, e o tamanho do registro")


if __name__ == "__main__":
    main()
