В начале 2000-х годах я любил ковыряться в играх и доставать из них различные ресурсы - графику, звуки, модели. Моими главными и единственными инструментами тогда были шестнадцатеричные редакторы pview и hiew, компиляторы Pascal и C/C++. Наковырял я довольно много, правда тогда мне было интересно только доставать из игр ресурсы, но не делать инструменты для редактирования.
Некоторые форматы файлов были проанализированы поверхностно - на уровне, достаточном для извлечения данных, но не более. Некоторые были проанализированы довольно хорошо, так что я даже смог реализовать не только извлечение данных, но и обратную упаковку.
Спустя некоторое время я погряз в дебрях ООП, так что на проектирование объектной структуры программы стало уходить столько времени, что я не мог довести до конца ни одной программы. Это было началом "творческой депрессии", в результате которой я надолго забросил программирование.
История.
В сентябре 2010 года я решил тряхнуть стариной и написать программу для распаковки и упаковки файлов с игровыми ресурсами. Чтобы не повторять прошлых ошибок, писать я решил на чистом C, без использования ООП. Я спроектировал плагинную систему и решил реализовать в этой программе поддержку только тех форматов, которые хорошо на неё ложатся.
К февралю 2011 года я практически завершил разработку программы, планируя добавить поддержку ещё одного формата (IWAD/PWAD). С этим форматом возникли сложности и работа застопорилась. Хотя я периодически вспоминал о программе и пытался доделать поддержку этого формата, в конце концов я нашёл какое-то более интересное занятие и забыл об этой программе.
Недавно я о ней вспомнил и подумал - не пропадать же добру, может кому пригодится. Попробовал собрать, исправил пару ошибок, из-за которых сборка не шла, удалил зачатки неудавшегося плагина и решил выложить всё это.
Поскольку к 2010 году я уже пользовался Debian'ом, то программа тоже делалась на нём. Сборка осуществляется с помощью shell-скрипта, для сборки нужны пакеты gcc, zlib1g, zlib1g-dev. Сборка в других системах не тестировалась.
Кстати, большинство игр прекрасно работает в wine и dosbox'е. Все я не тестировал, но думаю, что на самом деле работают все.
Программа.
Программа называется PUP - Packer/UnPacker (слово pup также можно перевести с английского как "щенок"). Программа имеет несколько режимов работы:
1. Если программа запущена без опций, выводится справка по доступным опциям.
2. При указании опции --list программа выводит описание всех доступных плагинов.
3. При указании опции --unpack, программа попытается определить формат указанного ей файла и распакует его содержимое в указанный каталог, а метаданные файла сохранит в указанный файл.
Если имя файла метаданных не указано, используется имя каталога с добавленным к нему расширением ".txt".
Если имя каталога не указано, используется имя файла без расширения.
Чтобы программа не пыталась определить формат файла самостоятельно, можно указать опцию --plugin с указанием определённого формата.
4. При указании опции --pack, программа запакует данные в указанный файл из указанного каталога с использованием метаданных из указанного файла.
Если имя файла метаданных не указано, используется имя каталога с добавленным к нему расширением ".txt".
Если имя каталога не указано, используется имя файла без расширения.
При упаковке обязательно указать опцию --plugin с указанием формата формируемого файла.
5. При указании опции --savemeta, программа пытается определить формат указанного ей файла и сохранить метаданные в указанный файл. Если плагин не содержит метаданных, программа сообщит об этом и завершит работу.
Если имя файла метаданных не указано, используется имя исходного файла с расширением, заменённым на ".txt".
Чтобы программа не пыталась определить формат файла самостоятельно, можно указать опцию --plugin с указанием определённого формата.
6. При указании опции --print, программа выводит на стандартный вывод техническую информацию из каталога ресурсов указанного файла (смещение ресурса, его сжатый и исходный размер, название ресурса и т.п.).
Чтобы программа не пыталась определить формат файла самостоятельно, можно указать опцию --plugin с указанием определённого формата.
Справка программы:
$ ./pup Usage: pup --list pup --plugin <plugin> --pack <file> [<dir> [<meta>]] pup [--plugin <plugin>] --unpack <file> [<dir> [<meta>]] pup [--plugin <plugin>] --savemeta <file> [<meta>] pup [--plugin <plugin>] --print <file> Options: --plugin <plugin> - specify certain plugin Modes: --list - list of all supported plugins --pack <file> [<dir> [<meta>]] - packing dir to specified file --unpack <file> [<dir> [<meta>]] - unpacking specified file to dir --savemeta <file> [<meta>] - only save metadata to specified metafile --print <file> - print technical information to stdoutСписок поддерживаемых форматов:
$ ./pup --list Supported plugins: grp GRP-files of Duke Nukem 3D, Witchaven, Redneck Rampage, Shadow Warrior gob GOB-files of Star Wars: Dark Forces pak PAK-files of Dune II: The Building of a Dynasty viv VIV-files of the Need For the Speed 3: Hot Pursuit vpp VPP-files of Red Faction, The Punisher, Summoner pack PAK-files of Quake, Quake II, Half-Life, Heretic 2, MDK 2 pack2 PAK-files of Daikatana wad2 WAD-file of Quake wad3 WAD-files of Half-Life res RES-file of Comanche 3 dpk4 DPK-file of Starmageddon 2 dat DAT-files of Fallout dat2 DAT-files of Fallout 2 rff20 RFF-files of Blood, version 2.0 rff30 RFF-files of Blood, version 3.0 rff31 RFF-files of Blood, version 3.1Программа отлично справляется с определением типа исходного файла, поэтому используемый плагин указывать совершенно не обязательно. Плагин нужно указывать только при упаковке, чтобы программа сформировала файл нужного вам формата.
Метаданные используются только в формате RFF. Метаданные - это дополнительные информационные поля, сопровождающие каждый упакованный файл. Эти поля никак нельзя восстановить по содержимому самого файла, поэтому при распаковке RFF-файла нужно сохранить эти поля в текстовый файл, а при упаковке - загрузить их из текстового файла.
Есть много различных упаковщиков и распаковщиков файлов игр. Уникальность моей программы заключается в поддержке форматов pak-файлов Daikatana, res-файлов Comanche 3 и rff-файлов Blood.
Формат pak-файлов Daikatana и его алгоритм компрессии я анализировал сам. Сам же написал декомпрессор, что было довольно легко, и компрессор, что оказалось значительно сложнее. Я не силён в алгоритмах сжатия, поэтому компрессор работает довольно медленно, однако сжимает он лучше, чем компрессор разработчиков. Распаковка и упаковка была реализована 15 сентября 2002, а более эффективная версия упаковщика - 20 октября 2002.
Формат res-файлов Comanche 3 я тоже анализировал сам. Сложность анализа этого формата заключалась в том, что имена ресурсов в нём были зашифрованы, так что просто глазами найти имена файлов оказалось непросто.
Тогда у меня была самописная программа, которая позволяла находить в произвольном файле содержащиеся в нём файлы определённых форматов по их сигнатурам. Например, wav-файлы и avi-файлы находились по сигнатуре RIFF (плюс дополнительные проверки), PCX-файлы находились по характерной для них сигнатуре и т.п. С помощью этой программы я нашёл в res-файле Comanche 3 точки начала нескольких файлов. Рядом с ними я увидел их размеры и какие-то смещения. И ещё были поля с непонятным содержимым. Я написал программу, которая выводила мне каталог всех ресурсов и смогла их распаковать в файлы, имена которых составлялись из порядкового номера ресурса.
Дальше я заметил в тех непонятных записях, что последние байты часто бывают одинаковыми и сделал предположение, что это - результат шифрования байта со значением 0 операцией XOR. Я подумал, что в конце имени файла обычно бывает его расширение и нашёл ресурсы, формат которых мне известен. Так я восстановил четырёхбайтовый ключ шифрования, который для шифрования 12-байтового имени файла использовался трижды. Первая программа была написана 09 февраля 2003 и умела только распаковывать ресурсы.
С rff-файлами Blood всё оказалось и проще и сложнее. Информацию об алгоритме шифрования файлов я нашёл в Интернете, однако она подходила не ко всем найденным мной файлам rff. Я нашёл несколько разных версий утилиты BARF для создания новых файлов для Blood и попробовал сформировать ими новые файлы. Так я нашёл три разных формата rff-файлов, распаковку и упаковку которых и реализовал. Однако, игра не захотела работать с родными файлами, обработанными распаковкой и упаковкой. В конце концов я реализовал сохранение и загрузку метаданных rff-файлов при их распаковке и упаковке соответственно. Приём сработал и Blood стал работать с пересобранными файлами.
С остальными форматами всё было проще. Некоторые я проанализировал сам, о некоторых почитал в интернете.
Архив с исходными текстами программы можно скачать здесь. Там же есть текстовый файлик с именем ideas.txt, в котором я записывал идеи и отмечал этапы разработки программы.
8 комментариев:
Отличная программа. Единственная, которая заработала у меня с файлами Heretic 2. Одна беда - при попытке распаковать файл от русских пиратов пишет:
user@amd:/media/heretic2/base/pup$ ./pup --unpack htic2-1.pak
Autoselected path: htic2-1/.
Autoselected metafile: htic2-1.txt.
Autodetected plugin: pack.
Checking entries 0/161. Found 0 referenced to same block, 0 with same content, 0rt_fix: entries 1 and 2 overlapped!
unpack: rt_fix failed.
И что делать? Как быть? Мне нужно распаковать именно русскую пиратку чтобы исправить перевод.
Области данных первой и второй записей частично перекрываются. Получается что один из ресурсов начинается внутри другого, а другой - заканчивается внутри первого.
Как я понимаю, вы скомпилировали программу из исходных текстов? В таком случае откройте файл restable.c и закоментируйте в нём вот этот блок:
/* Проверка, не пересекаются ли блоки. */
if (((rt->entries[i].offset > rt->entries[j].offset)
&& (rt->entries[i].offset < rt->entries[
j].offset + rt->entries[j].compressed))
|| ((rt->entries[i].offset + rt->entries[i].compressed > rt->entries[j].offset)
&& (rt->entries[i].offset + rt->entries[i].compressed < rt->entries[j].offset + rt->entries[j].compressed)))
{
fprintf(stderr, "rt_fix: entries %zu and %zu overlapped!\n", i, j);
return FALSE;
}
После этого перекомпилируйте программу и попробуйте распаковать ваш файл. Однако предупреждаю, что распакованные ресурсы будут скорее всего частично повреждены, т.к. перекрываются в исходном файле.
Отлично! Помогло. Спасибо. Ещё вопрос... Может вы в курсе чем можно отредактировать файлы шрифтов Heretic 2? Хочу сделать нормальный русский шрифт и русские тексты, а то в пиратке как всегда - тексты без дешифратора не прочесть )))
Спасибо ещё раз за помощь и программу, очень выручили.
Ещё проблема. Решил запаковать готовый материал в pak файл Heretic 2
/base/pup$ ./pup --plugin pack --pack ru
Selected plugin: pack.
Autoselected path: ru/.
Autoselected metafile: ru.txt.
rt_init: failed.
Что я делаю не так?
Вот мой лог компиляции самой программы, там есть фатальные ошибки... Возможно в них дело.
Спасибо за ответ.
https://www.dropbox.com/s/bvl92v42fa7eu23/pup_comp.txt?dl=0
У себя Heretic 2 я не нашёл и сейчас не помню, в каком формате там шрифты. Если не изменяет память, то это картинка 256x256 в собственном формате Heretic 2, который имеет расширение m32. Символы там имеют размеры 8x8 и выстроены в виде таблицы 16x16.
Можете попробовать приспособить в целях распаковки и упаковки мою программу texture, которую я писал на C++ в 2002 году ещё под Windows:
http://stupin.su/files/texture.tbz
К сожалению, помочь со сборкой и адаптацией под Linux не смогу - времени не хватает даже просто чтобы написать этот ответ.
Формат вызова команды такой:
pup --plugin <plugin> --pack <file> [<dir> [<meta>]]
Программе надо указывать имя собираемого файла, а каталог и имя файла с метаданными она сгенерирует сама. Вы указали имя каталог, соответственно - программа посчитала его именем собираемого файла, неправильно сгенерировала имя каталога и файла с метаданными, не смогла их найти и завершилась. Как вариант - можно указать все три имени явно.
Помню, у меня были аналогичные проблемы со сборкой. Изначально я писал программу под Linux i386. Потом я стал пользоваться Linux amd64 и под ним она не собиралась из-за несовпадения размеров целых чисел: в программе предполагалось что они всегда 32-битные, а оказались 64-битные. И ещё были проблемы с тем, что тип ssize_t переехал в другой заголовочный файл. Я для себя программу доработал, чтобы она собиралась. К сожалению, в процессе доработки редактор покорёжил отступы и я их так и не исправил.
Архив с доработанной программой можно взять тут:
http://stupin.su/files/pup64.tbz
Как время появится - поправлю отступы. Эта версия под Linux amd64 должна собираться нормально, если опять состав заголовочных файлов в системе не изменился.
1. texture работает хорошо со всеми форматами, кроме конвертации в m32 ))) Программа валится с ошибкой в винде7 х64, но нормально отрабатыват под винХП.
2. Понимаю что времени нет у вас... Но, может, сможете объяснить логику шрифтов font.m32, font1.m32, font2.m32? Ибо правило что "Символы там имеют размеры 8x8 и выстроены в виде таблицы 16x16." распростаняется только на шрифт conchars.m32. Не могу понять каких размеров и где рисовать новые.
Я приложил ссылки на скрины как выглядят шрифты и ссылку на оригинальные файлы шрифтов.
Может, этот комментарий не постите в блог. Ответье хоть что в почту, скайп или вайбер.
https://www.dropbox.com/s/n8jnl3jlhrf57ir/%D0%A1%D0%BA%D1%80%D0%B8%D0%BD%D1%88%D0%BE%D1%82%202017-12-06%2013.31.17.png?dl=0
https://www.dropbox.com/s/5fu9zyagy8c5mg8/%D0%A1%D0%BA%D1%80%D0%B8%D0%BD%D1%88%D0%BE%D1%82%202017-12-06%2013.31.28.png?dl=0
https://www.dropbox.com/s/5fyhyqf1qhpivi4/%D0%A1%D0%BA%D1%80%D0%B8%D0%BD%D1%88%D0%BE%D1%82%202017-12-06%2013.31.44.png?dl=0
https://www.dropbox.com/s/h40cun1ls66nbv5/misc.rar?dl=0
Спасибо за доработки, приятно что автор не в стол все это сделал.
Отправить комментарий