372 lines
30 KiB
Markdown
372 lines
30 KiB
Markdown
# I. Путеводитель и методика
|
||
|
||
Первый том задаёт язык и правила всей документации. Он объясняет, как читать
|
||
технические главы, какие термины используются для игрового runtime, как
|
||
разделяются уровни уверенности и какие требования предъявляются к реализации,
|
||
которая должна работать с оригинальными данными без потери информации.
|
||
|
||
Документация рассчитана на разработчика, который уже умеет читать C/C++,
|
||
байтовые форматы, PE-модули и графические pipeline, но не обязательно знаком с
|
||
Iron3D. Поэтому этот том не описывает один конкретный crate, package или
|
||
физическое деление будущего кода. Он фиксирует контракты: что должно быть
|
||
прочитано, сохранено, рассчитано и показано.
|
||
|
||
## Назначение книги
|
||
|
||
Книга ведёт от общей архитектуры Iron3D к точным форматам данных и алгоритмам
|
||
исполнения. Практическая цель -- реализация, способная открыть оригинальный
|
||
каталог *Parkan: Iron Strategy*, загрузить миссию, создать мир, провести
|
||
игровой шаг и сформировать кадр.
|
||
|
||
Форматы в главах описываются как байтовые контракты. Если указано поле
|
||
`+0x10`, это означает расположение в потоке или структуре данных, а не
|
||
разрешение читать файл прямым `reinterpret_cast`. Для постоянных layouts
|
||
используются offsets, проверки размеров, bounded cursor и явное сохранение
|
||
неизвестных байтов. Для versioned и variable-length записей приоритет имеет
|
||
последовательный parser с контролем границ.
|
||
|
||
Игровое поведение описывается не только размером структур. Совместимая
|
||
реализация должна учитывать порядок событий, время, fallback-правила,
|
||
идентификаторы объектов, численные ограничения, состояние материалов,
|
||
границы кадра и правила завершения операций.
|
||
|
||
## Маршруты чтения
|
||
|
||
**Читатель, новый для игровой разработки**, начинает с базовых понятий этого
|
||
тома, затем переходит к архитектуре, игровому циклу и вводу в рендер. После
|
||
этого имеет смысл читать главы о миссиях, мире и ресурсных форматах.
|
||
|
||
**Разработчик совместимого движка** читает тома II-VII линейно. Технические
|
||
главы имеют одинаковую логику: назначение подсистемы, данные на диске,
|
||
представление в памяти, алгоритм работы, проверки и требования к новой
|
||
реализации.
|
||
|
||
**Аналитик оригинальной программы** использует этот том вместе с разделами о
|
||
доказательной базе, ABI, результатах корпусных проверок и границах знания.
|
||
Факты, согласованные выводы и открытые вопросы должны оставаться разделёнными:
|
||
это позволяет расширять реализацию без подмены проверенных контрактов
|
||
удобными догадками.
|
||
|
||
## Состав документации
|
||
|
||
1. **Путеводитель и методика** -- язык предметной области, правила чтения и
|
||
процедура проверки.
|
||
2. [**Запуск, архитектура и игровой цикл**](02-architecture.md) -- от
|
||
`iron_3d.exe` до расчёта и вывода кадра.
|
||
3. [**Ресурсная система и форматы**](03-resources.md) -- архивы, кэши, реестры
|
||
и служебные данные.
|
||
4. [**Мир, миссии и игровой runtime**](04-world.md) -- TMA, ландшафт, ареалы и
|
||
создание объектов.
|
||
5. [**Геометрия, материалы и рендер**](05-render.md) -- от вершины модели до
|
||
изображения на экране.
|
||
6. [**Поведение, управление, звук и сеть**](06-behavior.md) -- интерактивные
|
||
подсистемы.
|
||
7. [**Руководство по полной реализации**](07-implementation.md) -- предлагаемая
|
||
архитектура и порядок работ.
|
||
8. [**Справочник и доказательная база**](08-evidence.md) -- ABI,
|
||
конфигурация, статистика и открытые вопросы.
|
||
|
||
Дополнительные краткие определения собраны в
|
||
[глоссарии](../appendices/glossary.md). Технические области, где контракт ещё
|
||
не закрыт полностью, перечислены в
|
||
[границах знания](../appendices/knowledge-boundaries.md).
|
||
|
||
## Условные обозначения
|
||
|
||
`+0x10` означает смещение поля относительно начала структуры или записи.
|
||
`RVA 0x13B60` -- адрес относительно базы PE-модуля. `u16`, `u32`, `i16` и
|
||
`float32` обозначают типы фиксированной ширины. `LE` означает little-endian.
|
||
`payload` -- полезные данные записи после метаданных контейнера. `EOF` -- точное
|
||
завершение файла или ограниченного блока.
|
||
|
||
Если в тексте указан hash, RVA или ordinal, значение относится к явно
|
||
обозначенному binary profile. Адреса разных сборок не объединяются по имени
|
||
функции. При публикации функции нужны минимум модуль, SHA-256 сборки и RVA.
|
||
|
||
Размеры структур выражаются в байтах. Счётчики и offsets считаются частью
|
||
формата, даже когда их можно восстановить из длины файла. Padding, reserved
|
||
поля, неизвестные хвосты и gaps не нормализуются без доказанного правила.
|
||
|
||
## Совместимость
|
||
|
||
Слово "совместимость" в этой книге имеет несколько уровней.
|
||
|
||
**Reader** умеет открыть файл, проверить границы, извлечь известные поля и
|
||
сохранить неизвестные bytes так, чтобы данные можно было записать обратно.
|
||
|
||
**Viewer** умеет показать ресурс: модель, texture, material, эффект или карту.
|
||
Viewer может быть полезен для анализа, но он не доказывает поведение runtime.
|
||
|
||
**Runtime** умеет создать мир, зарегистрировать объекты, исполнять события,
|
||
обновлять время, применять контроллеры, выбирать видимое состояние и передавать
|
||
его рендеру.
|
||
|
||
**Полноценный движок** дополнительно воспроизводит порядок операций, численные
|
||
правила, fallback-поведение, resource lifetime, reference ownership, pause,
|
||
manual input, сетевые идентификаторы, boundaries кадра и состояние
|
||
интерактивных подсистем.
|
||
|
||
Поэтому файл может быть "прочитан правильно", но всё ещё не быть реализованным
|
||
на уровне движка. Например, reader MSH может восстановить вершины и индексы,
|
||
viewer может нарисовать mesh, а runtime обязан ещё сохранить material slots,
|
||
animation state, bounds, LOD, visibility, collision и связи с объектом мира.
|
||
|
||
## Движок как программа длительного действия
|
||
|
||
Обычная прикладная программа получает запрос, вычисляет результат и заканчивает
|
||
работу. Игра живёт в цикле: прочитать ввод, обновить состояние мира,
|
||
сформировать звук и изображение, показать кадр и повторить. Движок -- набор
|
||
подсистем и соглашений, которые делают этот цикл устойчивым.
|
||
|
||
**Simulation** отвечает на вопрос "что произошло в мире": куда переместился
|
||
объект, кого он видит, сколько у него здоровья, сработал ли эффект, изменился
|
||
ли маршрут или приказ. **Rendering** отвечает на другой вопрос: "как текущее
|
||
состояние показать". В корректной архитектуре рендер не решает игровые правила,
|
||
а читает подготовленное состояние.
|
||
|
||
**Tick** -- один шаг расчёта. **Frame** -- одно изображение. Они могут
|
||
выполняться с разной частотой: игра способна рассчитать несколько шагов между
|
||
двумя показами или временно не рисовать, не останавливая логику. Поэтому время,
|
||
накопление input, порядок callbacks и момент удаления объектов считаются частью
|
||
контракта.
|
||
|
||
## Мир, сцена и объект
|
||
|
||
**Мир** -- долгоживущее состояние миссии: ландшафт, объекты, время, погода,
|
||
принадлежность к кланам и глобальные сервисы. **Сцена** -- представление той
|
||
части мира, которую можно обработать для текущей камеры. **Игровой объект** --
|
||
сущность с идентификатором, положением, набором свойств и поведением.
|
||
|
||
В Iron3D объектами управляет World3D. Объекты регистрируются в общей очереди,
|
||
получают события, участвуют в расчёте и могут быть удалены отложенно, чтобы не
|
||
разрушить обход коллекции посреди шага. Это важнее, чем конкретный контейнер в
|
||
новой реализации: совместимость определяется моментом наблюдаемого добавления,
|
||
обновления и удаления.
|
||
|
||
Мир не равен renderer scene graph. Один объект может иметь runtime state,
|
||
controller, сетевой mirror, визуальную модель, collision bounds и script state.
|
||
Часть этих данных нужна для gameplay, часть -- для вывода, часть -- для
|
||
сохранения и воспроизведения.
|
||
|
||
## Ресурс, модель и материал
|
||
|
||
**Ресурс** -- именованный блок данных, который можно найти и загрузить. Архивы
|
||
`NRes` и `RsLi` содержат таблицы таких блоков. Имя, индекс, размер, offset,
|
||
compression method и fallback-правило являются частью контракта загрузки.
|
||
|
||
**Модель** описывает форму объекта. Она состоит из вершин, индексов, узлов,
|
||
групп треугольников, слотов материалов и auxiliary streams. **Vertex** хранит
|
||
положение и обычно дополнительные атрибуты: нормаль для освещения и
|
||
UV-координату для выборки texture. **Triangle** -- три вершины, образующие
|
||
примитив. **Index buffer** хранит номера вершин и позволяет переиспользовать их
|
||
между треугольниками. **Batch** -- непрерывный диапазон индексов, который
|
||
рисуется одним материалом и одним набором состояний.
|
||
|
||
**Материал** описывает способ отображения поверхности: texture references,
|
||
цвет, прозрачность, режимы смешивания и анимацию параметров. **Texture** --
|
||
изображение в памяти графической системы. **Mip-уровни** -- уменьшенные копии
|
||
изображения для дальних объектов. **Lightmap** -- дополнительная texture с
|
||
заранее рассчитанным освещением.
|
||
|
||
Runtime должен связывать эти уровни по цепочке: миссия выбирает объект, объект
|
||
ссылается на prototype, prototype приводит к модели, модель -- к WEAR,
|
||
материалам, textures и lightmaps. Ошибка на любом участке этой цепочки может
|
||
не проявиться в parser-е, но проявится в игровом кадре.
|
||
|
||
## Пространственные понятия
|
||
|
||
**Transform** переводит точку из локальных координат модели в координаты мира,
|
||
камеры и экрана. **Иерархия узлов** позволяет одному элементу наследовать
|
||
движение другого. **LOD** выбирает менее подробную геометрию вдали. **Culling**
|
||
отбрасывает то, что не видно. **Bounds** -- упрощённая оболочка объекта,
|
||
обычно сфера или AABB, используемая для быстрых тестов.
|
||
|
||
**Collision** отвечает на геометрические пересечения. **Navigation** ищет
|
||
допустимый маршрут. В Iron3D эти задачи разделены: Control обслуживает
|
||
физическую модель и столкновения, а ArealMap хранит пространственные области и
|
||
связи между ними.
|
||
|
||
Важно не смешивать визуальные и игровые упрощения. Render bounds могут быть
|
||
достаточны для отсечения, но не обязаны совпадать с collision shape. Навигация
|
||
может использовать areal graph, который не является ни mesh-ем модели, ни
|
||
геометрией ландшафта в renderer-е.
|
||
|
||
## Графический конвейер
|
||
|
||
Процессор выбирает видимые объекты, готовит матрицы, материалы и списки
|
||
примитивов. Графический backend передаёт вершины, индексы, textures и state
|
||
драйверу. Видеокарта преобразует вершины в координаты экрана, разбивает
|
||
треугольники на фрагменты, проверяет глубину, смешивает цвет и записывает
|
||
результат в буфер кадра. После завершения буфер становится видимым
|
||
пользователю.
|
||
|
||
Для совместимости важны не только данные draw call. Контракт включает frame
|
||
boundaries, viewport, camera state, порядок world traversal, material resolve,
|
||
shadow/transparent/FX subpasses, завершение renderer-а, восстановление state и
|
||
callbacks после рендера. Если часть имён vtable slots ещё не доказана, новая
|
||
реализация должна фиксировать крупный порядок операций и оставлять
|
||
детализацию проверяемой.
|
||
|
||
## Практический словарь реализации
|
||
|
||
**Handle** -- компактная ссылка на управляемый объект. **Cache** -- сохранённый
|
||
результат загрузки или декодирования. **Reference count** -- число владельцев
|
||
ресурса. **Fallback** -- предписанный запасной вариант при отсутствии данных.
|
||
**Invariant** -- условие, которое всегда должно быть истинным для корректного
|
||
файла или runtime-состояния. **Determinism** -- повторяемость результата при
|
||
одинаковых входных данных и порядке событий.
|
||
|
||
**Strict mode** -- режим parser-а, который принимает только корректный файл:
|
||
верные magic, версии, размеры, ranges, индексы и точный EOF. **Lossless mode**
|
||
-- режим чтения/записи, который сохраняет неизвестные поля, padding, gaps и raw
|
||
payload без нормализации. **Quirk** -- именованное отклонение, разрешённое
|
||
только после проверки на реальных данных или исполняемом коде.
|
||
|
||
Эти слова используются как технические термины. Если глава называет значение
|
||
fallback-ом, invariant-ом или quirk-ом, это должно иметь проверяемое
|
||
последствие в reader-е, writer-е или runtime.
|
||
|
||
## Как читать C/C++-схемы структур
|
||
|
||
Структуры в главах описывают байтовый layout, а не переносимый C++ object
|
||
model. Если поля на диске идут без padding, reader должен читать их по offsets
|
||
либо использовать явно проверенный packed layout. Прямое отображение native
|
||
struct допустимо только при доказанном размере, выравнивании и endian-правиле.
|
||
|
||
`sizeof` обязательно проверяется `static_assert` или эквивалентным compile-time
|
||
test. Это особенно важно для records, где 32-битное поле начинается после
|
||
нечётного числа 16-битных или 8-битных полей: стандартное выравнивание
|
||
современного compiler-а может вставить скрытые bytes и изменить offsets.
|
||
|
||
Для variable-length форматов предпочтителен bounded cursor:
|
||
|
||
1. Прочитать header и проверить минимальный размер.
|
||
2. Проверить, что offsets и sizes лежат внутри текущего блока.
|
||
3. Прочитать таблицы до объявленного count, не до "пока получается".
|
||
4. Проверить ссылки между таблицами.
|
||
5. Дойти до точного EOF или сохранить явно разрешённый trailing payload.
|
||
|
||
Writer пересчитывает только производные значения: размеры, offsets, число
|
||
записей, сортировочные таблицы и padding, если правило доказано. Unknown fields
|
||
и reserved ranges сохраняются побайтно.
|
||
|
||
## Иерархия доказательств
|
||
|
||
Документация использует четыре уровня уверенности.
|
||
|
||
**Прямое наблюдение** -- поле, значение или последовательность видны в
|
||
инструкции программы, таблице PE, экспорте, строке, обработчике файла или в
|
||
самом ресурсе. Это самый сильный уровень.
|
||
|
||
**Корпусное подтверждение** -- правило проверено на всех подходящих файлах
|
||
одного или нескольких явно названных наборов: демоверсии, Части 1 и Части 2.
|
||
Например, базовый корпус содержит 435 моделей MSH, 518 textures Texm и 923
|
||
эффекта FXID, прошедших структурные проверки без ошибок; полные части расширяют
|
||
эту матрицу вариантов.
|
||
|
||
**Согласованный вывод** -- назначение восстановлено по нескольким независимым
|
||
признакам: вызывающим функциям, vtable slots, строкам ошибок, диапазонам
|
||
значений и связям между форматами. Такой вывод пригоден для реализации, но его
|
||
численные детали следует проверять тестами.
|
||
|
||
**Открытый вопрос** -- данные можно читать и сохранять, однако предметный смысл
|
||
поля или редкой ветки не доказан. Такие bytes нельзя обнулять,
|
||
переупорядочивать или превращать в authoring API.
|
||
|
||
Уровень уверенности должен быть виден из формулировки. "Поле равно" означает
|
||
проверенный layout или значение. "Вероятно отвечает за" означает согласованный
|
||
вывод. "Неизвестно" означает сохранять без изменения и не строить вокруг этого
|
||
публичный контракт.
|
||
|
||
## Проверенные материалы
|
||
|
||
Локальный набор проверки включает демоверсию, полные каталоги Частей 1 и 2,
|
||
исполняемые файлы, 15 DLL каждой сборки и игровые ресурсы. DLL из
|
||
первоначального архива и DLL демоверсии совпали по SHA-256: `15/15`, поэтому
|
||
выводы по этому коду и demo-ресурсам образуют один доказательный профиль.
|
||
|
||
Исполняемый файл демоверсии `iron_3d.exe` имеет размер 36 864 байта, PE32/x86,
|
||
entry RVA `0x141E`, image base `0x400000` и SHA-256
|
||
`b0a8b0db1c3a8698c4d4604d89c655496bd91ac1f8859a455e8a45838aebfbd6`.
|
||
|
||
Исполняемые файлы Частей 1 и 2 также имеют размер 36 864 байта и побайтно
|
||
совпадают между собой, но относятся к другому binary profile: entry RVA
|
||
`0x147E`, SHA-256
|
||
`f476af85c034a4b4f34f49d0806e4dff397b5da0ee26d382a7674231144979f7`.
|
||
|
||
Полные каталоги Частей 1 и 2 суммарно включают 60 TMA, 1 101 unit DAT, 254
|
||
NRes-файла и 14 975 NRes entries. Все контейнеры и TMA прошли bounded parser до
|
||
точного EOF; полный достижимый граф обеих частей разрешился без ошибок.
|
||
|
||
## Процедура проверки
|
||
|
||
Проверка строится как воспроизводимая цепочка:
|
||
|
||
1. Снять PE-метаданные, хэши, импорты, экспорты, ordinals, RTTI и строки.
|
||
2. Построить граф вызовов между модулями и отметить фабрики подсистем.
|
||
3. Разобрать функции запуска, загрузчики файлов, главный цикл и критические
|
||
vtable-вызовы.
|
||
4. Проверить форматы независимыми reader-скриптами с контролем границ и точного
|
||
завершения файла.
|
||
5. Построить цепочку миссия -> объект -> прототип -> модель -> материал ->
|
||
texture.
|
||
6. Сравнить счётчики, диапазоны, ссылки и размеры на всём доступном корпусе.
|
||
|
||
Ключевой результат сквозной проверки демо-миссий: все 201 объектов шести
|
||
миссий разрешились в 501 запрос прототипов, затем в 501 модель, 501 таблицу
|
||
WEAR, 3 879 слотов материалов и 5 085 ссылок на textures или lightmaps. Ошибок
|
||
в фактически исполняемом пути нет.
|
||
|
||
## Что не считается доказательством
|
||
|
||
Удобное имя поля не доказывает его назначение. Совпадение layout с текущей
|
||
реализацией не доказывает поведение оригинального runtime. Успешный viewer не
|
||
доказывает writer. Успешный reader одного файла не доказывает формат всего
|
||
корпуса. Совпадение ABI не доказывает побайтную идентичность всех сборок.
|
||
|
||
Если локальные данные и предположение расходятся, приоритет имеют исполняемый
|
||
код, реальные ресурсы и взаимные invariants между форматами. Неизвестное поле
|
||
лучше оставить без имени, чем дать ему ложное предметное значение.
|
||
|
||
## Требования к воспроизводимости
|
||
|
||
Каждая новая реализация должна иметь strict parser mode, lossless roundtrip
|
||
mode и набор corpus tests. Неизвестные поля сохраняются побайтно. Любое
|
||
присвоенное полю имя должно сопровождаться наблюдаемым поведением или тестом.
|
||
Численные правила -- округление, порядок умножения, RNG и время -- считаются
|
||
частью формата исполнения, даже если файл читается правильно.
|
||
|
||
Минимальный отчёт проверки должен фиксировать:
|
||
|
||
1. build profile и hashes модулей;
|
||
2. путь или ключ ресурса;
|
||
3. размер входного файла и hash входных bytes;
|
||
4. версию parser-а или commit реализации;
|
||
5. список включённых quirks;
|
||
6. число прочитанных записей и точку EOF;
|
||
7. ошибки, предупреждения и unknown ranges;
|
||
8. результат roundtrip, если writer участвует в проверке.
|
||
|
||
Для runtime-проверок дополнительно нужны mission key, configuration, device
|
||
profile, начальное состояние, input/time script и trace значимых callbacks.
|
||
|
||
## Разделение профилей
|
||
|
||
Binary profile описывает исполняемый код: PE-метаданные, exports/imports,
|
||
ordinals, hashes, RVA и layout функций. Corpus profile описывает набор файлов:
|
||
каталог, миссии, ресурсы, размеры, counts, variants и статистику parser-а.
|
||
|
||
Эти профили нельзя смешивать без явной пометки. Один и тот же формат может
|
||
иметь общий смысл в разных сборках, но отличаться редкими ветками, адресами
|
||
функций или набором встреченных вариантов. Один и тот же address может иметь
|
||
смысл только внутри конкретного module hash.
|
||
|
||
При расширении документации новое утверждение должно отвечать на три вопроса:
|
||
|
||
1. Где это видно напрямую?
|
||
2. На каком корпусе это проверено?
|
||
3. Что должна сделать реализация, если правило нарушено?
|
||
|
||
Если на один из вопросов нет ответа, утверждение остаётся согласованным выводом
|
||
или открытым вопросом, а не закрытым контрактом.
|