Вскрытие игровых ресурсов на примере игры You Are Empty

 

Привет всем! Как и обещал - как только разобрался с сессией, выкладываю статью по вскрытию игровых ресурсов. Начнем мы ковыряться в отечественной игрушке - YouAreEmpty. Надеюсь что у вас имеется лицензионная копия ;) Для тех у кого игры нет - я возьму на себя смелость приложить к примеру одну из моделей.

Что же нам понадобиться - сама игра (ну или модель которую я приложил в архиве), любой современный компилятор С++ и любимая вами IDE (я пользовался Visual Studio 2008) и ваш любимый Hex Editor (я предпочитаю McAfee FileInsight).

И так приступим. Так как разбирать мы будем формат моделей, то идем в директорию gameres/models. Там вы увидите файл models.pak - это самый обычный ZIP-архив, поэтому можете распаковать его куда вам удобно. Внутри этого архива вы найдете много файлов с расширением *.ds2md - это и есть игровые модели, формат которых мы сейчас попробуем разгадать. Давайте возьмем не особо большую модель - я взял для этих целей модель granata.ds2md. Давайте откроем этот файл в Hex-редакторе.

Первое что сразу бросается в глаза - ASCII-строки. Ok, что нам нужно для того чтобы считать строку - естественно нужно знать ее длину, а это значит что перед самой строкой должна находиться ее длина. Проверим нашу догадку - первой видимой строкой в файле идет строка “DS2ModelFile_1”, если мы посчитаем количество символов - получим цифру 14. Теперь глянем что есть перед этой строкой - т.к. эта строка почти в самом начале файла, перед ней только 2 байта, что соответствует размеру слова - если мы прочитаем это слово - то увидим что оно содержит значение 14 - отлично, теперь мы знаем как читать строки в этом файле. Давайте посмотрим какие строки здесь вообще присутствуют. “DS2ModelFile_1” скорее всего дескриптор формата. Далее строка “1.0” - это скорее всего версия формата. Далее “granade_det_hires” - скорее всего название модели. Хм... а далее перед строкой идут 3 байта. При этом 2 байта перед самой строкой - как положено, это ее длина. Тогда что же это за байт? Ладно, потом разберемся, пока просто запомним что после имени модели надо пропустить 1 байт. Далее строка “default_model” - очень похоже на название материала. Далее - “Z:\gameres\textures\models\weapon\wpn_granaten01.tga” - думаю всем ясно предназначение сей строки - это путь к текстуре. Далее 4 раза повторяется строка “notexture” - забегая на перед скажу что просмотрев еще с десяток моделей я я понял что модель может ссылаться на несколько текстур, и как видим - их максимальное количество 5.

Ну вот - строки закончились, и пошли непонятные данные, более похожие на float - числа с плавающей запятой, неужели это вершинны?!? Стоп! А как их читать? Сколько их? Ведь мы же шли читая все подряд, нигде не пропуская. Хм... значит это не вершины, попробуем просмотреть чуть дальше, авось чего прояснится. Ага, начиная со смещения 0xBF можно заметить интересную особенность - далее данные идут неким паттерном - в порядке увеличения, и причем данные идут размерностью 2 байта, то есть словом. Ничего не напоминает? Да это же индексы! А сколько же их? А давайте попробуем найти конец индексных данных - а вот и оно по смещению 0x34C9. Ok, давайте узнаем их количество - отнимем от конечного смещения начальное 0x34C9 - 0xBF = 0x340A (13322). Теперь мы знаем размер вершинного буфера, а нам нужно знать количество индексов, разделим размер буфера на размер одного индекса - 13322 / 2 = 6661. Хм... не может быть такого количества индексов, ибо оно не делится на 3. А! Скорее всего последнее слово - это не индекс а количество вершин! Значит индексов 6660 (0x1A04). Давайте поищем это значение в той каше данных перед индексным буфером - а вот и оно по смещению 0xBB.

Отлично, теперь мы уже можем прочесть индексы модели. Перейдем к вершинам. Как мы уже установили - сразу после индексов идут 2 байта (слово) хранящее в себе количество вершин (в данном случае = 1406). Но какой же формат этих вершин? Ok, давайте для начала предположим что вершины имеют самы стандартный набор - позиция, нормаль и текстурные координаты. Опишем формат вершины как такую структуру:

