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

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


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

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

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

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

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

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

Войти

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

Войти сейчас


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

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

    • Вот оно как, оказывается мы просто неправильно играли, а на деле всё "просто". Фанаты такие фанаты. И не важно о какой игре речь, любая даже самая кривая механика по их мнению канон, а те кто считают иначе криворучки и казуалы.  
    • @faviner Всё, такое сочетание форматирующих символов не видел, поэтому мой валидационный скрипт не справился. Причем оно используется во всем тексте буквально что-то около 5-6 раз. Исправил, проверяй.

      Алсо. Пока выдираю весь игровой текст полностью с учетом тех кто говорит в диалогах, и переписываю инструментарий под себя. Хочу заново перевести, но уже с соблюдением глоссария, полов говорящих, контекста диалогов, игровой графики(вот это если меня лень не одолеет). Посмотрю насколько лучше будет результат и будет ли он стоить всех моих запар.
      Кто бы мог подумать, что меня будет так бесить это несоответствие пола говорящего в тексте. Еще хотелось бы избавиться от формальной речи там где это не нужно, но боюсь это будет что-то из разряда фантастики. Это для человека будет очевидно, а вот для нейронки сомнительно. Впрочем, посмотрим.
    • @ivdos есть еще текст селин
      (Э-это сейчас неважно. Просто пойдём.)
      после этого если поговорить с валимаром будет текст (валимар мирно спит)
    • @faviner точный текст нужен перед вылетом. Желательно еще пару фраз перед этим. А еще лучше сохранение.
    • Тема сисек не раскрыта! Сиськи вроде бы и есть но какие то “не вразумительные”, похожие как у Алисии Викандер
    • Да. Тоже в пятницу вечером посмотрел, 2 часа прям незаметно пролетели, люблю такое: мистика, тайна, пропажи, заговоры. Бодро очень смотрится.  Насколько знаю от этого режиссёра был еще фильм Варвар, надо тоже глянуть наверное.  П.С. нарвался тут на постер Кайдзю #8 и возникла мысль посмотреть первый сезон, а там если зайдет и 2 на подходе. 
    • Перевод на русский с использованием нейросети + правки по тексту + шрифты. Большую помощь в создании русификатора оказал  Chillstream за что больше спасибо) Авторы: Я: нейроперевод, правки, перенос текстов и шрифтов на более новые версии.  Chillstream : создание шрифтов, упаковка ассетов, помощь в решении технических нюансов. О переводе: Постарался сделать полноценный перевод, но игру не проходил. Могут быть проблемы с родами, обращениями(ты/вы), унификацией терминов, выражений, но в целом было внесено много правок, поэтому недочёты не должны повлиять на погружение в мир игры. Перевод включает себя недавно вышедшее DLC. Установка: Содержимое архива скопировать в основную папку игры, предложит заменить, нажимаете да.
      Скачать: Google | Boosty Версия игры: V1.2.1(19609941-19820955 build)  
    • Ребят расходимся там есть перекаты это же опять Сосалик! фу буу бэ. 
    • Во, теперь карабкайся на самый верх кучи и начинай разгребать ее, пока не найдешь золотой черепок, этот черепок дает +100% к урону и хп героя, а если ты с этим черепком дойдешь до конца игры, разрабы лично приедут и привезут тебе его реальную копию + еще подарят такой же костюм как у гг и такой же дрон, так что давай дерзай!
  • Изменения статусов

    • Дмитрий Соснов  »  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 | Реклама на сайте.

×