# -*- coding: utf-8 -*-
r"""Monta o ZIP de patch do client, para o jogador extrair na raiz da pasta do jogo.

    python tools/fazer_patch_client.py            -> patch_client/AxionPatch_AAAAMMDD.zip
    python tools/fazer_patch_client.py --ip 1.2.3.4 --porta 19999

O QUE VAI DENTRO, com a arvore ja feita:

    ResUnpak/Interface/IE_Lod/string.lod_pt                    "Kart" -> "Axion"
    ResUnpak/Interface/IE_Script/Contents/Contents_Enter.Sct   sem os selos NEW e as
                                                               pastilhas de regiao
    ServerList.ten                                             aponta para o NOSSO servidor
    LEIA-ME.txt

O `ServerList.ten` E GERADO, NAO COPIADO. O que esta na maquina do servidor aponta para
127.0.0.1, que so funciona ali; o do jogador precisa do IP publico. Cifra de subtracao
encadeada com seed 0x3042, a mesma do CheckFileListW.ten -- ver docs/findings.md.

NAO HA RISCO DE VERIFICACAO DE INTEGRIDADE: o `CheckFileListW.ten` cobre 760 ficheiros, todos
`EIDString`/`EIDMain` e afins dentro de IE_Lod. Nenhum dos tres ficheiros deste patch esta
la, conferido em 30/08.
"""
import argparse
import io
import os
import zipfile

RAIZ = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
CLIENT = r"C:\Games\Axion"
DESTINO = os.path.join(RAIZ, "patch_client")

# (caminho no client, caminho dentro do ZIP)
FICHEIROS = [
    (r"ResUnpak\Interface\IE_Lod\string.lod_pt",
     "ResUnpak/Interface/IE_Lod/string.lod_pt"),
    (r"ResUnpak\Interface\IE_Script\Contents\Contents_Enter.Sct",
     "ResUnpak/Interface/IE_Script/Contents/Contents_Enter.Sct"),
]

IP_PADRAO = "144.217.65.115"
PORTA_PADRAO = 19999


def cifrar_ten(texto: bytes, seed: int = 0x3042) -> bytes:
    """Inversa do decrypt_ten. Ver docs/findings.md, FUN_00720fba no client."""
    key = seed & 0xFF
    saida = bytearray(len(texto))
    for i, p in enumerate(texto):
        saida[i] = (p + key) & 0xFF
        sp = p if p < 128 else p - 256
        key = (key + sp) & 0xFF
    return bytes(saida)


def decifrar_ten(buf: bytes, seed: int = 0x3042) -> bytes:
    key = seed & 0xFF
    saida = bytearray(len(buf))
    for i, c in enumerate(buf):
        b = (c - key) & 0xFF
        saida[i] = b
        sb = b if b < 128 else b - 256
        key = (key + sb) & 0xFF
    return bytes(saida)


LEIAME = """PATCH DO CLIENT -- Axion

Extrair na PASTA RAIZ do jogo, aquela onde esta o Axn.exe.
Aceitar substituir os ficheiros quando o Windows perguntar.

O que muda:

  - o mundo na tela de entrada passa a chamar-se Axion
  - saem os selos "NEW" e as etiquetas de regiao, fica so uma linha
  - o jogo passa a ligar-se ao nosso servidor

Ficheiros alterados:

  ResUnpak/Interface/IE_Lod/string.lod_pt
  ResUnpak/Interface/IE_Script/Contents/Contents_Enter.Sct
  ServerList.ten

Nenhum deles esta na lista de verificacao de integridade do jogo, portanto o client
nao vai reclamar nem tentar descarregar os originais.

Se alguma coisa correr mal, e so voltar a instalar o client original e extrair de novo.
"""


def carimbo():
    import datetime
    return datetime.datetime.now().strftime("%Y%m%d")


def main() -> int:
    ap = argparse.ArgumentParser(description="Monta o ZIP de patch do client.")
    ap.add_argument("--ip", default=IP_PADRAO, help="IP do servidor de login")
    ap.add_argument("--porta", type=int, default=PORTA_PADRAO, help="porta do login")
    ap.add_argument("--client", default=CLIENT, help="pasta do client de onde copiar")
    args = ap.parse_args()

    os.makedirs(DESTINO, exist_ok=True)
    nome = os.path.join(DESTINO, "AxionPatch_%s.zip" % carimbo())

    # O ServerList.ten do jogador: IP publico, terminado como o original do jogo.
    texto = ("%s\r\n%d\r\n" % (args.ip, args.porta)).encode("ascii")
    servidor = cifrar_ten(texto)
    de_volta = decifrar_ten(servidor)
    if de_volta != texto:
        raise SystemExit("a cifra nao fecha: %r != %r" % (de_volta, texto))

    faltam = []
    with zipfile.ZipFile(nome, "w", zipfile.ZIP_DEFLATED, compresslevel=9) as z:
        for rel, dentro in FICHEIROS:
            cam = os.path.join(args.client, rel)
            if not os.path.exists(cam):
                faltam.append(rel)
                continue
            z.write(cam, dentro)
            print("  %-58s %.0f KB" % (dentro, os.path.getsize(cam) / 1024.0))
        z.writestr("ServerList.ten", servidor)
        print("  %-58s %d bytes  -> %s:%d" % ("ServerList.ten", len(servidor),
                                              args.ip, args.porta))
        z.writestr("LEIA-ME.txt", LEIAME.encode("utf-8"))
    if faltam:
        print("NAO ENCONTRADOS (o ZIP saiu sem eles): %s" % ", ".join(faltam))
    print()
    print("%s  (%.0f KB)" % (nome, os.path.getsize(nome) / 1024.0))
    return 1 if faltam else 0


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