struct vertex
{
	float px, py, pz;
	float nx, ny, nz;
	float u, v;
};

Теперь используя данную структуру попробуем прочитать пару-тройку вершин (я для этого использовал возможность FileInsight задавать свои структуры). Как вы видите - данные действительно хранятся в виде чисел с плавающей запятой (float), но внимательно просмотрев их содержимое можно прийти к выводу - числа больше напоминают трехмерные координаты. Это точно не нормали (ибо каждая тройка значений, образующая вектор, не соответствует требованию единичности). На текстурные координаты тоже не особо похоже (очень мелкие значения, много отрицательных значений). Значит это позиции вершин. А это наталкивает нас на мысль что атрибуты вершин хранятся не в interleaved виде, а в виде отдельных буфферов. Ну и ладно, давайте прочитаем наши вершины. Мы знаем их количество - 1406, знаем размер данных задающих позицию вершины (3 float, 3 * 4 = 12) значит нам надо прочитать 1406 * 12 = 16872 байт. Читаем их. У нас есть позиции вершин. Смотрим что за данные идет дальше (можно для этого использовать ту же структуру). Ага - видим что дальше тоже идут вещественные числа, и каждая тройка значение образует единичный вектор - отлично, мы нашли нормали. Прочтем их (1406 * 12 = 16872 байт). Теперь смотрим что у нас дальше - еще вещественные числа, и уж больно они смахивают на текстурные координаты, читаем и их (1406 * 8 = 11248). Далее пошли загадочные данные, с виду напоминающие веса костей для скелетной анимации (тем более что за этими данными следуют имена костей). Анимация нас пока не интересует, остановимся на достигнутом.

И так - мы теоретически прочитали геометрические данные модели, достаточные для того чтобы ее отобразить. Давайте проверим это, написав небольшую программку которая будет конвертировать модели в формате *.ds2md в *.obj который понимают большинство современных 3D-редакторов и просмотрщиков. Исходный код я приводить в статье не буду, он приаттачен в архиве в конце статьи.

Вот так выглядит сконвертированный OBJ файл импортированный в 3DS Max. Модель выглядит классно, однако на ней нет текстуры. Все текстуры находятся в директории gameres/textures в файле textures.pak. Распакуйте его куда вам удобно. Внутри вы обнаружите директорию $dds - именно здесь хранятся все текстуры используемые моделями. Тестуры хранятся в формате DDS, а в моделях текстуры значатся с расширением TGA - не обращайте внимание, скорее всего экспортер у разработчиков записывал использованные в материале текстуры, а при сборке ресурсов все текстуры конвертировались в DDS. В любом случае - внутри нашей модели гранаты записана текстура wpn_granaten01 - ищем ее в директории $dds - есть, вот она - wpn_granaten01.dds. Применим ее к нашей модели.

Отлично, значит мы правильно разобрали формат. Думаю что вам не составит труда на основе всего вышесказанного и исходного кода примера написать импортер для какого-нибудь 3D-редактора или самостоятельный просмотрщик. Надеюсь я дал достаточно общих приемов чтобы вы смогли дальше уже самостоятельно вскрывать более сложные форматы, а для примера можете взглянуть на код моей утилиты исследования ресурсов игры Need For Speed: Hot Pursuit по этой ссылке.

PS. Все описанное в статье, а так же исходный код и прилагаемые модель и текстура даны лишь для ознакомления. Автор (то есть я) никоим образом не призывает к незаконному использованию чужих игровых ресурсов или незаконному распространению ресурсов.

