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

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


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

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

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

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

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

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

Войти

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

Войти сейчас


  • Сейчас популярно

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

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

    • всё просто игра никому не интересна для модинга
    • Ну с таким подходом можно вообще всю свою жизнь превратить в постоянные придирки ко всему  Все это — художественные произведения и везде нормально — что-то допускать и закрывать глаза на некоторые шероховатости. К ужасу Экспанс совсем не близок, так как он пытается как раз объяснить и продумать многие моменты. Какие-то косячки и вопросы к некоторым моментам — да, конечно. Но как цельное произведение оно очень неплохо занимает свою нишу действительно годной научной фантастики. Мне очень нравится базовая концепция, что произведение уделяет внимание не только политическим, человеческим отношениям, не только боевичковой части но и то, как мог бы, приближенно к реальности, действительно выглядеть и функционировать подобный мир. И в произведении огромное количество таких интересных мелочей, за что и стоит его ценить) Я после сериала еще и весь цикл книг прочел, так что задуматься в процессе чтения можно было о многом) Другое дело — хочется ли ругать или придираться к каждой мелочи просто потому, что авторы хотя бы попытались как-то это интересно описать, а не просто сказали — вот это так работает примите это и дальше наслаждайтесь пиу-пиу — в Экспанс — совершенно не хочется) Кстати, забавный факт — сериал по Экспанс довольно точная экранизация книг) Я, как прочитавший всю серию, от сериала как раз получил даже больше, чем ждал) С Властиленом Колец всеж таки было немного не так — там экранизация прилично так отходили местами от книг и я очень хорошо помню жаркие споры после сеансов каждой части — что сделали не так и как надо было, от читавших серию Ну и, если вдруг — я считаю экранизацию ВК просто отличной, да и на выходе считал) А все потому, что оттуда убрали дурацкого Тома Бомбадилла  (если что, шутка, но мне правда не нравилась эта часть в книгах). В Экспанс наверняка будет дофига таких отношений, там это в основах книг заложено - Астеры, живущие большими семьями да и вообще идея общего и целого, без различий и полов  Но это не повесточка и пропаганда — потому это не проблема) У Совокотов все игры так сделаны, а не тольтко эта. Полная локализация только на одном языке — английском (да и то в предыдущих cRPGшках — даже английская озвучка не полная, так как килотонны текста). Субтитры на русском на месте. Продается в ру-регионе. Делаем выводы?)) Ну и что лучше — сделать полную локализацию на несколько языков или прописать более обширные диалоги (которые трудозатратно озвучивать из-за их объемов) и сделать глубже какие-либо игровые механики? Как по мне — второе очевидно выигрывает для ролевой игры.
    • Вобщем как видим все равно было не  быстро как думаете Clockwork Revolution  добавят русский ? Там явна заглушка один язык только в списке.
    • Причем тут свечка, болезный, не твою личную жизнь обсуждаем.
    •                                                                                                       Русификатор                                                                                             Версия: GOG 1.0.3.4089 
                                                           Steam: у меня нету в наличии новой версии, как будет сделаю
                                                           Установка: Закинуть папку из архива в основную папку игры, выставить                                                                   Английский в меню прям, так и будет на русском написано)
                                                           p.s: Нейроперевод с полной вычиткой и исправлениями, переведены почти                                                             все текстуры которые нашёл, особенно важные, парочку будет на англ.                                                                                               Workupload | Boosty  
    • Я себе это смутно представляю.
    • Да, еще был Пентимент. Там ситуация была уникальная, сам Фил же обращение русским геймерам записал, xbox news на русском открыли, сразу начали делать локализации.
    • Итак, может ли кто-нибудь открыть файлы игры и получить то, что нужно перевести?  Насколько я понимаю, многие люди с этим раньше не сталкивались... Потому что я пока не могу найти решение.
  • Изменения статусов

    • Дмитрий Соснов  »  Tirniel

      Привет! ты разбираешься в компьютерном железе, сможешь помочь с советом по апгрейду старого компа?
      · 1 ответ
    • SHAMAH

      Куда вход на сайт убрали и ЗАЧЕМ? Хотел файл скачать, там только медленная загрузка и “зарегистрируйтесь”. Все. Пришлось вручную страницу входа прописывать.
      · 0 ответов
    • Nosferatu  »  behar

      Добрый вечер.
      Подскажите пожалуйста, у вас не осталось случайно исходников для фикса на широкоформатные мониторы для игры Vampire The Masquerade Redemption?
      Если да, то не могли бы вы ими поделиться, а если нет, то прошу прощенья что побеспокоил.
      Заранее спасибо.
      · 0 ответов
    • AlcoKolyic  »  makc_ar

      Здраствуйте! Извините, а можно попросить ссылку на место где можно взять перевод (патч или образ игры с переводом) El Shaddai: Ascension of the Metatron для ps3, пожалуйста? А то в теме к этой игре у меня не получилось найти работающие ссылки… Первая ведет в группу в которой удалены большинство постов, а пост с этой игрой ведет на сайт https://psnext.ru который сейчас не имеет отношения к видеоиграм. 
      · 0 ответов
    • oleg72  »  Boor

      https://www.skidrowcodex.net/fate-reawakened-goldberg/
      · 0 ответов
  • Лучшие авторы


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

×