Перейти к содержимому
Zone of Games Forum
KosmoRei

Помогите окончательно разобраться в структуре HD6 + DAT (Rogue Galaxy PS2)

Рекомендованные сообщения

Нужно перепаковывать главные архивы Rogue Galaxy с изменением размеров внутренних файлов. Используется пара HD6 (TOC) и DAT (просто файлы друг за другом).

Вроде, похожий формат в Dragon Quest 8, но общедоступных инструментов нет.

За неимением общедоступного адекватного рабочего репакера, пришлось разбираться самому. Плагин к xpert вообще непонятно, как работает (и работает ли?) (жамкаю rebuild файла, а он создается точно таким же, каким и был).

Взял IDA pro, посмотрел в нем на логику работы плагина (извлекать файлы он умеет, по крайней мере). Многое что понял по структуре HD6, однако самое важное до сих пор неясно.

Наверное, япошкам действительно доплачивают за примененные извращения.

 

Ладно, начнем… Файл HD6 состоит из трех блоков.

  1. Блок с уникальными кусочками названий файлов и папок… Типа так они их пожали, чтобы сэкономить место на схожих названиях.
  2. Блок с информацией о том, как собрать название файла из кусочков
  3. Блок с 8-ми байтовыми дескрипторами хранящихся в DAT файлов. Сколько файлов, столько и дескрипторов.

Дескрипторы третьего блока состоят трех частей (это я понял из IDA)

2 байта — ну… вероятно… это… пи-и-и-и-и-и-и, в общем, они определяют как адрес начала цепочки, из которой собирать имя файла во втором блоке, так и влияют на адрес файла в DAT.

3 байта — через хитро закрученную жопу определяют адрес начала файла в DAT.

3 байта — через нехитро закрученную жопу определяют размер файла в DAT.

//--

Итак… 3-мя байтами можно максимум представить число 16 777 215, а DAT больше этого размера. Соответственно, чтобы представить адреса начал файлов, нужно извращаться…

Первые два байта дескриптора постоянно возрастают (от 00 00 до ~FF FF), как только они достигли ~FF FF, они обнуляются, а в коде начинают работать костыли. Там имеется счетчик, сколько раз был перескок от ~FF FF к 00 00.

//--

Пока счетчик перескоков равен 0, адрес файла в DAT вычисляется ТОЛЬКО так:

ulong temp_file_start_addr = (ulong)(16 * ЧИСЛО, СОБРАННОЕ ИЗ ТРЕХ БАЙТОВ, А ЧЕТВЕРТЫЙ ЗАДАЕТСЯ, КАК 00) / 8

А адрес цепочки кусочков названия файла во втором блоке вычисляется ТОЛЬКО так:

uint temp_file_name_pieces_start_addr = ЧИСЛО, СОБРАННОЕ ТОЛЬКО ИЗ ПЕРВЫХ ДВУХ БАЙТ (ushort) + адрес начала второго блока (вот это поворот!!! используются все биты первых двух байт)

//--

Когда счетчик перескоков становится больше 0, то в дополнение к извращениям выше, происходит следующее:

temp_file_start_addr -= "количество перескоков << 9";
temp_file_name_pieces_start_addr = "количество перескоков << 16" + ЧИСЛО, СОБРАННОЕ ТОЛЬКО ИЗ ПЕРВЫХ ДВУХ БАЙТ (ushort) + адрес начала второго блока

//--

Вот это двоякое назначение первых двух байт выносит мне мозг. Это вообще жесть какая-то. Выходит, что положение файла в DAT связано с положением цепочки кусочков названия файла в HD6? И как мне перестраивать это дело? Вот захочу я увеличить размер файла в середине… и… как вычислять значение первых двух байт в дескрипторе для файла, следующего за этим увеличенным? Ведь они влияют еще и на адрес начала цепочки кусочков его названия.

В общем, кто чем может, помогите… Как перепаковать архив с изменением размеров хранимых файлов?..

//--

