# -*- coding: utf-8 -*-
r"""Troca os efeitos de refino nos modelos de arma em FORMATO DE TEXTO (`SET_17` e afins).

    python tools/brilho_modelo_texto.py DSM_SET_17_DUALSWORD Sword_1H
    python tools/brilho_modelo_texto.py DSM_SET_17_DUALSWORD Sword_1H --aplicar

Dois modelos deste client guardam a lista de encantamento em texto em vez de binario:

    #.EFs_SIZE          13
    #.EFFECTIDs        000 0xEBA24A56 DSM_DUALSWORD_11._EF
    ...

CADA NOME LEVA UM HASH AO LADO, e nao se pode trocar o nome sem o recalcular. O hash e o
DJB2: h = 5381; para cada caracter, h = h*33 + c, em 32 bits. Descoberto pela diferenca
entre entradas consecutivas -- entre `_11` e `_12` da sempre 0x121881, que e 33^4, o peso
do digito que muda. Conferido nos 6 pares (nome, hash) conhecidos dos dois ficheiros.

Nao se mexe no `#.EFs_SIZE`: escrevem-se tantos nomes quantos os que la estao, os ULTIMOS N
niveis ate ao 18.
"""
import argparse
import os
import re
import shutil

WEAPON = r"C:\Games\Axion\ResUnpak\Character\Weapon"
EFEITOS = r"C:\Games\Axion\ResUnpak\Character\Effect"
LINHA = re.compile(rb"(#\.EFFECTIDs\s+(\d+)\s+0x)([0-9A-Fa-f]{8})(\s+)([A-Za-z0-9_]+\._EF)")


def djb2(s: str) -> int:
    h = 5381
    for c in s:
        h = (h * 33 + ord(c)) & 0xFFFFFFFF
    return h


def main() -> int:
    ap = argparse.ArgumentParser(description="Efeitos de refino nos modelos em texto.")
    ap.add_argument("modelo")
    ap.add_argument("familia")
    ap.add_argument("--aplicar", action="store_true")
    args = ap.parse_args()

    cam = os.path.join(WEAPON, args.modelo + "._OB")
    d = open(cam, "rb").read()
    achados = list(LINHA.finditer(d))
    if not achados:
        print("este modelo nao tem lista em texto")
        return 1
    n = len(achados)
    print("%s: %d bytes, %d entradas em texto" % (args.modelo, len(d), n))

    niveis = list(range(6, 19))[-n:]
    novos = ["Enchant_%s_%02d._EF" % (args.familia, k) for k in niveis]
    faltam = [x for x in novos if not os.path.exists(os.path.join(EFEITOS, x))]
    if faltam:
        print("faltam efeitos: %s" % faltam)
        return 1

    saida, fim = b"", 0
    for m, novo in zip(achados, novos):
        h = djb2(novo)
        saida += d[fim:m.start()] + m.group(1) + (b"%08X" % h) + m.group(4) + novo.encode("latin-1")
        fim = m.end()
        print("   %-24s -> %-24s hash 0x%s -> 0x%08X"
              % (m.group(5).decode(), novo, m.group(3).decode().upper(), h))
    saida += d[fim:]
    print("ficheiro: %d -> %d bytes (EFs_SIZE intacto em %d)" % (len(d), len(saida), n))

    if not args.aplicar:
        print("nada gravado. junte --aplicar")
        return 0
    if not os.path.exists(cam + ".bak13"):
        shutil.copy2(cam, cam + ".bak13")
    open(cam, "wb").write(saida)
    conf = list(LINHA.finditer(open(cam, "rb").read()))
    print("gravado. releitura: %d entradas, primeira=%s" % (len(conf), conf[0].group(5).decode()))
    return 0


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