Як потрошити ігрові архіви - статті про ігри і опису форматів

У статті, як приклад, описується процес розпакування архіву формату DAT з гри Imperium Galactica II

Припустимо, що вам захотілося добути графіком з якоїсь іграшки. Припустимо також, що ваш вибір припав на Imperium Galactica II. Ті ж правила підійдуть практично до будь-якій грі.

Різні виробники по різному і ресурси ховають. В одному випадку можна знайти текстури, розміщені по окремих файлів, в іншому їх запаковують в архіви, точніше - в псевдоархіви, тому що в них досить рідко застосовуються алгорітни стиснення і в повному сенсі цього слова архівами вони не є. Значить, шукаємо файли, які виділяються своїм розміром на тлі інших. Втім, архіви можуть бути і порівняно невеликими, як в нашому випадку з IG2. Тут в кожному архіві міститься іноді по одному, а іноді і по сотні файлів. Ось цей випадок ми і розглянемо.

При розшифровці будь-якого формату ймовірність успішного результату різко зростає, якщо розбирати одночасно кілька зразків цього формату. У нашому випадку використовується два архіву: sounds_colony_speech_self-destruct.dat і textures_colony_lens.dat.

Тепер звернемося до другого експерементальний архіву і до Малюнку 3. Як ми вже зрозуміли, імовірно зміщення FAT можна прочитати в останніх чотирьох байтах архіву. Щоб остаточно підтвердити це припущення, подивимося на останні чотири байти другого архіву (Малюнок 3). Там, за зміщення $ 5BB2 читаємо значення $ 106. Віднімаємо це значення із загального розміру архіву і отримуємо $ 5AB0. Йдемо цим зміщення і куди потрапляємо? Так, на початок FAT другого архіву. Значить, гіпотеза підтвердилася.

Залишилося тільки розглянути структуру записи про одне з файлів. Ось що містяться в запису поля (в дужках вказана довжина поля):

  1. Файл (довжина довільна).
  2. Зсув в архіві до нього (4 байта).
  3. Мотлох (4 байта).
  4. Розмір даних (4 байта).
  5. Реальний розмір файлу (якщо використаний алгоритм стиснення GZip) (4 байта).
  6. Мотлох (4 байта).

Довжина поля імені може бути в усіх записах фіксованого або довільної. У другому випадку або перед полем вказується довжина імені, або (як у нашому випадку) ім'я читається до першого нульового символу. Решта поля відгадуються експерементально. Розмір першого файлу = Зсув другого-зміщення першого. Саме зміщення можна відгадати. Це особливо легко, в разі якщо всі файли мають один і той-же тип, тобто мають загальний ідентифікатор в заголовку. Наприклад, у другому архіві розглянемо запис першого запису (зміщення $ 5AB0), і про другий (зміщення $ 5ACE). Після імені першого файлу, пропустивши один нульовий символ, читаємо $ 00 00 00 00. Будемо вважати, що це і є зміщення до першого файлу. Йдемо цим зміщення, тобто в початок архіву і запам'ятовуємо хоча б два перших байта - $ 78 9C. Тепер читаємо ті ж чотири символи - $ FF 48 00 00. Йдемо з цього зміщення ($ 48FF) і бачимо ті ж два байта. Т.ч. збігаються ідентифікатори обох файлів, отже поле, що містить зсув файлу визначено вірно. Таким же методом наукового тику визначаються і інші поля.

Наостанок хотілося б приділити увагу ще дещо чого. Іноді файли в псевдоархівах стиснуті абсолютно справжнім архіватором GZip. Цей метод використаний і в IG2, і в HOMM III. Ідентифікатором формату GZip є послідовність $ 1F 8B 08 00 00 00 00 00 00 00. Однак в іграх цей ідентифікатор з невідомої мені причини замінюють на $ 78 9C. Що б при розпакуванні псевдоархіва сформувати повноцінний архів GZip з того файлу, що там зберігається, наприклад, правильно витягти з другого архіву файл flare.bmp (Малюнок 3), потрібно:

  1. Створити файл з ім'ям Flare.bmp.
  2. Записати туди справжній ідентифікатор GZip, тобто $ 1F 8B 08 00 00 00 00 00 00 00.
  3. Копіювати в створений файл дані Flare.bmp з псевдоархіва. починаючи з третього байта (щоб відсікти помилковий ідентифікатор GZip).
  4. В кінці Flare.bmp вписати Реальний розмір файлу. Він включений в запис FAT.

Маневр з «хибним заголовком» GZip справедливий в тому, і тільки в тому випадку, коли ви використовуєте бібліотеку GZipLib або інші бібліотеки того ж класу. При використанні ZLib заголовок редагувати не треба.