Перейти к содержимому
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

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


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

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

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

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

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

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

Войти

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

Войти сейчас


  • Продвигаемые темы

  • Последние сообщения

    • а помните какая на этом сайте течка была по этой игре, все рассказывали что игра будет пушка-бомба-вышка, сталкер сосёт, никчёмная игра в сравнении с PIONER. Но что-то пошло не так, очередная смута.  Видимо теперь будут писать что “ВОТ, КАК ВЫЙДЕТ С РАННЕГО ДОСТУПА, ТОГДА ОНА ПОРВЁТ ВСЕ ПОСТАПОК ИГРЫ!!!111” Верим и ждём
    • Давайте поздравим ребят, они долго к этому шли,и воот релиз..900 онлайн, это ли не успех при 500к вишлистов   
    • Нейрозвучка лучше чем вообще нечего. Нельзя сравнить нейроозвучку с двухголосной озвучкой которой … нету. Можно обсудить качество озвучки, когда есть выбор из нескольких озвучек. Я понимаю, что у переводчиков может гореть пятая место и злить нейрозвучка, так как это угроза для их бизнеса. И что бы они не говорил, не мало людей которым вполне достаточно уровня — нейрозовучки и даже больше скажу, есть не мало людей которые даже не смогу отличить нейрозвучку от реальной. да даже я сам в последнее время часто не понимаю переведённое видео на youtube - это живой перевод или нейронка. А говорить что это “помои на уши” это манипуляция с использованием аналогии, вообще аналогия это хороший инструмент для манипуляции. Я лучше тогда выберу “помои на ушли”, чем пропускать сюжет игры, из за отсутствии “помоев на ушах”.
       
    • Двухголосный закадр от какого нибудь кубика в кубе будет значительно лучше чем любой "дубляж" от нейронки. Кривые ударения, странные паузы, растянутые слова, резкая смена тона голоса, все это никуда не пропало.  Это не то что средний терпимый уровень, это очень низкое качество, если обычный синтезатор голоса можно оценить как 1/10, то озвучка от нейронки 2/10. Даже гнусавая одноголоска получше будет 
    • Начал новую игру (чистая установка), тупо вылетает перед встречей с сенатором.
    • @x_ander82  “5 минут, Турецкий!”
    • Свершилось! Вот с этим всем версия 1.0 и должна была быть наверное, - сделали всё и забыли. В любом случае спс им и "спонсорам", теперь можно и поиграть.
    • Ну да, так и поверили.Почему-то кажется выкатили постановочный CG ролик для подогрева, а на деле там и близко такого не будет.
    • А сколько всего питонов в таком случае? Начинаю подозревать, что между 4-м и 87-м было ещё немало. 38 игр, на 28 больше, чем в прошлом году. 208 ачивок в 18 играх, 27 редкие. 2% новинки. Теги в стиме настолько упоротые, что из-за того, что я много играл в но ман скай (скорее всего), у меня самый веский тег в диаграмке — “симулятор колонии” О_о Ну есть там опционально управление поселением, но для галочки же. Откуда ещё тег мог взяться из игранного даже хз. Откуда тег “автоматизации” — вообще не представляю, ни одной игры подобной не запускал, ну разве что десяток игр на карточки потрошил, мб там что-то такое было.
  • Изменения статусов

    • АлинаQT

      Играю в СТИМовскую версию. Столкнулась с непроходимым багом: при входе в Клуб капитанов игра вылетает на Рабочий стол и закрывается. Собрала 6 ингредиентов из 7, нужен мел. Но не могу войти. Пробовала войти на английской версии без русификатора, все равно вылетает и закрывается. Есть решение для этой проблемы? Буду рада любой помощи.
      · 0 ответов
    • Jimmi Hopkins  »  SerGEAnt

       
      Добавлена ONLINE версия. Проверено в steam — достижения, поддержка по сети.
      · 0 ответов
    • fevralski  »  SerGEAnt

      привет, есть что по хмурому?
      · 0 ответов
    • Netulogina  »  SerGEAnt

      Здравствуйте.
      Назрел такой вопрос. Существует ли архив/резервная копия переводов которые хранятся на данном сайте?
      · 5 ответов
    • Jimmi Hopkins  »  SerGEAnt

      Добавились версии для Linux и MAC OS
       
      · 0 ответов
  • Лучшие авторы


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

×