Поширені підводні камені моддингу й розбору
Джерело: 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; }.
Див. також
- Посібник із моддингу — наскрізний розбір створення мода юніта для TA.
- Глосарій — визначення всіх міжформатних термінів, що згадуються вище.
- Швидкий старт — п'ять команд kbot, з яких варто почати.