Я перепрогал на шарп то, что увидел в IDA, результат (temp_hd6_dump.txt https://www.dropbox.com/s/ma1lwzpndzbv6j3/temp_hd6_dump.txt?dl=1) аналогичен результату плагина от xpert.

Структура файла temp_hd6_dump.txt такая:

00000000|00016540|0002660A|00000000|chr_lock.chr
00016800|00120250|00026612|00000000|font16.img
00137000|0005A5E0|0002661A|00000000|abi\c001_abi01.pak

адрес начала файла в DAT | размер файла | адрес трех байт в HD6, задающих адрес начала файла в DAT | количество перескоков от ~FF FF к 00 00 | собранное из кусочков имя.

Вот экземпляр файла HD6 https://www.dropbox.com/s/z9hjdzvtvnuqf1n/DATA0_0.HD6?dl=1 Сам гиговый DAT нужен вряд ли, ибо все адреса файлов в нем и их размеры указаны в temp_hd6_dump.txt.

class HD6
    {
        public string FilePath;
        public byte[] Data;

        public uint FirstBlockStartPos { get { return BitConverter.ToUInt32(this.Data, 4); } }
        public uint FirstBlockSize { get { return BitConverter.ToUInt32(this.Data, 8); } }
        public uint FirstBlockPiecesCount { get { return BitConverter.ToUInt32(this.Data, 12); } }
        public uint SecondBlockStartPos { get { return BitConverter.ToUInt32(this.Data, 20); } }
        public uint SecondBlockSize { get { return BitConverter.ToUInt32(this.Data, 24); } }
        public uint FilesCount { get { return BitConverter.ToUInt32(this.Data, 36); } }
        public uint ThirdBlockStartPos { get { return BitConverter.ToUInt32(this.Data, 40); } }
        public uint WholeHD6Size { get { return BitConverter.ToUInt32(this.Data, 48); } }

        public HD6(string file_path)
        {
            this.FilePath = file_path;
            this.Data = File.ReadAllBytes(this.FilePath);

            FileStream hd6_fs = new FileStream(this.FilePath, FileMode.Open, FileAccess.Read);

            uint curr_pos_in_third_block = this.ThirdBlockStartPos;
            byte lowest_byte = 0;
            byte prelowest_byte = 0;
            byte posthighest_byte = 0;
            byte highest_byte = 0;
            uint ushort_from_first_2_bytes = 0;
            uint ushort_from_first_2_bytes_copy = 0;
            uint prev_step_ushort_from_first_2_bytes = 0;
            int two_bytes_overflow_count = 0;
            uint processed_files_count = 0;

            List<string> lines_to_write = new List<string>();

            do
            {
                hd6_fs.Position = curr_pos_in_third_block;
                lowest_byte = (byte)hd6_fs.ReadByte();
                prelowest_byte = (byte)hd6_fs.ReadByte();

                posthighest_byte = 0;
                highest_byte = 0;
                ushort_from_first_2_bytes = BitConverter.ToUInt32(new byte[] { lowest_byte, prelowest_byte, 0, 0 }, 0);
                ushort_from_first_2_bytes_copy = ushort_from_first_2_bytes;

                if (ushort_from_first_2_bytes < prev_step_ushort_from_first_2_bytes)
                    two_bytes_overflow_count++;

                long file_descriptor_start_addr_in_hd6 = hd6_fs.Position;
                lowest_byte = 0;
                prelowest_byte = (byte)hd6_fs.ReadByte();
                posthighest_byte = (byte)hd6_fs.ReadByte();
                highest_byte = (byte)hd6_fs.ReadByte();

                uint test_val_from_3_bytes = BitConverter.ToUInt32(new byte[] { lowest_byte, prelowest_byte, posthighest_byte, highest_byte }, 0);
                ulong temp_file_start_addr_in_dat_as_in_bytes = (ulong)16 * BitConverter.ToUInt32(new byte[] { lowest_byte, prelowest_byte, posthighest_byte, highest_byte }, 0) / 8;
                //ulong temp_file_start_addr_in_dat_as_in_bytes = BitConverter.ToUInt32(new byte[] { lowest_byte, prelowest_byte, posthighest_byte, highest_byte }, 0) >> 7 << 8; //мой эксперимент, результат аналогичен строке выше
                ulong temp_file_start_addr_in_dat = temp_file_start_addr_in_dat_as_in_bytes;

                if (two_bytes_overflow_count > 0)
                {
                  temp_file_start_addr_in_dat -= (ulong)(two_bytes_overflow_count << 9);
                  ushort_from_first_2_bytes_copy = (uint)((two_bytes_overflow_count << 16) + ushort_from_first_2_bytes);
                }
                
                lowest_byte = 0;
                prelowest_byte = (byte)hd6_fs.ReadByte();
                posthighest_byte = (byte)hd6_fs.ReadByte();
                highest_byte = (byte)hd6_fs.ReadByte();

                uint temp_file_size_in_dat = BitConverter.ToUInt32(new byte[] { lowest_byte, prelowest_byte, posthighest_byte, highest_byte }, 0) >> 4;
                long Offset = ushort_from_first_2_bytes_copy + this.SecondBlockStartPos;
                hd6_fs.Position = ushort_from_first_2_bytes_copy + this.SecondBlockStartPos;
//Начиная отсюда я вообще не понимаю, как происходит сборка имени файла, да и не очень-то это важно
                string assembled_file_path = string.Empty;
                int v32 = 0;
                int v27_possible_str_to_read_length = 0;
                int v35 = 0;

                do
                {
                  byte second_block_first_byte = (byte)hd6_fs.ReadByte();
                  byte second_block_second_byte = 0;
                  v27_possible_str_to_read_length = second_block_first_byte;

                  if (second_block_first_byte == 0)
                    break;

                  if (second_block_first_byte >= 128)
                  {
                    second_block_second_byte = (byte)hd6_fs.ReadByte();
                    v27_possible_str_to_read_length = second_block_first_byte + (second_block_second_byte << 8);
                    byte v28 = (byte)(second_block_second_byte % 2);

                    if (v28 == 0)
                    {
                      v35 = ((second_block_second_byte >> 1) + 1) << 8;
                      v27_possible_str_to_read_length = v27_possible_str_to_read_length + 128 - v35;
                    }

                    if (v28 > 0)
                    {
                      v35 = (second_block_second_byte + 1) / 2 << 8;
                      v27_possible_str_to_read_length = v27_possible_str_to_read_length - v35;
                    }

                    Offset++;
                  }
                  
                  hd6_fs.Position = this.FirstBlockStartPos;
                  uint curr_read_str_bytes_count = 0;

                  do
                  {
                    int temp_read_byte = hd6_fs.ReadByte();

                    if (temp_read_byte == 0)
                      curr_read_str_bytes_count++;
                  }
                  while (curr_read_str_bytes_count < v27_possible_str_to_read_length);

                  byte v81 = 0;

                  do
                  {
                    v81 = (byte)hd6_fs.ReadByte();
                    if (v81 == 47)
                      v81 = 92;
                    if (v81 >= 253)
                      v81 = (byte)(v81 - 26);
                    if (v81 >= 227)
                      v81 = (byte)(v81 - 26);
                    if (v81 >= 201)
                      v81 = (byte)(v81 - 26);
                    if (v81 >= 175)
                      v81 = (byte)(v81 - 26);
                    if (v81 >= 149)
                      v81 = (byte)(v81 - 26);
                    if (v81 >= 123)
                      v81 = (byte)(v81 - 26);

                    if (v81 > 0)
                        assembled_file_path += Encoding.ASCII.GetString(new byte[] { v81 });
                  } while (v81 > 0);
                    
                  —v32;
                  hd6_fs.Position = ++Offset;
                } while (v27_possible_str_to_read_length > 0);

                curr_pos_in_third_block += 8;
                prev_step_ushort_from_first_2_bytes = ushort_from_first_2_bytes;
                processed_files_count++;

                string file_line = temp_file_start_addr_in_dat.ToString("X8") + "|" + temp_file_size_in_dat.ToString("X8") + "|" + file_descriptor_start_addr_in_hd6.ToString("X8") + "|" + two_bytes_overflow_count.ToString("X8") + "|" + assembled_file_path;
                lines_to_write.Add(file_line);
              }
              while (processed_files_count < this.FilesCount - 1);

            hd6_fs.Close();
            File.WriteAllLines(Application.StartupPath + "\\temp_hd6_dump.txt", lines_to_write, Encoding.UTF8);
        }
    }

64ee5e21d5dbfd2de69ccb64b04ca6bc.png

3d0d7269c5562fc25f1169c60f7bc4e6.png

//----

На ночь глядя появились идеи, возможно, я со всем разобрался, жаль тему нельзя удалить самому.

Похоже, это мое “Выходит, что положение файла в DAT связано с положением цепочки кусочков названия файла в HD6?” утверждение и это “как вычислять значение первых двух байт в дескрипторе для файла, следующего за этим увеличенным? Ведь они влияют еще и на адрес начала цепочки кусочков его названия.” неверны. Первые два байта в дескрипторах файлов трогать вообще не нужно. Просто делать обратные извращения с делением на 16, умножением на 8 и, невесть зачем, начиная с определенного файла, плюсовать к получившемуся адресу  количество перескоков « 9”

//---

Репакер сделал, тема неактуальна.

Изменено пользователем KosmoRei

Поделиться сообщением


Ссылка на сообщение

Создайте аккаунт или войдите в него для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать аккаунт

Зарегистрируйтесь для получения аккаунта. Это просто!

Зарегистрировать аккаунт

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас



Zone of Games © 2003–2024 | Реклама на сайте.

×