Усі сторінки форматів ↓
Посібники й довідка

Поширені підводні камені моддингу й розбору

Джерело: coreprime/kbot @ 0ab97bf · Редагувати на GitHub

Шпаргалка на одну сторінку з підводними каменями, на які найчастіше натрапляють і які розкидані по документації форматів kbot. Якщо ви зіткнулися з помилкою, яку неможливо пояснити, почніть звідси. Кожен пункт посилається на докладніше пояснення на сторінці відповідного формату.

Tip

Шукаєте, чому «мій мод не завантажується»? Правило крапки з комою в TDF (#tdf--fbi--ota, перший пункт) відповідає за більше повідомлень про «тиху поломку», ніж усі інші підводні камені разом узяті.


HPI / UFO / CCX / GP3

  • Файли поза архівами в корені інсталяції перевизначають будь-який архів. Покладіть на диск units/MYUNIT.fbi, і рушій віддасть йому перевагу над будь-чим у totala1.hpi. Повний стек пріоритетів показано на схемі нашарування в hpi.md.
  • Серед архівів перемагає той, що змонтований першим, і монтується лише десять *.hpi. TA 3.1c монтує rev31.gp3, далі *.ccx, *.ufo і перші десять *.hpi, які вдалося відкрити, — кожну групу в порядку імен ASCII у верхньому регістрі. Мод, що постачається як zz_mod.hpi, має нижчий пріоритет за кожен релізний архів, а в інсталяції, де вже є десять *.hpi, що сортуються перед ним, він монтується лише через сканування диска, нижче за все інше. Постачайте моди як .ufo.
  • Вказівники HPI v1 відраховуються від початку файлу, а не від початку каталогу. Нічого не віднімайте. Канонічний прийом парсерів — виділити буфер на DirectorySize байтів і розшифрувати каталог у нього за зміщенням Start, щоб вказівники індексували буфер безпосередньо. Див. Дерево каталогів.
  • Початкове значення (seed) шифру HPI v1 зростає разом із позицією у файлі. Розшифрувати буфер окремо від файлу неможливо — потрібно знати, з якого абсолютного зміщення у файлі цей буфер узято. Див. Виведення ключа шифру.
  • Якщо DecryptKey == 0, XOR-шифр узагалі не застосовуйте. Деякі саморобні архіви постачаються у відкритому вигляді; якщо все одно застосувати шифр, він перемішає те, що й так можна прочитати.
  • Контрольна сума фрагмента SQSH обчислюється над зашифрованими байтами. Перевіряйте її до розшифрування. Див. Стиснені фрагменти (SQSH).
  • TA: Kingdoms використовує HPI v2 (Version == 0x00020000) з іншою структурою — без XOR, по одному фрагменту SQSH на файл. Перш ніж пробувати парсер v1 на архіві TAK, прочитайте розділ HPI v2 у TA: Kingdoms.
  • Збереження гри з маркером BANK — зовсім інша річ: вони повторно використовують оболонку HPI, але всередині — серіалізований стан гри, а не файлова система. kbot їх не декодує.
  • .gp3 потребує патча rev3.1. Усередині це все той самий HPI, але TA без патча відмовляється його монтувати.

TDF / FBI / OTA

  • Кожне значення має закінчуватися на ;. Пропущена крапка з комою мовчки зливає значення з кількох рядків і дає геть неправильні характеристики. Це причина № 1 скарг на кшталт «у мого юніта дивні HP / немає опису / неправильна вартість».
  • Ключі нечутливі до регістру, а значення — чутливі. Side=arm працює для більшості пошуків, але ламає будь-який код мода, що порівнює Side як рядки на рівність.
  • Значення-списки розділяються пробілами (Category=COMMANDER MOBILE WEAPON), а не комами. Виняток — numplayers=2,4,6,8,10 в OTA, де використовуються коми.
  • Булеві значення — цілі числа 1/0, а не true/false. Рушій приймає лише цілочислові форми.
  • У модах ніколи не редагуйте sidedata.tdf, щоб додати юніти. Натомість реєструйте їх через [MENUENTRY] у download/<UNITNAME>.tdf — тим самим механізмом, яким користується Core Contingency. Див. TDF: [MENUENTRY] і посібник із моддингу.
  • Bmcode=0 ховає юніт із меню будівництва, навіть якщо все інше правильно. Майже завжди вам потрібен Bmcode=1.
  • Юніт, якого немає в списку жодного будівельника, побудувати неможливо, навіть якщо його FBI в усьому іншому правильний. Це найпоширеніша помилка типу «мій новий юніт існує, але його не можна збудувати». Див. крок 5 посібника з моддингу.
  • Глибина вкладених секцій — це домовленість, а не специфікація. Деякі інструменти розраховують на 2 рівні; TDF зброї мають 3 рівні ([WEAPON][DAMAGE][category]).

TNT / SCT — рельєф мапи

  • Width і Height у TNT задано в 16-піксельних клітинках атрибутів, а не в тайлах і не в пікселях. Поділіть на 2, щоб отримати кількість тайлів; помножте на 16, щоб отримати розміри в пікселях. Див. Заголовок TNT.
  • SeaLevel == 0 НЕ означає «води немає» — це означає, що поріг збігається з найменшим можливим значенням, тож під водою немає нічого. Для справді сухих мап використовуйте SeaLevel = 1.
  • Слова об'єктів у TNT розміщують об'єкт лише тоді, коли вони менші за кількість записів у таблиці об'єктів. 0xFFFC — порожня (void) клітинка; 0xFFFE (Lava Run, AC02), 0xFFFD і будь-яке інше слово, що дорівнює розміру таблиці чи перевищує його, нічого не розміщують, і на клітинці можна будувати.
  • Заповнення мінімапи TNT використовує індекс палітри 0x64, а розмір області мапи визначається з розміру самої мапи (довша сторона 252, пропорції видимої мапи), а не шляхом пошуку заповнення. Гра читає збережену мінімапу лише тоді, коли встановлено біт 0 слова заголовка за зміщенням 0x2c. Див. TNT.
  • .tnt у TA: Kingdoms використовує IDVersion == 0x4000 замість 0x2000 у TA (TA також читає старішу структуру 0x1020). kbot tnt image видає сміття на TNT із TAK; декодер тайлів специфічний для TA. Див. Мапи TA:K.
  • Секції SCT мають бути кратні 4 за обома вимірами, щоб завантажитися в TAE. Формат на диску допускає й інші розміри, але редактор відхиляє їх із повідомленням «section misaligned».
  • SCT V2 має 8-байтові клітинки висот, V3 — 4-байтові. kbot приймає обидві версії; моди, зібрані для редактора, бачать лише V3.

3DO — моделі

  • Вгору спрямована вісь Y, а не Z. Якщо імпортувати 3DO в рушій, де вгору спрямована Z (Blender, сучасний Unreal), не помінявши осі, кожен юніт лежатиме на боці.
  • Примітиви без текстури невидимі, якщо не виконується IsColored != 0 — псевдокод visible = (texture != "") || (IsColored != 0) охоплює обидва випадки. Це найпоширеніша помилка рендерингу 3DO.
  • OffsetToSelectionPrim має сенс лише в кореневому об'єкті; у дочірніх об'єктах там має бути -1 (0xFFFFFFFF).
  • Масштаб чисел із фіксованою комою — 1 / 65536. Вершина, записана як 0x00010000, = 1,0 світової одиниці; молодші 16 бітів — дробова частина.
  • Вершини локальні для об'єкта, а не глобальні. Два об'єкти можуть мати однакові масиви вершин — це дзеркальна геометрія, а не спільні дані.

COB / BOS — скрипти

  • Опкод call-script — це 0x10062000, а НЕ 0x10063000, як стверджують деякі документи часів до 3.1. Старі декомпілятори, що використовують 0x10063000, видають нісенітницю — див. довідник опкодів.
  • Номери частин в опкодах — це індекси в таблиці імен частин, а не рядки. COB зберігає імена як пул рядків; під час виконання заради швидкості використовуються індекси.
  • SET_SIGNAL_MASK лише реєструє зацікавленість — він нічого не зупиняє. Скрипти, у масці яких встановлено X, зупиняє саме надсилання signal X. Правило лінтера signal-never-signalled виявляє скрипти, що встановлюють маску, якій ніхто ніколи не надсилає сигналу.
  • Scriptor від Cavedog вбудовує CURRENT_SPEED як літерал 29, через що скрипти в стилі MoveWatcher після round-trip ламаються. Декомпілятор kbot відновлює символьне ім'я; якщо вам доводиться робити round-trip через Scriptor, беріть вираз у дужки: (get CURRENT_SPEED) > 5.
  • Зміщення переходів — це зміщення в байтах відносно OffsetToScriptCode, а не відносно поточної інструкції. JUMP 0x4C означає «перейти до OffsetToScriptCode + 0x4C».
  • Номери портів GET_UNIT_VALUE у TA і TA:K різняться. Каталог TA (декомпілятор kbot підписує порти 1–20) задокументовано в Додатку B.

GAF — анімації спрайтів

  • Послідовність зациклюється лише тоді, коли встановлено молодший байт її слова за +2; якщо там 0, вона відтворюється один раз і зупиняється. У кожній стандартній послідовності там записано 1, тож інструмент, який записує нуль, заморожує анімовані об'єкти.
  • Кадри в textures/*.gaf і anims/vismasks.gaf мають бути без стиснення. Гра читає їх як звичайні масиви пікселів.
  • Деякі послідовності постачаються з тривалістю кадрів 0; гра показує такий кадр один тік. Не «виправляйте» їх на значення за замовчуванням.
  • Індекс палітри 0 сам по собі не прозорий. Прозорі лише ключовий колір кадру без стиснення й серії пропуску в стисненому кадрі.
  • Стиснені рядки пікселів можуть бути коротші за Width. Гра читає далі наступні байти; kbot доповнює рядок прозорими пікселями й видає попередження. У стандартних файлах таких рядків немає.
  • Точки прив'язки шарів — це гарячі точки: кожен шар складеного кадру розміщується так, щоб його точка прив'язки збіглася з точкою прив'язки кадру.
  • anims/terrain.gaf і anims/vismasks.gaf мають у заголовках Version == 0. Гра ігнорує слово версії.
  • .taf у TA: Kingdoms буває двох видів. Палітровий .taf — це і є GAF: той самий формат на диску, тільки TAK бере палітру з .pcx відповідної сторони, а не з глобальної палітри TA. Повнокольоровий .taf використовує той самий контейнер, але зберігає 16-бітні пікселі ARGB і палітри не потребує. Вирішальним є байт формату кадру (0/1 = палітровий, 4/5 = повнокольоровий), а не розширення — див. TAF / TSF.

TAF / TSF — повнокольорові анімації

  • .taf позначає два різні формати: палітровий (читається як GAF) або повнокольоровий (читається через kbot taf). Завжди перевіряйте байт формату кадру, а не ім'я файлу, хоча більшість повнокольорових файлів TAK за домовленістю мають суфікс _4444 / _1555.
  • Експорт у GIF іде із втратами. GIF вміщує лише 1-бітну маску вирізання й 255 кольорів, тож м'яка прозорість ARGB4444 перетворюється на чіткі краї. Щоб зберегти справжній альфа-канал, використовуйте APNG (kbot taf export --format apng).
  • Поле імені кадру й байт прапорця 0x0B можуть містити сміття. Зберігайте їх без змін для round-trip, точного до байта; не нормалізуйте.
  • Delay / Duration, рівні 0, — допустимі: як і в GAF, кадр із нулем тіків керується подіями (так зроблено статичні фони меню). Не «виправляйте» це.

PCX — зображення

  • TA ігнорує BytesPerLine. Стандартні читачі відкидають доповнення, коли BytesPerLine > Width; TA 3.1c декодує рівно Width байтів на рядок, тож у грі файли з доповненням зсуваються. Записуйте BytesPerLine == Width і перевіряйте через kbot pcx describe.
  • TA завантажує лише версію 5 і декодує кожен файл як 8-бітні одноплощинні дані.
  • Байти-літерали з двома встановленими старшими бітами мають екрануватися через RLE. Голий байт зі значенням 0xC0–0xFF як літерал неприпустимий; записуйте його як серію довжиною 1 (0xC1, 0xFF для одного 0xFF). Неправильна обробка розсинхронізує декодування вже після одного рядка.
  • Гра бере останні 768 байтів як палітру незалежно від того, чи є маркер 0x0C; стандартним читачам маркер потрібен.

PAL / ALP / LHT / SHD — палітри й таблиці пошуку

  • 4-й байт кожного запису — НЕ альфа. Це невикористовуваний байт доповнення, який Cavedog завжди встановлювала в 0x00. Інструменти з відкритим кодом для палітр, що вважають його альфою, не можуть відрендерити палітру (alpha=0 → усе прозоре).
  • Індекс 0 — чорний, а не прозорий. Рельєф, мінімапи й фони малюють його непрозорим; спрайти обирають власний ключ прозорості.
  • Порожній або відсутній palette.pal замінюється на palette.pcx (його останні 768 байтів); .pal, довший за 1 024 байти, читається до байта 1 024.
  • .alp / .lht / .shd — НЕ RGB-палітри: .alp — це таблиця 256×256, а .shd / .lht — таблиці 32×256 з індексів палітри, і таблицю будь-якого іншого розміру гра ігнорує (і будує заново). Якщо рендерити їх через kbot pal describe, вийде сміття; натомість використовуйте kbot pal lookup.
  • Палітра TA містить 13 повторюваних RGB-трійок. Якщо ваш редактор під час імпорту прибирає дублікати (деякі так роблять), ви втратите записи, і палітра мовчки скоротиться до 243 записів.

FNT — растрові шрифти

  • Offsets[c] == 0 означає «гліфа немає», а не «гліф за зміщенням 0». Зміщення 0 потрапляє в середину заголовка — Cavedog використовує нуль як сторожове значення.
  • Біти в кожному байті йдуть від старшого (MSB first). У сучасних растрових шрифтах частіше трапляється порядок від молодшого (LSB first); не робіть припущень.
  • Бітовий потік між рядками неперервний — у кінці кожного рядка розгортки доповнення немає.

Профілі ШІ

  • Файли ШІ в TA: Kingdoms не мають директив plan — лише голі рядки weight/limit. Парсер kbot відкриває неявний план default; читачі мають спокійно сприймати відсутність цього блоку. Див. TA:K — профілі без plan.
  • Жодної перевірки. Неправильно написані імена юнітів, зіпсовані значення ваг, рядки поза plan — усе мовчки ігнорується. Перша ознака зіпсованого профілю — «ШІ поводиться дивно», і жодного повідомлення в журналі.
  • Псевдоніми категорій (PLANT, LEVEL3) «існують», лише якщо принаймні один юніт оголошує їх у Category=. Напишіть із помилкою LEVL3 — і директива мовчки нічого не робитиме.

Звуки

  • Відсутній WAV — це тиша, а не помилка. Напишіть із помилкою select1=cmsdel1 (переставлені літери), і рушій нічого не відтворить — ні запису в журналі, ні запасного варіанта.
  • Розширення .wav у значеннях TDF додається неявно — ніколи його не пишіть. select1=cmdsel1.wav перетворюється на sounds/cmdsel1.wav.wav і нічого не знаходить.

Особливості TA: Kingdoms

  • TAK потребує аргументу командного рядка -disablecavedogverification, щоб завантажувати більшість змінених файлів. Без нього рушій відхиляє все, чий хеш відсутній у його списку дозволених.
  • У даних TAK 8 кодів сторін, а не 4: ARA / TAR / VER / ZON для чотирьох ігрових сторін, а також CRE (Creon, Iron Plague), MON (монстри), LIF (дика природа), NPC (персонажі кампанії). Фільтрування списків юнітів має враховувати їх усі.
  • Зброю в TAK описано безпосередньо в кожному FBI як підсекції [WEAPON1]–[WEAPON3], а не в окремому каталозі weapons/*.tdf.
  • Меню будівництва в TAK лінійні, а не 2×3. Кожен будівельник має каталог canbuild/<builder>/<unit>.tdf з одним блоком [Menu] { Priority = N; }.

Див. також