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

KosmoRei

Новички+
  • Публикации

    13
  • Зарегистрирован

  • Посещение

Репутация

2 Нейтральная

О KosmoRei

  • Звание
    Новичок

Посетители профиля

1 299 просмотров профиля
  1. Нужно перепаковывать главные архивы Rogue Galaxy с изменением размеров внутренних файлов. Используется пара HD6 (TOC) и DAT (просто файлы друг за другом). Вроде, похожий формат в Dragon Quest 8, но общедоступных инструментов нет. За неимением общедоступного адекватного рабочего репакера, пришлось разбираться самому. Плагин к xpert вообще непонятно, как работает (и работает ли?) (жамкаю rebuild файла, а он создается точно таким же, каким и был). Взял IDA pro, посмотрел в нем на логику работы плагина (извлекать файлы он умеет, по крайней мере). Многое что понял по структуре HD6, однако самое важное до сих пор неясно. Наверное, япошкам действительно доплачивают за примененные извращения. Ладно, начнем… Файл HD6 состоит из трех блоков. Блок с уникальными кусочками названий файлов и папок… Типа так они их пожали, чтобы сэкономить место на схожих названиях. Блок с информацией о том, как собрать название файла из кусочков Блок с 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); } } //---- На ночь глядя появились идеи, возможно, я со всем разобрался, жаль тему нельзя удалить самому. Похоже, это мое “Выходит, что положение файла в DAT связано с положением цепочки кусочков названия файла в HD6?” утверждение и это “как вычислять значение первых двух байт в дескрипторе для файла, следующего за этим увеличенным? Ведь они влияют еще и на адрес начала цепочки кусочков его названия.” неверны. Первые два байта в дескрипторах файлов трогать вообще не нужно. Просто делать обратные извращения с делением на 16, умножением на 8 и, невесть зачем, начиная с определенного файла, плюсовать к получившемуся адресу “количество перескоков « 9” //--- Репакер сделал, тема неактуальна.
  2. Спасибо, поразбираюсь… Однако после такой распаковки отличий с файлом из RAM чуть больше, чем половина размера файла. Что-то не так со ссылками. Ну я еще посоображаю… Имена переменных крайне неиформативные. int init_chr = '\0'; //так, хотя бы, первые 138 байт файлов совпадают.
  3. Скажи, куда указывают ссылки, сколько на них выделено битов, если не 12? Я прогал lzss однажды, так там нифига не как здесь. Здесь контрольные байты, а там были контрольные биты, чье значение определяло то, что сразу за ним — дословный байт или ссылка.
  4. Есть сжатые файлы игры Dragon Quest 5 для PS2. Хочу сделать порт русского перевода с NDS. Выдрал из RAM распакованный вариант и сравнивая с запакованным выяснил все, что опишу ниже здесь.В сжатом потоке периодически расставлены контрольные байты (КБ), а их биты значат, чем являются следующие 8 сущностей (С1 - С8) (дословными байтами (1 байт) или ссылками (2 байта)).Пример: КБ С1 С2 С3 С4 С5 С6 С7 С8 КБ С1 ...С8 КБ ...Т. е. байт FC (11111100) значит, что у нас сразу за ним идет две двухбайтовых ссылки одна за другой, а затем 6 дословных байт. А затем следующий контрольный байт.Я описал все, что мне известно об этом алгоритме на картинке. И прикладываю сжатый файл и распакованный, который я выдрал из RAM.Сжатый файл: https://www.dropbox.com/s/bw53z2qp9qsw2n1/cdf3b.chaindata.lz?dl=1Распакованный файл: https://www.dropbox.com/s/hd0hvbnaevszh76/cdf3b.chaindata.lz_decompr?dl=1Проблема в том, что непонятно куда указывают ссылки. Первая невесть куда указывает. Вторая тоже. Остальные чуть поближе. Судя по последовательностям, на которые они указывают, они берутся не из воздуха, а именно из того, что было уже распаковано с помощью дословных байт. Но непонятно, как с помощью таких больших адресов и при малом количестве распакованных байт что-то работает.Было бы неплохо, если бы кто-то помог добить этот алгоритм... Ассемблера я не знаю.
  5. Вроде, у киви комиссия за вывод? Надо ж не только собрать )
  6. И, вообще, как это лучше делать? Сча я просмотрел несколько тем, всюду указаны яндекс деньги и веб мани. Неужели этим кто-то пользуется? Сейчас карты есть почти у всех, перевод сделать проще и без комиссий. Однако, может есть какие-то подводные камни при сборе денег на карту? Ну мало ли, выследят и поймают за незаконную деятельность Или карту заблокируют из-за множества переводов от разных лиц. В общем, что вы скажете?
  7. Final Fantasy 13-2

    Эпичный перевод вопроса о том, какая из легенд о прорицателях не передавалась из поколения в поколение. Ну а это…
  8. Куплю вам любые игры в steam со скидкой 20%

    Начиная с июня купил уже 9 игр, последние .hack, get even и tokyo dark. Все честно и быстро.
  9. Magna Carta: The Phantom of Avalanche

    Наверное, никто 10 лет минимум не берется за ее перевод, потому что переводчика с корейского фиг найдешь... По этой же причине, небось до сих пор нет и полного английского перевода.
  10. Life Is Strange

    Извините, что не прочитал все 95 страниц темы, может там и есть ответ, но что делать, если после русификации некоторые системные надписи так и остались на английском? В частности предупреждения о совпадениях с реальным миром и эпилепсии на русском, а вот про автосохранение уже на английском. Press any button - на английском. В геймплее надписи при сохранении игры на английском. Ставлю на стим версию для винды. Делал проверку целостности хеша и снова ставил русик - нифига не изменилось.
  11. Rogue Galaxy

    Текст сообщения утерян.
  12. Rogue Galaxy

    Эта команда давно развалилась, там остался лишь Курой Кенсай, который пилит Сферу Одина уже года 4 минимум. Они много чего собирались переводить: и персону 3, и паразитку 2. Только получилось, что персону 3 уже почти перевели на Тагтим, а паразиткой 2 занят я и калаш48, в то время как они даже не начинали. Ждать какой-либо помощи от них нет смысла, им уже много лет не до переводов.
  13. Rogue Galaxy

    Rogue Galaxy — научно-фантастическая японская ролевая игра разработанная японской студией Level-5 и изданная Sony Computer Entertainment эксклюзивно для PlayStation 2. Изначально игра была выпущена только в Японии 8 декабря 2005 года. Википедия Дата выпуска: 8 декабря 2005 г. Гейм-дизайнер: Акихиро Хино Издатель: Sony Interactive Entertainment Жанр: Action/RPG Разработчики: Level-5, SCE Japan Studio Платформа: PlayStation 2 С начала перевода прошло уже три года, за это время переведен весь сюжет и 50% системных текстов. Все инструменты, нужные для перевода уже готовы. Скрины с первой успешной замены текста в игре здесь: http://zerolabs.somee.com/rg_translation.html
  14. FINAL FANTASY 13

    У кого-нибудь есть опыт русификации стим версии игры? У меня была обновленная версия фф13, установил на нее русик, запустил игру через стим, он вдруг принялся проверять ее целостность, хотя на другие стимовские игры я ставил русики и все было норм. P.S. Со второго раза русифицировалась. И вот еще что-то странное на скрине написано... Будто я вернулся в далекие двухтысячные.
Zone of Games © 2003–2024 | Реклама на сайте.

×