Скачать исходный код к статье:
ds2md_to_obj.zip (1431)

  19 Ответов в “Вскрытие игровых ресурсов на примере игры You Are Empty”

  1. Вот это да... отличная статья!!!

  2. Спасибо, как раз нужно разобрать не известный формат и накатать конвертер.

  3. Это в статье всё так легко кажется. На самом деле попробуйте узнать что могут из себя представлять те или иные данные. У автора это получается благодаря обширному опыту.

  4. А не пробовали ли Вы конвертировать уровни в удобоваримый формат?

    • Пробовал, даже получалось, но оставались мелкие нюансы. Если нужны подробности - обращайтесь.

  5. Взгляни пожалуйста на файл из игры https://docs.google.com/file/d/0B9EL6JcIQuyxMV9Gdjk5WTJEdU0/edit Первая игра с которой не извлекли ни одной текстуры.Ответь пожалуйста можно что-нибудь вытянуть

  6. автору уважение и спасибо! очень редко если встретится в инете такая статья. всегда было интересно как создаются программы для вскрытия ресурсов.

  7. iOrange, спасибо за такой разбор. Было бы здорово, если бы вы еще какие-нибудь ресурсы разобрали. Больше не планируется?

    У меня возникли вопрос, может вы идею подкинете:

    1) Пытаюсь вытащить модель из игры, которая создана на старом движке (какой, не могу сказать, извините).

    Создал в 3dsmax куб и экспортировал его в OBJ. Выглядит это дело примерно так:
    v -12.50000 -12.50000 -0.00000
    v 12.50000 -12.50000 -0.00000
    v -12.50000 -12.50000 -25.00000
    --вырезана еще 5 подобных строк ---

    В ресурсах, почему-то эти 8 вершин повторены аж 3 раза. Т.е. не 8 вершин, а 24.

    У вас нет предположений, почему? Где они могут использоваться?

    2) Вопрос по этой же теме:
    Этот куб в OBJ:
    v -12.50000 -12.50000 -0.00000
    v 12.50000 -12.50000 -0.00000
    v -12.50000 -12.50000 -25.00000

    А в ресурсах некоторые координаты перемешиваются. Т.е. примерно так:
    v -12.50000 -25.00000 -0.00000
    v 12.50000 -12.50000 -0.00000
    v -12.50000 -12.50000 -12.50000

    Да еще и некоторые значения меняют свои знаки. Было плюсовое значение, стало минусовое и наоборот.
    Если я вытаскиваю координаты подряд, то получается, что некоторые координаты показывают в одну и туже точку и куб может становится треугольником (отсутствуют грани).

    • по поводу 2 вопроса. Может из-за того, что f 4//4 2//2 1//1 (и т.д.) я взял из оригинального OBJ, а они тоже меняются?

      • Да, вполне. Вершины скорее всего индексированы, хотя если вершин аж 24, то не похоже на индексированные данные.
        Чтобы точнее ответить - нужно видеть данные.

        • 1) У вас будет время посмотреть? Я могу скинуть 2 файла. В первом (244 байта) - имена файлов и скорее всего информация, в каком месте находятся данные. Во-втором файле (2.5 кб) , находится сам куб.

          2) И еще: Никак не пойму эти f в OBJ-файле.
          Вот возьмем вашу гранату:
          f 1/1/1 2/2/2 3/3/3
          f 4/4/4 1/1/1 5/5/5

          Я понял, что эти цифры принадлежат :
          f v/vt/vn v/vt/vn v/vt/vn

          Но что означают сами цифры? Почему они идут по-порядку 1,2,3,4 , а затем хаотично - 1, 5?

          • Блин, задал вопрос и кажется сам могу на него ответить.

            f 4/4/4 1/1/1 5/5/5
            это
            4-я вершина/4-я текст.вершина/4-я нормаль
            1-я вершина/1-я текст.вершина/1-я нормаль
            5-я вершина/5-я текст.вершина/5-я нормаль

            и берутся они соответственно из v, vt,vn. Верно?

  8. Здравствуйте, iOrange!

    Очень интересная статья, но, не могли бы вы продолжить ее, разобрав "загадочные данные, с виду напоминающие веса костей для скелетной анимации (тем более что за этими данными следуют имена костей)," если таковое возможно. Было бы очень здорово :)
    Заранее спасибо!

  9. Неплохой такой урок,а вы когда нибудь доставали анимации или кости из этих моделей?

    • Конкретно в этой игре - нет. Но я так же разбирал множество игр, в том числе и новый Doom

  10. […] Ну продолжим. Теперь возьмемся за OSS. Все начинается не так радужно - file magic в начале нет, значит формат, скорее всего, уже свой (оказалось что нет ) Не буду тут расписывать как я разбирал этот файл, в этом деле у меня богатый опыт, кому интересно как это происходит - можете почитать у меня здесь (http://3d-orange.com.ua/reversing-game-resources-in-you-are-empty/) […]

 Оставить комментарий

(required)

(required)

Вы можете использовать HTML теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

© 2011 3D-Orange.com.ua
e-mail me

3D-Orange.com.ua is proudly powered by WordPress.
Suffusion theme by Sayontan Sinha