Todas as páginas de formatos ↓
Tutoriais e referência

Armadilhas comuns de modding e de leitura de arquivos

Fonte: coreprime/kbot @ 0ab97bf · Editar no GitHub

Um resumo de uma página com as armadilhas que mais pegam as pessoas, espalhadas pelos documentos de formatos do kbot. Se você está esbarrando num bug que não faz sentido, comece por aqui. Cada item leva à explicação mais detalhada na página do formato correspondente.

Tip

Procurando por "meu mod não carrega"? A regra do ponto e vírgula nos TDFs (#tdf--fbi--ota, primeiro item) explica mais relatos de "quebra silenciosa" do que todas as outras armadilhas juntas.


HPI / UFO / CCX / GP3

  • Arquivos soltos na raiz da instalação têm prioridade sobre qualquer arquivo compactado. Coloque units/MYUNIT.fbi no disco e o motor o prefere a qualquer coisa em totala1.hpi. O diagrama de camadas em hpi.md mostra a pilha de prioridades completa.
  • Entre os arquivos compactados, vence o primeiro montado, e só dez *.hpi são montados. O TA 3.1c monta rev31.gp3, depois *.ccx, *.ufo e os dez primeiros *.hpi que conseguir abrir, cada grupo na ordem ASCII dos nomes em maiúsculas. Um mod lançado como zz_mod.hpi fica abaixo de todos os arquivos compactados da versão comercial e, numa instalação que já tenha dez *.hpi que venham antes dele na ordem, só é montado pela varredura do disco, abaixo de tudo. Lance mods como .ufo.
  • Os ponteiros do HPI v1 são absolutos a partir do início do arquivo, não do início do diretório. Não subtraia nada. O truque do analisador canônico é alocar um buffer de DirectorySize bytes e descriptografar o diretório nele a partir do offset Start, para que os ponteiros indexem o buffer diretamente. Veja Árvore de diretórios.
  • A semente da cifra do HPI v1 avança com a posição no arquivo. Não dá para descriptografar um buffer isolado: é preciso saber de qual offset absoluto do arquivo o buffer veio. Veja Derivação da chave de criptografia.
  • Se DecryptKey == 0, não aplique a cifra XOR. Alguns arquivos compactados personalizados vêm em texto puro; aplicar a cifra mesmo assim embaralha o que já era legível.
  • O checksum dos chunks SQSH é calculado sobre os bytes criptografados. Confira antes de desembaralhar. Veja Chunks comprimidos (SQSH).
  • O TA: Kingdoms usa HPI v2 (Version == 0x00020000), com outra estrutura: sem XOR, um único chunk SQSH por arquivo. Leia a seção HPI v2 do TA: Kingdoms antes de tentar o analisador da v1 num arquivo compactado do TAK.
  • Os jogos salvos com marcador BANK são outro bicho: eles reaproveitam o envelope do HPI, mas o conteúdo é o estado serializado da partida, não um sistema de arquivos. O kbot não os decodifica.
  • O .gp3 exige o patch rev3.1. Por dentro continua sendo HPI, mas o TA sem o patch se recusa a montá-lo.

TDF / FBI / OTA

  • Todo valor precisa terminar com ;. Pontos e vírgulas faltando juntam valores de linhas diferentes sem aviso e produzem atributos totalmente errados. É a causa nº 1 de "minha unidade tem HP estranho / está sem descrição / com o custo errado".
  • As chaves não diferenciam maiúsculas de minúsculas, os valores sim. Side=arm funciona na maioria das consultas, mas quebra qualquer código de mod que compare Side como string exata.
  • Os valores de lista são separados por espaços (Category=COMMANDER MOBILE WEAPON), não por vírgulas. A exceção é o numplayers=2,4,6,8,10 do OTA, que usa vírgulas.
  • Os booleanos são os inteiros 1/0, não true/false. O motor só aceita a forma inteira.
  • Em mods, nunca edite sidedata.tdf para acrescentar unidades. Use um registro [MENUENTRY] em download/<UNITNAME>.tdf, o mesmo mecanismo que o Core Contingency usa. Veja TDF: [MENUENTRY] e o tutorial de modding.
  • Bmcode=0 esconde a unidade do menu de construção, mesmo que todo o resto esteja válido. Quase sempre você quer Bmcode=1.
  • Uma unidade que não aparece na lista de nenhum construtor não pode ser construída, mesmo que o FBI esteja válido. É o bug mais comum de "minha unidade nova existe mas não dá para construir". Veja o passo 5 do tutorial de modding.
  • A profundidade das seções aninhadas é convenção, não especificação. Algumas ferramentas presumem 2 níveis; os TDFs de armas chegam a 3 ([WEAPON][DAMAGE][category]).

TNT / SCT: terreno dos mapas

  • Width e Height do TNT são células de atributo de 16 px, não blocos nem pixels. Divida por 2 para ter o número de blocos; multiplique por 16 para ter as dimensões em pixels. Veja o cabeçalho do TNT.
  • SeaLevel == 0 NÃO quer dizer "sem água": quer dizer que o menor valor possível está no limite, então nada fica submerso. Use SeaLevel = 1 para mapas realmente secos.
  • As palavras de elemento do TNT só posicionam um elemento abaixo do número de entradas da tabela de elementos. 0xFFFC é vazio; 0xFFFE (Lava Run, AC02), 0xFFFD e qualquer outra palavra igual ou maior que o tamanho da tabela não posicionam nada, e a célula continua edificável.
  • O preenchimento do minimapa do TNT usa o índice de paleta 0x64, e a região do mapa é dimensionada a partir do próprio mapa (lado maior 252, com a proporção do mapa visível), e não encontrada procurando o preenchimento. O jogo só lê o minimapa guardado quando o bit 0 da palavra do cabeçalho em 0x2c está ligado. Veja TNT.
  • O .tnt do TA: Kingdoms usa IDVersion == 0x4000 em vez do 0x2000 do TA (o TA também lê uma estrutura mais antiga, 0x1020). O kbot tnt image gera lixo com TNTs do TAK; o decodificador de blocos é específico do TA. Veja Mapas do TA:K.
  • As seções SCT precisam ter dimensões múltiplas de 4 nos dois eixos para carregar no TAE. O formato no disco permite outros tamanhos, mas o editor os rejeita com "section misaligned".
  • O SCT V2 tem células de altura de 8 bytes, o V3 tem de 4 bytes. O kbot aceita os dois; os mods feitos com o editor só veem o V3.

3DO: modelos

  • O eixo para cima é Y, não Z. Importar um 3DO num motor com Z para cima (Blender, Unreal moderno) sem trocar os eixos deixa todas as unidades deitadas de lado.
  • Primitivas sem textura ficam invisíveis, a não ser que IsColored != 0: o pseudocódigo visible = (texture != "") || (IsColored != 0) cobre os dois casos. É o bug mais comum na renderização de 3DO.
  • OffsetToSelectionPrim só faz sentido no objeto raiz; os objetos filhos precisam defini-lo como -1 (0xFFFFFFFF).
  • A escala de ponto fixo é 1 / 65536. Um vértice gravado como 0x00010000 = 1,0 unidade do mundo; os 16 bits baixos são a parte fracionária.
  • Os vértices são locais ao objeto, não globais. Dois objetos podem ter arrays de vértices idênticos: é geometria espelhada, não dados compartilhados.

COB / BOS: scripts

  • O opcode call-script é 0x10062000, NÃO 0x10063000, como afirmam alguns documentos anteriores à 3.1. Descompiladores antigos que usam 0x10063000 produzem lixo; veja a referência de opcodes.
  • Os números de peça nos opcodes são índices na tabela de nomes de peças, não strings. O COB guarda os nomes num pool de strings; o runtime usa índices por velocidade.
  • SET_SIGNAL_MASK só registra interesse; não mata nada. O que para os scripts cuja máscara tem X ligado é enviar signal X. A regra de lint signal-never-signalled pega scripts que definem uma máscara que ninguém sinaliza.
  • O Scriptor da Cavedog embute CURRENT_SPEED como o literal 29, o que quebra scripts no estilo MoveWatcher depois de uma ida e volta. O descompilador do kbot restaura o nome simbólico; se você precisar fazer a ida e volta pelo Scriptor, use parênteses: (get CURRENT_SPEED) > 5.
  • Os offsets de salto são offsets em bytes relativos a OffsetToScriptCode, não à instrução atual. JUMP 0x4C quer dizer "salte para OffsetToScriptCode + 0x4C".
  • Os números de porta de GET_UNIT_VALUE mudam entre o TA e o TA:K. O catálogo do TA (o descompilador do kbot dá nome às portas 1–20) está documentado no Apêndice B.

GAF: animações de sprites

  • Uma sequência só repete em loop quando o byte baixo da sua palavra em +2 está ligado; com 0, ela toca uma vez e para. Todas as sequências originais guardam 1, então uma ferramenta que grava zero congela os elementos animados.
  • Os quadros em textures/*.gaf e anims/vismasks.gaf precisam ser brutos. O jogo os lê como arrays simples de pixels.
  • Algumas sequências vêm com duração de quadro 0; o jogo mostra esse quadro por um tick. Não as "corrija" para um valor padrão.
  • O índice 0 da paleta não é transparente por si só. Só a chave de um quadro bruto e os trechos pulados de um quadro comprimido são.
  • As linhas de pixels comprimidas podem ser mais curtas que Width. O jogo continua lendo os bytes seguintes; o kbot completa a linha com pixels transparentes e avisa. Nenhum arquivo original tem linhas assim.
  • As origens das camadas são hotspots: cada camada de um quadro composto é posicionada de modo que a sua origem caia na origem do quadro.
  • anims/terrain.gaf e anims/vismasks.gaf têm Version == 0 no cabeçalho. O jogo ignora a palavra de versão.
  • O .taf do TA: Kingdoms existe em dois tipos. Um .taf com paleta É um GAF: mesmo formato no disco, só que o TAK tira a paleta de um .pcx por lado, e não da paleta global do TA. Um .taf truecolor reaproveita o mesmo contêiner, mas guarda pixels ARGB de 16 bits e não precisa de paleta. Quem decide é o byte de formato do quadro (0/1 = com paleta, 4/5 = truecolor), não a extensão; veja TAF / TSF.

TAF / TSF: animações truecolor

  • O .taf tem dois significados: com paleta (lido como GAF) ou truecolor (lido com kbot taf). Confira sempre o byte de formato do quadro, nunca o nome do arquivo, embora a maioria dos arquivos truecolor do TAK traga por convenção um sufixo _4444 / _1555.
  • A exportação para GIF perde informação. O GIF só guarda um recorte de 1 bit e 255 cores, então o alfa suave do ARGB4444 vira bordas duras. Use APNG (kbot taf export --format apng) para manter o canal alfa real.
  • O campo de nome do quadro e o byte de flags em 0x0B podem conter lixo. Preserve-os sem alteração para uma ida e volta byte a byte; não os normalize.
  • Delay / Duration igual a 0 é válido: como no GAF, um quadro de zero ticks é controlado por eventos (os fundos estáticos de menu usam isso). Não o "corrija".

PCX: imagens

  • O TA ignora BytesPerLine. Os leitores padrão descartam o preenchimento quando BytesPerLine > Width; o TA 3.1c decodifica exatamente Width bytes por linha, então os arquivos com preenchimento se deslocam no jogo. Grave BytesPerLine == Width e confira com kbot pcx describe.
  • O TA só carrega a versão 5 e decodifica todo arquivo como dados de 8 bits e um único plano.
  • Os bytes literais com os dois bits mais altos ligados precisam ser escapados em RLE. Um byte solto de valor 0xC0–0xFF não é válido como literal; grave-o como uma sequência de contagem 1 (0xC1, 0xFF para um único 0xFF). Errar isso tira a decodificação de sincronia depois de uma linha.
  • O jogo toma os 768 bytes finais como paleta, com ou sem o marcador 0x0C; os leitores padrão precisam do marcador.

PAL / ALP / LHT / SHD: paletas e tabelas de consulta

  • O 4º byte de cada entrada NÃO é alfa. É um byte de preenchimento não usado, que a Cavedog sempre deixou em 0x00. As ferramentas de paleta de código aberto que o tratam como alfa se recusam a renderizar a paleta (alfa=0 → tudo transparente).
  • O índice 0 é preto, não transparente. Terreno, minimapas e fundos o desenham opaco; os sprites escolhem a sua própria chave de transparência.
  • Um palette.pal vazio ou ausente é substituído por palette.pcx (os seus últimos 768 bytes); um .pal com mais de 1.024 bytes é lido até o byte 1.024.
  • .alp / .lht / .shd NÃO são paletas RGB: o .alp é uma tabela de 256×256 e o .shd / .lht são tabelas de 32×256 de índices da paleta, e o jogo ignora (e reconstrói) uma tabela de qualquer outro tamanho. Renderizá-los com kbot pal describe mostra lixo; use kbot pal lookup.
  • A paleta do TA tem 13 trios RGB duplicados. Se o seu editor elimina duplicatas ao importar (alguns fazem isso), você perde entradas e a paleta vira, sem aviso, uma paleta de 243 entradas.

FNT: fontes bitmap

  • Offsets[c] == 0 significa "sem glifo", não "glifo no offset 0". O offset 0 cai no meio do cabeçalho; a Cavedog reaproveita o zero como valor sentinela.
  • A ordem dos bits é MSB primeiro dentro de cada byte. LSB primeiro é mais comum nas fontes bitmap modernas; não presuma.
  • O fluxo de bits é contínuo entre as linhas: não há preenchimento no fim de cada linha de varredura.

Perfis de IA

  • Os arquivos de IA do TA: Kingdoms não têm diretivas plan, só linhas weight/limit soltas. O analisador do kbot abre um plan default implícito; os leitores devem tolerar a falta do bloco. Veja TA:K: perfis sem plan.
  • Não há validação em lugar nenhum. Nomes de unidades escritos errado, valores de peso quebrados, linhas fora de um plan: tudo é ignorado sem aviso. O primeiro sinal de um perfil quebrado é "a IA se comporta de um jeito estranho", sem nenhuma mensagem no log.
  • Os nomes de categoria (PLANT, LEVEL3) só "existem" se pelo menos uma unidade os declarar em Category=. Escreva LEVL3 errado e a diretiva, sem aviso, não faz nada.

Sons

  • Um WAV ausente é silêncio, não erro. Escreva errado select1=cmsdel1 (letras trocadas) e o motor não toca nada: nenhum log, nenhuma alternativa.
  • A extensão .wav fica implícita nos valores do TDF: nunca a escreva. select1=cmdsel1.wav vira sounds/cmdsel1.wav.wav e não acha nada.

Particularidades do TA: Kingdoms

  • O TAK exige -disablecavedogverification como argumento de linha de comando para carregar a maioria dos arquivos de mods. Sem ele, o motor rejeita tudo cujo hash não esteja na sua lista de permitidos.
  • O TAK tem 8 códigos de lado nos dados, não 4: ARA / TAR / VER / ZON para os quatro lados jogáveis, mais CRE (Creon, Iron Plague), MON (monstros), LIF (vida selvagem) e NPC (personagens da campanha). A filtragem de listas de unidades precisa lidar com todos eles.
  • As armas do TAK ficam embutidas em cada FBI, como subseções [WEAPON1]–[WEAPON3], e não num diretório weapons/*.tdf separado.
  • Os menus de construção do TAK são lineares, não 2×3. Cada construtor tem um diretório canbuild/<builder>/<unit>.tdf com um único bloco [Menu] { Priority = N; }.

Veja também