16 KiB
Reverse Engineering игры Parkan Железная стратегия 1998
Сборка проекта
Проект написан на C# под .NET 9
Вам должно хватить dotnet build
для сборки всех проектов отдельно.
Все приложения кросс-платформенные, в том числе UI.
Состояние проекта
- Поддержка всех
NRes
файлов - звуки, музыка, текстуры, карты и другие файлы. Есть документация. - Поддержка всех
TEXM
текстур. Есть документация. - Поддержка файлов миссий
.tma
. - Поддержка шрифтов TFNT.
- Поддержка файлов скриптов
.scr
. - Поддержка файлов параметров
.var
. - Поддержка файлов схем объектов
.dat
.
Структура проекта
Внимание!
Проект делается как небольшой PET, поэтому тут может не быть
- чёткой структуры
- адекватных названий
- комментариев
Я конечно стараюсь, но ничего не обещаю.
Для Reverse Engineering-а использую Ghidra
Наблюдения
- Игра использует множество стандартных библиотек, в частности stl_port, vc++6 и другие. Если хотите что-то изучить в игре, стоит поискать по строкам и сигнатурам, что именно используется в конкретной
dll
. - Строки в основном используются двух форматов -
char*
иstd::string
. Последняя состоит из 16 байт -undefined4, char* data, int length, int capacity
. - В игре очень много
inline
функции, которые повторяются по куче раз в бинарнике. - Игра загружает и выгружает свои
dll
файлы по несколько раз, так что дебаг сMemory Map
очень затруднён. - Игра активно и обильно течёт по памяти, оставляя после чтения файлов их
MapViewOfFile
и подобные штуки. - Игра нормально не работает на Win10. Мне помог dgVoodoo. Хотя с ним не работает
MisEditor
.
Как быстро найти текст среди всех файлов игры
grep -rl --include="*" "s_tree_05" .
Как быстро найти байты среди всех файлов игры
grep -rlU $'\x73\x5f\x74\x72\x65\x65\x5f\x30\x35' .
Как работает игра
Главное меню:
Игра сканирует хардкод папку missions
на наличие файлов миссий. (буквально 01, 02, 03 и т.д.)
Сначала игра читает название миссии из файла descr
- тут название для меню.
- Одиночные игры -
missions/single.{index}/descr
- Тренировочные миссии -
missions/tutorial.{index}/descr
- Кампания -
missions/campaign/campaign.{index1}/descr
- Далее используются подпапки -
missions/campaign/campaign.{index1}/mission.{index2}/descr
- Далее используются подпапки -
Как только игра не находит файл descr
, заканчивается итерация по папкам (понял, т.к. пробуется файл 05 - он не существует).
Загрузка миссии:
Читается файл ui/game_resources.cfg
Из этого файла загружаются ресурсы
library = "ui\\ui.lib"
- загружается файлui.lib
library = "ui\\font.lib"
- загружается файлfont.lib
library = "sounds.lib"
- загружается файлsounds.lib
library = "voices.lib"
- загружается файлvoices.lib
Затем игра читает save/saveslots.cfg
- тут слоты сохранения
Затем Comp.ini
- тут системные функции, которые используются для загрузки объектов.
-
Host.url
- этого файла нет -
palettes.lib
- тут палитры, но этот NRes пустой -
system.rlb
- не понятно что -
Textures.lib
- тут текстуры -
Material.lib
- тут какие-то материалы - не понятно -
LightMap.lib
- видимо это карты освещения - не понятно -
sys.lib
- не понятно -
ScanCode.dsc
- текстовый файл с мапом клавиш -
command.dsc
- текстовый файл с мапом клавиш
Тут видимо идёт конфигурация ввода
-
table_1.man
- текстовый файл -
table_2.man
- текстовый файл -
hero.man
- текстовый файл -
addition.man
- текстовый файл -
Снова
table_1.man
-
Снова
table_1.man
-
M1.tbl
- текстовый файл -
Снова
table_2.man
-
Снова
table_2.man
-
M2.tbl
- текстовый файл -
Снова
hero.man
-
Снова
hero.man
-
HERO.TBL
-
Снова
addition.man
-
ui/hq.cfg
-
Снова
ui/hq.cfg
Дальше непосредственно читается миссия
mission.cfg
- метадата миссииunits\\units\\prebld\\scr_pre1.dat
из метаданныхobject prebuild
-cp
файл (грузятся подряд все)- Опять
ui/hq.cfg
mistips.mis
- описание для игрока (экран F1)scancode.dsc
- хзcommand.dsc
- хзui_hero.man
- хзui_bots.man
- хзui_hq.man
- хзui_other.man
- хз- Цикл чтения курсоров
ui/cursor.cfg
- тут настройки курсора.ui/{name}
- курсор
- Снова
mission.cfg
- метадата миссии descr
- названиеdata/textres.cfg
- конфиг текстов- Снова
mission.cfg
- метадата миссии - Ещё раз
mission.cfg
- метадата миссии ui/minimap.lib
- NRes с текстурами миникарты.messages.cfg
- Tutorial messages
УРА НАКОНЕЦ-ТО data.tma
-
Из
.tma
берётся LAND строка (я её так назвал) -
DATA\\MAPS\\SC_3\\land1.wea
-
DATA\\MAPS\\SC_3\\land2.wea
-
BuildDat.lst
- Behaviour will use these schemes to Build Fortification -
DATA\\MAPS\\SC_3\\land.map
-
DATA\\MAPS\\SC_3\\land.msh
-
effects.rlb
Цикл по кланам из .tma
-
MISSIONS\\SCRIPTS\\screampl.scr
-
varset.var
-
MISSIONS\\SCRIPTS\\varset.var
-
MISSIONS\\SCRIPTS\\screampl.fml
-
missions/single.01/sky.ske
-
missions/single.01/sky.wea
Дальше начинаются объекты игры
"UNITS\\BUILDS\\BUNKER\\mbunk01.dat"
- cp файл
Загрузка cp
файлов
cp
файл - схема. Он содержит дерево частей объекта.
cp
файл читается в ArealMap.dll/CreateObjectFromScheme
В зависимости от типа объекта внутри схемы (байты 4..8) выбирается функция, с помощью которой загружается схема.
Функция выбирается на основе файла Comp.ini
.
- Для ClassBuilding (0x80000000) - вызывается функция c классом 3 (по таблице ниже Building).
- Для всех остальных - функция с классом 4 (по таблице ниже Agent).
На основе файла Comp.ini
и первом вызове внутри функции World3D.dll/CreateObject
ремаппинг id:
Class ID | ClassName | Function |
---|---|---|
1 | Landscape | terrain.dll LoadLandscape |
2 | Agent | animesh.dll LoadAgent |
3 | Building | terrain.dll LoadBuilding |
4 | Agent | animesh.dll LoadAgent |
5 | Camera | terrain.dll LoadCamera |
7 | Atmospehere | terrain.dll CreateAtmosphere |
9 | Agent | animesh.dll LoadAgent |
10 | Agent | animesh.dll LoadAgent |
11 | Research | misload.dll LoadResearch |
12 | Agent | animesh.dll LoadAgent |
Будет дополняться по мере реверса.
Всем этим функциям передаётся nres_file_name, nres_entry_name, 0, player_id
fr FORT
файл
Всегда 0x80 байт Содержит 2 ссылки на файлы:
.bas
.ctl
- вызываетсяLoadAgent
.msh
Загружается в AniMesh.dll/LoadAniMesh
- Тип 01 - заголовок
нулевому элементу добавляется флаг 0x1000000 Хранит стейты меша (в один стейт может входить несколько submesh) Содержит 2 ссылки на файлы анимаций (короткие - файл 13, длинные - файл 08) Если интерполируется анимация -0.5s короче чем magic1 у файла 13 И у файла есть OffsetIntoFile13 И ushort значение в файле 13 по этому оффсету > IndexInFile08 (это по-моему выполняется всегда) Тогда вместо IndexInFile08 используется значение из файла 13 по этому оффсету (второй байт)
- Тип 02 - описание submesh
Вначале идёт заголовок 0x8C (140) байт В заголовке: 8 Vector3 (x,y,z) - bounding box 1 Vector4 - center 1 Vector3 - bottom 1 Vector3 - top 1 float - xy_radius Далее инфа про куски меша
- Тип 03 - это вершины (vertex)
- Тип 06 -
- Тип 04 - скорее всего какие-то цвета RGBA или типа того
- Тип 08 - меш-анимации (см файл 01)
Индексируется по IndexInFile08 из файла 01 либо по файлу 13 через OffsetIntoFile13 Структура: Vector3 position; float time; // содержит только целые секунды short rotation_x; // делится на 32767 short rotation_y; // делится на 32767 short rotation_z; // делится на 32767 short rotation_w; // делится на 32767 --- Игра интерполирует анимацию между текущим стейтом и следующим по time. Если время интерполяции совпадает с исходным time, жёстко берётся первый стейт из 0x13. Если время интерполяции совпадает с конечным time, жёстко берётся второй стейт из 0x13. Если ни то и ни другое, тогда t = (time - souce.time) / (dest.time - source.time)
- Тип 12 - microtexture mapping
- Тип 13 - короткие меш-анимации (почему я это не дописал?)
Буквально (hex) 00 01 01 02 ...
- Тип 0A
Не имеет фиксированной длины. Хранит какие-то строки в следующем формате. Игра обращается по индексу, пропуская суммарную длину и пропуская 4 байта на каждую строку (длина). т.е. буквально файл выглядит так 00 00 00 00 - пустая строка 03 00 00 00 - длина строки 1 73 74 72 00 - строка "str" + null terminator .. и повторяется до конца файла Кол-во элементов из NRes должно быть равно кол-ву строк в этом файле, хотя игра это не проверяет. Если у элемента эта строка равна "central", ему выставляется флаг (flag |= 1)
Тип 02 имеет заголовок 140 байт.
.wea
Загружается в World3D.dll/LoadMatManager
Внутренняя система ID
1
-4
- IShader5
- ITerrain6
- IGameObject (0x138)7
- IShadeConfig (у меня в папке с игрой его не оказалось)8
- ICamera9
- Queue10
- IControl0xb
- IAnimation0xd
- IMatManager0xe
- ILightManager0x10
- unknown (implemented by Wizard in Wizard.dll, also by Hallway in ArealMap.dll)0x11
- IBasement0x12
- ICamera2 - BufferingCamera0x13
- IEffectManager0x14
- IPosition0x16
- ILifeSystem0x17
- IBuilding0x18
- IMesh20x19
- unknown (implemented by Wizard in Wizard.dll, also by Agent in AniMesh.dll)0x20
- IJointMesh0x21
- IShade0x23
- IGameSettings0x24
- IGameObject20x101
- 3DRender0x201
- IWizard0x202
- IItemManager0x203
- ICollManager0x301
- IArealMap0x302
- ISystemArealMap0x303
- IHallway0x401
- ISuperAI0x105
- NResFile0x106
- NResFileMetadata0x501
- MissionData0x502
- ResTree0x700
- NetWatcher0x701
- INetworkInterface0x10d
- CreateVertexBufferData
Опции
World3D.dll содержит функцию CreateGameSettings. Она создаёт объект настроек и далее вызывает методы в соседних библиотеках.
- Terrain.dll - InitializeSettings
- Effect.dll - InitializeSettings
- Control.dll - InitializeSettings
Остальные наверное не трогают настройки.
Resource ID | wOptionID | Name | Default | Description |
---|---|---|---|---|
1 | 100 (0x64) | "Texture detail" | ||
2 | 101 (0x65) | "3D Sound" | ||
3 | 102 (0x66) | "Mouse sensitivity" | ||
4 | 103 (0x67) | "Joystick sensitivity" | ||
5 | !not a setting! | "Illegal wOptionID" | ||
6 | 104 (0x68) | "Wait for retrace" | ||
7 | 105 (0x69) | "Inverse mouse X" | ||
8 | 106 (0x6a) | "Inverse mouse Y" | ||
9 | 107 (0x6b) | "Inverse joystick X" | ||
10 | 108 (0x6c) | "Inverse joystick Y" | ||
11 | 109 (0x6d) | "Use BumpMapping" | ||
12 | 110 (0x6e) | "3D Sound quality" | ||
13 | 90 (0x5a) | "Reverse sound" | ||
14 | 91 (0x5b) | "Sound buffer frequency" | ||
15 | 92 (0x5c) | "Play sound buffer always" | ||
16 | 93 (0x5d) | "Select best sound device" | ||
---- | 30 (0x1e) | ShadeConfig | из файла shade.cfg |
Контакты
Вы можете связаться со мной в Telegram.