"""Extrai a criacao de personagem de uma captura do hook.

    python analisar_criacao.py [caminho_do_traffic.log]

Procura o CLoginCreateCharPacket (02 02) que o client manda e mostra tudo em volta: a lista de
personagens antes (vazia), o pacote de criacao, a resposta do servidor e a lista depois
(populada). E o material que falta pra implementar criacao de personagem no emulador.
"""
import re
import struct
import sys

PADRAO = r"C:\RoshHook\traffic.log"

INTERESSE = {
    (2, 1): "CLoginCharViewInfoPacket (lista de personagens)",
    (2, 2): "CLoginCreateCharPacket  (CRIAR PERSONAGEM)",
    (2, 3): "CLoginDeleteCharPacket",
    (2, 4): "CLoginStartCharPacket",
    (2, 5): "CLoginCharInfoPacket    (personagem completo)",
    (2, 11): "CLoginSecondPasswdCheck",
    (1, 19): "CFailPacket",
}


def blocos(caminho):
    cab = re.compile(r"^\[(\d\d:\d\d:\d\d\.\d+)\] (SEND|RECV) sock=(\S+) len=(\d+)")
    atual, dados = None, []
    for linha in open(caminho, "r", errors="replace"):
        m = cab.match(linha)
        if m:
            if atual:
                yield atual + (bytes(dados),)
            atual, dados = (m.group(1), m.group(2)), []
        elif atual is not None:
            toks = linha.split()
            if toks and all(re.fullmatch(r"[0-9A-Fa-f]{2}", t) for t in toks):
                dados.extend(int(t, 16) for t in toks)
    if atual:
        yield atual + (bytes(dados),)


def frames(buf):
    i = 0
    while i + 4 <= len(buf):
        n = struct.unpack_from(">I", buf, i)[0]
        if n == 0 or n > 200000 or i + 4 + n > len(buf):
            return
        yield buf[i + 4:i + 4 + n]
        i += 4 + n


def despejar(f, limite=256):
    for j in range(0, min(len(f), limite), 16):
        p = f[j:j + 16]
        txt = "".join(chr(c) if 32 <= c < 127 else "." for c in p)
        print(f"    {j:4d}: {p.hex(' '):48s} |{txt}|")
    if len(f) > limite:
        print(f"    ... (+{len(f) - limite} bytes)")


def nome_utf32(f):
    """Tenta ler o nome no fim do pacote: contagem LE u32 + caracteres UTF-32LE."""
    for base in range(max(0, len(f) - 80), len(f) - 8):
        n = struct.unpack_from("<I", f, base)[0]
        if 1 <= n <= 16 and base + 4 + n * 4 <= len(f):
            bruto = f[base + 4:base + 4 + n * 4]
            try:
                s = bruto.decode("utf-32-le")
            except UnicodeDecodeError:
                continue
            if s.isprintable() and s.strip():
                return s, base
    return None, None


def main():
    caminho = sys.argv[1] if len(sys.argv) > 1 else PADRAO
    todos = []
    for ts, direcao, buf in blocos(caminho):
        for f in frames(buf):
            todos.append((ts, direcao, f))
    print(f"{caminho}: {len(todos)} frames\n")

    criacoes = [i for i, (_, d, f) in enumerate(todos)
                if d == "SEND" and len(f) >= 2 and f[0] == 2 and f[1] == 2]
    if not criacoes:
        print("NENHUM 02 02 encontrado -- o personagem nao chegou a ser criado nesta captura?")
        print("Opcodes 02 xx vistos:")
        vistos = {}
        for ts, d, f in todos:
            if len(f) >= 2 and f[0] == 2:
                vistos.setdefault((d, f[1]), 0)
                vistos[(d, f[1])] += 1
        for (d, op), n in sorted(vistos.items()):
            print(f"   {d}  02 {op:02x}  x{n}   {INTERESSE.get((2, op), '')}")
        return

    for idx in criacoes:
        print("=" * 78)
        print(f"CRIACAO DE PERSONAGEM no frame {idx}")
        print("=" * 78)
        for i in range(max(0, idx - 6), min(len(todos), idx + 10)):
            ts, d, f = todos[i]
            if len(f) < 2:
                continue
            rot = INTERESSE.get((f[0], f[1]), "")
            marca = "  <<<<<<" if i == idx else ""
            print(f"\n[{i:5d}] {ts} {d} {f[:2].hex(' ')} len={len(f)}  {rot}{marca}")
            if f[0] == 2 and f[1] in (1, 2, 5):
                despejar(f, 256 if i == idx else 96)
                nome, base = nome_utf32(f)
                if nome:
                    print(f"    -> nome encontrado em [{base}]: '{nome}'")


if __name__ == "__main__":
    main()
