# -*- coding: utf-8 -*-
r"""Extrai o skillData.lod do client -- a tabela de EFEITOS das skills.

    python tools/extrair_skilldata_lod.py  ->  server/Data/Skills/SkillEfeito.ini

DE ONDE VEM. C:\Games\Axion\ResUnpak\Interface\IE_Lod\skillData.lod_pt, decifrado com
`pular_zero=False` -- o mesmo modo do skillLevel, e nao o padrao. Ver o cabecalho de
tools/extrair_skills_lod.py para a razao.

FORMATO. Registo de 44 bytes de binario mais 3 cadeias com o comprimento a frente:

    @ 0  u32     id da skill        4146 valores distintos; 1289 batem com o skillLevel
    @ 4  u32     tipo do efeito     104 valores distintos, nenhum acima de 136
    @ 8  u32     ?                  so 58 registos em 4146 o tem preenchido
    @12  u32     ?                  so 64 registos
    @16  u32     sempre zero
    @20  u32     sempre zero
    @24  double  valor 1
    @32  double  valor 2
    @40  u32     ?                  um unico registo o tem preenchido
    +3 cadeias   listas de numeros separados por espaco, quase sempre "0"

ATENCAO AOS DOUBLES. Lidos como u32 de 4 em 4, o @28 e o @36 aparecem com centenas de
valores enormes (3230760960, 3227058176) e parecem campos proprios. Nao sao: sao as metades
altas dos doubles em @24 e @32. E a mesma armadilha do skillLevel, e a segunda vez que
aparece nestas tabelas.

O QUE A TABELA E. Nao sao os valores de combate da skill -- esses estao no skillLevel. Sao
os modificadores que ela aplica. Ve-se pelo cruzamento com a descricao:

    id 1105  tipo 28  valor  -5   "Maior protecao e chance de ataque"
    id 1106  tipo 28  valor -10   "Ataque 4 vezes com a espada..."
    id 1107  tipo 28  valor -15   "Defesa enfraquecida ao aumentar a forca de ataque"
    id 3105  tipo 27  valor2 -25  "Concentracao para aumentar a protecao, reduzindo..."

Tres niveis da mesma familia com -5, -10 e -15 numa descricao que fala em defesa
enfraquecida: o `tipo` e o atributo mexido e o valor e quanto.

OS TIPOS JA ESTAO IDENTIFICADOS -- este paragrafo dizia o contrario e estava desactualizado.
O `tipo` e o caso do switch FUN_006076cb do AMo.exe, lido no Ghidra e cruzado com o
string.lod: 98 casos com nome, em tools/importar_bonus_de_item.py (dicionario NOMES). Dai
saem "Saude", "Proteccao fisica", "Velocidade de movimento" e o bloco de PK nos casos 49-57.
Nao voltar a cacar isto a partir dos numeros.

NOTA: o "Golpe de ombro" (id 5001 no skillLevel) NAO esta nesta tabela -- so ca estao as
skills que aplicam modificadores. Por isso a area e os hits da dica do client continuam sem
sitio conhecido.
"""
import io
import os
import struct
import sys

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import lod_crypto as L                                              # noqa: E402

PASTA = r"C:\Games\Axion\ResUnpak\Interface\IE_Lod"
BIN = 44
N_CADEIAS = 3


def descricoes_do_skilllevel():
    """{id da skill: descricao}, para dar contexto a cada efeito."""
    p = os.path.join(PASTA, "skillLevel.lod_pt")
    if not os.path.isfile(p):
        return {}
    d = L.decrypt_lod_stream(open(p, "rb").read(), pular_zero=False)
    n = struct.unpack_from("<I", d, 4)[0]
    saida = {}
    q = 8
    for _ in range(n):
        if q + 284 > len(d):
            break
        sid = struct.unpack_from("<I", d, q)[0]
        q += 284
        for _k in range(18):
            ln = struct.unpack_from("<I", d, q)[0]
            q += 4 + ln
        ln = struct.unpack_from("<I", d, q)[0]
        desc = d[q + 4:q + 4 + ln].decode("latin-1")
        q += 4 + ln
        if sid not in saida and desc.strip():
            saida[sid] = desc
    return saida


def main() -> int:
    import re
    p = os.path.join(PASTA, "skillData.lod_pt")
    if not os.path.isfile(p):
        print("nao encontrei %s" % p)
        return 1
    d = L.decrypt_lod_stream(open(p, "rb").read(), pular_zero=False)
    n = struct.unpack_from("<I", d, 4)[0]

    def ler(q):
        ln = struct.unpack_from("<I", d, q)[0]
        return d[q + 4:q + 4 + ln].decode("latin-1"), q + 4 + ln

    linhas = []
    q = 8
    for _ in range(n):
        if q + BIN > len(d):
            break
        b = d[q:q + BIN]
        q += BIN
        cad = []
        for _k in range(N_CADEIAS):
            s, q = ler(q)
            cad.append(re.sub(r"[\x00-\x1f]+", " ", s).strip())
        v1 = struct.unpack_from("<d", b, 24)[0]
        v2 = struct.unpack_from("<d", b, 32)[0]
        linhas.append({
            "skill_id": struct.unpack_from("<I", b, 0)[0],
            "tipo": struct.unpack_from("<I", b, 4)[0],
            "campo8": struct.unpack_from("<I", b, 8)[0],
            "campo12": struct.unpack_from("<I", b, 12)[0],
            "valor1": 0.0 if v1 != v1 else v1,
            "valor2": 0.0 if v2 != v2 else v2,
            "lista1": cad[0], "lista2": cad[1], "lista3": cad[2],
        })
    print("registos lidos: %d de %d (consumiu %d de %d bytes)" % (len(linhas), n, q, len(d)))

    desc = descricoes_do_skilllevel()
    com = sum(1 for r in linhas if r["skill_id"] in desc)
    print("registos com descricao vinda do skillLevel: %d" % com)

    destino = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                           "..", "server", "Data", "Skills", "SkillEfeito.ini")
    nl, tb = chr(10), chr(9)
    with io.open(destino, "w", encoding="utf-8", newline="") as f:
        f.write("; SkillEfeito -- os modificadores que cada skill aplica, do skillData.lod." + nl)
        f.write("; Gerado por tools/extrair_skilldata_lod.py. Os valores de COMBATE (dano," + nl)
        f.write("; mana, recuperacao) nao estao aqui: estao no SkillLod.ini." + nl)
        f.write("; O significado dos 104 'tipo' ainda nao esta identificado." + nl)
        f.write(tb.join(["//skill_id", "tipo", "valor1", "valor2", "campo8",
                         "campo12", "lista1", "lista2", "lista3", "descricao"]) + nl)
        for r in linhas:
            f.write(tb.join([
                str(r["skill_id"]), str(r["tipo"]),
                "%g" % r["valor1"], "%g" % r["valor2"],
                str(r["campo8"]), str(r["campo12"]),
                r["lista1"], r["lista2"], r["lista3"],
                re.sub(r"[\x00-\x1f]+", " ", desc.get(r["skill_id"], "")).strip()[:90],
            ]) + nl)
    print("escrito em %s" % os.path.normpath(destino))
    return 0


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