- Арканум
- Что?
- Арканум
- Арканум?
- Да, Арканум. Знаете, двухголовый брамин, Гилберт Бейтс...
...баги, битые диалоги, неработающие квесты, Тройка Геймс, мать его. Арканум!
![картинка для привлечения внимания картинка для привлечения внимания](https://www.pvsm.ru/images/2022/05/12/o-bednom-Arcanum-zamolvite-slovo.jpg)
Это первая из примерно трех обзорных статей по форматам этой замечательной игры, которые я планирую написать. Каждая статья будет посвящена одной общей теме для всех упомянутых в ней форматов.
Введение
Недавно промелькнула новость, что игровое издательство Sierra перешло под контроль Microsoft, а значит у нас с вами есть потенциальная возможность увидеть официальный ремейк культовой игры 2001 года. В тоже время, на мой взгляд, эта новость автоматически означает, что все попытки создания альтернативных ремейков, портов и ремастеров скорее всего будут пресечены новыми владельцами. А может и нет.. в любом случае это то самое время, когда надо заняться эдакой ретроспективой, вспомнить и обсудить, то какую работу проделали разработчики Troika Games, как хранили игровые данные.. и надо ли выгонять Вирджила из партии.
Несколько лет назад я набрел на известный в узких кругах форум мододелов и фанатов игры Arcanum: Of Steamworks and Magick Obscura, и был удивлен, что формат файлов игры был известен лишь частично, хотя игре к этому моменту было уже около 15 лет. По факту редактирование некоторых файлов было эдакой магией "открой файл hex-редактором, отступи N байт и поменяй значение, и если тебе повезет, то характеристика NPC изменится", что на мой взгляд крайне контрпродуктивно.
В действительности бинарный формат файлов был известен сразу двум-трем пользователям этого форума, а может быть и всего нашего интернета.. Первый, и наверно единственный, кто обладал полной информацией - это известный в узких кругах Crypton, моддер из восточной европы. Располагая полными данными он с разницей в несколько лет совершил две попытки создания альтернативного движка игры на QT и WebGL, периодически пропадая на месяцы и годы, да так что в последний раз сообщество было уверено, что он скорее мертв, чем жив.
В своей последней попытке создал аккаунт на Patreon, собрал средств на три банана и успокоился. Обещания расшарить исходники не сдержал и опять пропал из инфополя.
В этом наверно основная проблема современного сообщества моддеров. Тотальная огороженность и нежелание делиться информацией.. в надежде, что благодаря ней они смогут поиметь какой-то мелкий гешефт (нет). В результате ценность ее с каждым днем снижается из-за естественного уменьшения фан-базы ретроигр, а сообщество страдает прямо сейчас.
Забавы ради я решил заняться этой проблемой, и не могу сказать, что добился больших успехов, но кое-что у меня получилось.. с чем и собираюсь дебютировать на Хабре.
Типы файлов
В Arcanum, как и в любой другой игре, существует масса собственных бинарных форматов упаковки архивов, изображений, и файлов данных. И очевидно большая часть из них была ориентирована на уменьшение размера дистрибутива.
Хотя на мой взгляд, там все еще есть куда уменьшать, и при желании можно было не выпиливать львиную долю контента, который засветился в бета-верисии игры, и поместить на тоже количество компакт дисков. Но как мы знаем студия столкнулась с финансовыми трудностями, и нехваткой времени.. поэтому хорошо что игра вообще смогла увидеть свет.
Графические и сопутствующие файлы представлены двумя расширениями (форматами):
-
ART - собственно бинарный формат для тайлов изображений
-
FACWALK - формат обьединения отдельных тайлов для создания крупного декора, как на скриншоте выше
Другие интересные, но практически не связанные с графикой бинарные файлы с расширениями:
-
PROTO + MOB - файлы данных с описанием свойств любого объекта на карте
-
SEC - файлы фрагментов карты размером 64х64 тайла
Естественно, все это упаковано в архивы. Так же без внимания не останутся текстовые файлы диалогов, и скриптов. Но в рамках этой первой статьи я не буду перегружать вас информацией, и расскажу только о ART файлах.
*.ART
Формат этих файлов был уже известен к тому времени, как я присоединился к сообществу.. но не упомянуть о нем я не могу.
Представьте что вы находитесь в 1999 и только что "отпочковались" от Interplay, чтобы построить "свой лунапарк с блекджеком и магией". Современных методов упаковки изображений еще не придумали, а о шейдерах вы едва что-то слышали. Поэтому в Arcanum применяется в общем-то не новый в то время способ сжатия без потери качества основанный на использовании цветовых палитр и исключения повторяющихся пикселей. Все как в старых добрых консолях.
В отличии от того же Fallout, в Arcanum игровая сетка не гексагональная, а изометрическая. Тайловая сетка в столь похожих играх так же отличается. Легендарный Fallout использует триметрическую сетку, а Arcanum - классическую изометрию.
![иллюстрация из редактора карт иллюстрация из редактора карт](https://www.pvsm.ru/images/2022/05/12/o-bednom-Arcanum-zamolvite-slovo-2.png)
Собственно за совпадение сеток авторам пришлось заплатить контентом. Если для гексагональной сетки требуется всего шесть наборов тайлов движущихся обьектов для обозначения движения по шести же возможным направлениям, то в случае с изометрической сеткой - требуется все восемь. Конечно тайлы скорее всего были сгенерированы по примитивным трехмерным моделям, но это не спасало от 25% увеличения их количества.
Заголовок
От лирики перейдем к структуре файлов.
![заголовок ART файла заголовок ART файла](https://www.pvsm.ru/images/2022/05/12/o-bednom-Arcanum-zamolvite-slovo-3.png)
В начале каждого файла находится 36 байт заголовка, которые содержат базовые данные для верного декодирования остальной части файла.
02 00 00 00 // ART type : Static=0х01, Сritter=0x02, Font=0x04 и тд
0F 00 00 00 // FrameRate : Unknown
08 00 00 00 // Direction Count
// Количество "направлений" арта по изометрической сетке.
// Всегда равно восьми, и скорее всего является унифицированной
// информацией о сетке. Игнорируется нами так как не несет
// практической пользы. Даже Static с одним единственным фреймом
// имеют значение 0x08000000
f8 f9 12 00 // Блок из 16 байт с неопределенным содержимым
00 00 00 00 // Возможно это 4xRGBA, по числу политр,
00 00 00 00 // но цвет не коррелирует. Если палитра не используется,
00 00 00 00 // то замещено нулями для выравнивания
0E 00 00 00 // Action Frame : Стартовый фрейм для анимации или редактора
0F 00 00 00 // Frame Count: Количество фреймов для каждого
// "направления" движения
Как вы наверно поняли, каждый ART файл содержит в себе сразу несколько фреймов из которых игровой движок в дальнейшем собирал анимацию движения, стрельбы или смерти. Один файл может содержать только одну определенную анимацию помноженную на количество направлений движения.
![NPС (кродеться) NPС (кродеться)](https://www.pvsm.ru/images/2022/05/12/o-bednom-Arcanum-zamolvite-slovo-4.gif)
Общее же количество фреймов внутри каждого ART файла легко вычислить по очевидной формуле [Frame Count] * [Direction Count].
Следом за количеством фреймов расположен интересный блок данных с опять же неопределенным содержимым. Это три блока по 32 байта.
![О бедном Arcanum замолвите слово - 5 О бедном Arcanum замолвите слово - 5](https://www.pvsm.ru/images/2022/05/12/o-bednom-Arcanum-zamolvite-slovo-5.png)
Даже с подключенным отладчиком, определить назначение первых и последних 32 байт мне не удалось. Средние 32 байта это набор из восьми 32 битных чисел, каждое из которых является суммой длинн отдельных фреймов для каждого из направлений сетки. Это, а так же тот факт, что собственноручно созданный арт с нулями в этом месте без проблем подтягивается игрой, наводит на мысли, что остальные 64 байта - это исключительно служебная информация.
[StructLayout(LayoutKind.Sequential)]
public struct ArtHeader
{
[MarshalAs(UnmanagedType.U4)]
public ArtType Flags;
[MarshalAs(UnmanagedType.U4)]
public uint FrameRate;
[MarshalAs(UnmanagedType.U4)]
public uint DirectionCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public ArtColor[] BackColors;
[MarshalAs(UnmanagedType.U4)]
public uint ActionFrameNumber;
[MarshalAs(UnmanagedType.U4)]
public uint FrameCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public uint[] Unk0;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public uint[] Unk1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public uint[] Unk2;
};
Палитра цветов
Следом за заголовком идет от одного до четырех блоков цветовых палитр, количество которых зависит от количества ненулевых фонов в заголовке. Каждая отдельная палитра содержит 256 цветов по 4 байта каждый, при этом первый цвет в палитре - это цвет фона, он же импровизированный альфаканал потому что фон какбы прозрачен.
![Самый маленький ART. Заголовок обрезан, фоновый цвет 0xFF0000 (красный) Самый маленький ART. Заголовок обрезан, фоновый цвет 0xFF0000 (красный)](https://www.pvsm.ru/images/2022/05/12/o-bednom-Arcanum-zamolvite-slovo-6.png)
Как видите одна из особенностей (проблемой это сложно назвать) заключается в том, что если в палитру занесено 28 цветов, то память все равно выделяется для 256.. оставшееся незатребованным заполняется нулями. Частично это купируется тем, что ART файлы упаковываются в архивы, но байты оперативной памяти это совсем не экономит. По моему мнению в этом месте первым байтом должно было располагаться количество цветов в палитре, а дальше собственно сама палитра.
Другое неожиданное открытие состоит в том, что не все из цветов в палитре используются. Возможно следствие какого-то постпроцессинга после конвертации.
Блок информации о фреймах
Сразу после заголовка располагается участок в котором располагаются данные о размере каждого фрейма: размер в байтах, высота, ширина, отступы и смещения.
![Один блок FRAME INFO Один блок FRAME INFO](https://www.pvsm.ru/images/2022/05/12/o-bednom-Arcanum-zamolvite-slovo-7.png)
При кажущейся простоте тут тоже не обошлось без белых пятен. Например Delta_X, Delta_Y. О назначении этих полей до сих пор спорят на профильных форумах.
09 00 00 00 // Frame_Width
0D 00 00 00 // Frame_Heigth
38 00 00 00 // Frame_Size
// Размер фрейма в байтах, равен или меньше,
// чем [Frame Width] * [Frame Heigth] из-за пропуска
// повторяющихся пикселей
// Смещение от верхнего левого угла для экономии памяти
0D 00 00 00 // Offset_X
12 00 00 00 // Offset_Y
// Почти везде нули. Предположительно, может быть как "точкой крепления"
// к сетке, так и единицей смещения для анимации передвижения.
// Не проверено.
00 00 00 00 // Delta_X
00 00 00 00 // Delta_Y
Полезная пиксельная нагрузка
Сразу после информации о фреймах идет непосредственно массив пикселей. Каждый пиксель имеет размер один байт и содержит номер одного из 256 цветов в палитре.
Но все не так просто. Чтобы не хранить повторяющиеся пиксели в чистом виде, в том числе и фоновые пиксели, применяется небольшая хитрость. Пиксели уложены неравномерными фрагментами (или чанками, если вам так удобнее), а размер и тип чанка "зашифрован" в первом байте. Старший бит определяет тип фрагмента, а младшие 7 бит - количество циклов чтения или повторения.
long originFrameSize = frameInfo.Height * frameInfo.Width;
byte[] unpackDump = new byte[originFrameSize];
if (originFrameSize == frameInfo.Size)
{
ms.Read(unpackDump, 0, unpackDump.Length);
}
else
{
using MemoryStream msUnpack = new MemoryStream(unpackDump);
while (msUnpack.Position < originFrameSize)
{
int chunkInfo = ms.ReadByte();
int readOrRepeatCount = chunkInfo & 0x7F;
bool isRepeat = (chunkInfo & 0x80) == 0;
byte c = 0x00;
for(int i = 0; i < readOrRepeatCount; i++)
{
if (!isRepeat || i == 0)
{
c = (byte) ms.ReadByte();
}
msUnpack.WriteByte(c);
}
}
}
Сохранив полученный массив в любой из устраивающих вас форматов, вы получите набор из отдельных изображений.
![О бедном Arcanum замолвите слово - 8 О бедном Arcanum замолвите слово - 8](https://www.pvsm.ru/images/2022/05/12/o-bednom-Arcanum-zamolvite-slovo-8.png)
Путем нехитрых манипуляций его можно превратить в атлас или GIF анимацию. Смотря какую задачу вам нужно выполнить.. только не забудьте применить смещения, без них анимации будут дерганными из-за неодинаковых размеров изображений.
Автор: Влад