# -*- coding: utf-8 -*-
"""Guarda um 05 00 e um 05 02 REAIS de monstro a golpear jogador, para servirem de molde.

    python tools/extrair_molde_golpe.py --aplicar

POR QUE ISTO EXISTE

Os nossos build_golpe e build_dano_no_jogador montavam o pacote a partir de um bytearray de
zeros e preenchiam so os campos decodificados. Comparando 600 pacotes do oficial com o que
escrevemos, sobram campos que ele preenche e nos deixamos vazios:

    05 00   [11:15]  [32]
    05 02   [11:15]  [21]  [28:32]

O [32] do 05 00 vem 0x01 em 412 de 600 amostras; o [11:15] do 05 02 vem 0x06000000 em 225.
Nao sei o que sao, e e justamente por isso que o molde e melhor do que continuar a adivinhar:
o que nao percebo fica com o valor que o servidor oficial mandou, em vez de zero.

E a mesma tecnica dos moldes de aparicao e das trilhas de movimento, que ja resolveram dois
problemas hoje. Um pacote gravado nao precisa de teoria.
"""
import collections
import glob
import os
import re
import struct
import sys

RAIZ = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(RAIZ, "server"))
import data_files as D                                              # noqa: E402

DESTINO = os.path.join(RAIZ, "captures")


def frames(cam):
    atual, blocos = None, []
    for l in open(cam, encoding="latin-1", errors="replace"):
        m = re.match(r"\[([\d:.]+)\] (SEND|RECV) sock=", l)
        if m:
            atual = [m.group(1), m.group(2), bytearray()]
            blocos.append(atual)
            continue
        if atual is not None and re.fullmatch(r"(?:[0-9A-Fa-f]{2} )+\s*", l):
            atual[2].extend(bytes.fromhex(l.strip()))
    for t, d, b in blocos:
        p = 0
        while p + 4 <= len(b):
            (n,) = struct.unpack_from(">I", b, p)
            if n == 0 or n > 16384 or p + 4 + n > len(b):
                break
            yield d, bytes(b[p + 4:p + 4 + n])
            p += 4 + n


def main() -> int:
    aplicar = "--aplicar" in sys.argv
    D.carregar()
    achados = {}
    for cam in sorted(glob.glob(r"C:\RoshHook\traffic*.log")):
        quem = {}
        eu = collections.Counter()
        pacotes = list(frames(cam))
        for d, c in pacotes:
            if d == "RECV" and c[:2] == bytes([4, 2]) and len(c) >= 74:
                quem[struct.unpack_from("<I", c, 8)[0]] = struct.unpack_from("<I", c, 70)[0]
            elif d == "SEND" and c[:2] == bytes([4, 1]) and len(c) == 42:
                eu[struct.unpack_from(">I", c, 8)[0]] += 1
        if not eu:
            continue
        EU = eu.most_common(1)[0][0]
        for d, c in pacotes:
            if d != "RECV":
                continue
            # 05 00 de MONSTRO contra MIM
            if (c[:2] == bytes([5, 0]) and len(c) == 55
                    and quem.get(struct.unpack_from(">I", c, 7)[0], 0) >= 1000
                    and struct.unpack_from(">I", c, 33)[0] == EU):
                achados.setdefault("golpe_05_00", c)
            # 05 02 de MONSTRO contra MIM
            elif (c[:2] == bytes([5, 2]) and len(c) == 51
                    and quem.get(struct.unpack_from(">I", c, 7)[0], 0) >= 1000
                    and struct.unpack_from("<H", c, 22)[0] == (EU & 0xFFFF)):
                achados.setdefault("dano_05_02", c)
    if not achados:
        print("nao achei nenhum golpe de monstro contra jogador nas capturas")
        return 1
    for nome, c in sorted(achados.items()):
        print("%-14s %d bytes  %s" % (nome, len(c), c[:24].hex(" ")))
        if aplicar:
            cam = os.path.join(DESTINO, "molde_%s.bin" % nome)
            open(cam, "wb").write(c)
            print("   gravado em %s" % cam)
    if not aplicar:
        print()
        print("nada gravado. rode com --aplicar")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
