- Арканум
- Что?
- Арканум
- Арканум?
- Да, Арканум. Знаете, двухголовый брамин, Гилберт Бейтс...
...баги, битые диалоги, неработающие квесты, Тройка Геймс, мать его. Арканум!
- Что?
- Арканум
- Арканум?
- Да, Арканум. Знаете, двухголовый брамин, Гилберт Бейтс...
...баги, битые диалоги, неработающие квесты, Тройка Геймс, мать его. Арканум!
Это первая из примерно трех обзорных статей по форматам этой замечательной игры, которые я планирую написать. Каждая статья будет посвящена одной общей теме для всех упомянутых в ней форматов.
Введение
Недавно промелькнула новость, что игровое издательство 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 - классическую изометрию. Заголовок
От лирики перейдем к структуре файлов.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 файл содержит в себе сразу несколько фреймов из которых игровой движок в дальнейшем собирал анимацию движения, стрельбы или смерти. Один файл может содержать только одну определенную анимацию помноженную на количество направлений движения.
[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 байта каждый, при этом первый цвет в палитре - это цвет фона, он же импровизированный альфаканал потому что фон какбы прозрачен.Другое неожиданное открытие состоит в том, что не все из цветов в палитре используются. Возможно следствие какого-то постпроцессинга после конвертации.
Блок информации о фреймах
Сразу после заголовка располагается участок в котором располагаются данные о размере каждого фрейма: размер в байтах, высота, ширина, отступы и смещения.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);
}
}
}Сохранив полученный массив в любой из устраивающих вас форматов, вы получите набор из отдельных изображений.
Источник: https://habr.com/ru/post/665182/