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.fbino disco e o motor o prefere a qualquer coisa emtotala1.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
*.hpisão montados. O TA 3.1c montarev31.gp3, depois*.ccx,*.ufoe os dez primeiros*.hpique conseguir abrir, cada grupo na ordem ASCII dos nomes em maiúsculas. Um mod lançado comozz_mod.hpifica abaixo de todos os arquivos compactados da versão comercial e, numa instalação que já tenha dez*.hpique 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
DirectorySizebytes e descriptografar o diretório nele a partir do offsetStart, 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
BANKsã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
.gp3exige 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=armfunciona na maioria das consultas, mas quebra qualquer código de mod que compareSidecomo string exata. - Os valores de lista são separados por espaços
(
Category=COMMANDER MOBILE WEAPON), não por vírgulas. A exceção é onumplayers=2,4,6,8,10do OTA, que usa vírgulas. - Os booleanos são os inteiros
1/0, nãotrue/false. O motor só aceita a forma inteira. - Em mods, nunca edite
sidedata.tdfpara acrescentar unidades. Use um registro[MENUENTRY]emdownload/<UNITNAME>.tdf, o mesmo mecanismo que o Core Contingency usa. Veja TDF:[MENUENTRY]e o tutorial de modding. Bmcode=0esconde a unidade do menu de construção, mesmo que todo o resto esteja válido. Quase sempre você querBmcode=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
WidtheHeightdo 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 == 0NÃO quer dizer "sem água": quer dizer que o menor valor possível está no limite, então nada fica submerso. UseSeaLevel = 1para 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),0xFFFDe 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 em0x2cestá ligado. Veja TNT. - O
.tntdo TA: Kingdoms usaIDVersion == 0x4000em vez do0x2000do TA (o TA também lê uma estrutura mais antiga,0x1020). Okbot tnt imagegera 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ódigovisible = (texture != "") || (IsColored != 0)cobre os dois casos. É o bug mais comum na renderização de 3DO. OffsetToSelectionPrimsó 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 como0x00010000= 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ÃO0x10063000, como afirmam alguns documentos anteriores à 3.1. Descompiladores antigos que usam0x10063000produzem 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_MASKsó registra interesse; não mata nada. O que para os scripts cuja máscara temXligado é enviarsignal X. A regra de lintsignal-never-signalledpega scripts que definem uma máscara que ninguém sinaliza.- O Scriptor da Cavedog embute
CURRENT_SPEEDcomo o literal29, o que quebra scripts no estiloMoveWatcherdepois 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 0x4Cquer dizer "salte paraOffsetToScriptCode + 0x4C". - Os números de porta de
GET_UNIT_VALUEmudam 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/*.gafeanims/vismasks.gafprecisam 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.gafeanims/vismasks.gaftêmVersion == 0no cabeçalho. O jogo ignora a palavra de versão.- O
.tafdo TA: Kingdoms existe em dois tipos. Um.tafcom paleta É um GAF: mesmo formato no disco, só que o TAK tira a paleta de um.pcxpor lado, e não da paleta global do TA. Um.taftruecolor 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
.taftem dois significados: com paleta (lido como GAF) ou truecolor (lido comkbot 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
0x0Bpodem conter lixo. Preserve-os sem alteração para uma ida e volta byte a byte; não os normalize. Delay/Durationigual a0é 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 quandoBytesPerLine > Width; o TA 3.1c decodifica exatamenteWidthbytes por linha, então os arquivos com preenchimento se deslocam no jogo. GraveBytesPerLine == Widthe confira comkbot 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–0xFFnão é válido como literal; grave-o como uma sequência de contagem 1 (0xC1, 0xFFpara um único0xFF). 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.palvazio ou ausente é substituído porpalette.pcx(os seus últimos 768 bytes); um.palcom mais de 1.024 bytes é lido até o byte 1.024. .alp/.lht/.shdNÃO são paletas RGB: o.alpé uma tabela de 256×256 e o.shd/.lhtsão tabelas de 32×256 de índices da paleta, e o jogo ignora (e reconstrói) uma tabela de qualquer outro tamanho. Renderizá-los comkbot pal describemostra lixo; usekbot 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] == 0significa "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ó linhasweight/limitsoltas. O analisador do kbot abre um plandefaultimplí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 emCategory=. EscrevaLEVL3errado 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
.wavfica implícita nos valores do TDF: nunca a escreva.select1=cmdsel1.wavvirasounds/cmdsel1.wav.wave não acha nada.
Particularidades do TA: Kingdoms
- O TAK exige
-disablecavedogverificationcomo 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órioweapons/*.tdfseparado. - Os menus de construção do TAK são lineares, não 2×3. Cada construtor
tem um diretório
canbuild/<builder>/<unit>.tdfcom um único bloco[Menu] { Priority = N; }.
Veja também
- Tutorial de modding: passo a passo completo de um mod de unidade para o TA.
- Glossário: definições de todos os termos comuns aos formatos citados acima.
- Início rápido: cinco comandos do kbot para começar.