image

Всем привет! Так уж выдалось, но я являюсь ромхакером в команде Russian Studio Video 7, которая когда-то занималась фанатскими переводами игр на наш великий и могучий. И, в одно время, мы пытались взломать множество игр, либо для того, чтобы перевести самим, либо на заказ, чтобы их перевели другие команды. И так уж получилось, что собралась куча проектов, которые мы начинали взламывать и взламывали до конца, либо забрасывали по причине лени или потери интереса.

О таких проектах я бы и хотел рассказать, запустив, так сказать, новую рубрику “Взломать, чтобы перевести”. И первая игра, попавшая в рубрику, будет культовая Resident Evil 4 на PS3.

Предисловие


Данная рубрика предполагает рассказ о устройстве либо какого-то определённого формата данных, либо о разборе вовсе всей игры для перевода.

В зависимости от игры будет рассказываться взлом либо каких-то определённых форматов данных, которые не были взломаны до сих пор, либо полный разбор игры для перевода.
Надеюсь, эта информация поможет кому-либо в будущем.

Об Игре


Если верить игровым критикам, да и самим игрокам, то Resident Evil 4 вышла весьма культовой игрой. Новый сеттинг, интересный сюжет, необычный подход к игре жанра Survival Horror сыграл свою роль. Многие до сих пор считают, что Resident Evil 4 считается одной из лучшей игр серии.

Чего греха таить, когда я был слишком юн, то и я считал, что игра весьма прекрасна. После опыта Resident Evil на Sony Playstation 1, Resident Evil 4 вызывала много эмоций и в плане графики, и в плане геймплея. Она, в каком-то смысле, совершила революцию в жанре Survival Horror.

Но спустя время я вырос, оценил классические Resident Evil на Sony Playstaion 1, оценил Resident Evil 4. И, как ни странно, примкнул к любителям классических резидентов.
Изначально Resident Evil 4 вышла в начале 2005 года на игровую консоль Nintendo Gamecube и позже был портирована на Sony Playstation 2, Nintendo Wii и PC. И, до начала выпуска псевдо-HD ремастеров, версии для GameCube\Wii считались лучшими по графике, ибо данные платформы в разы были мощнее, чем Playstation 2.

К сожалению, версия игры для PC была портирована с урезанной по графике\эффектам версии для PlayStation 2. И переведена она была только для этих платформ. А про GameCube\Wii все забыли. Не в плане игры, а в плане перевода на наш великий и могучий. Возможно, повлияла малая популярность этих консолей на нашем рынке, но есть ещё один фактор, который мешает перевести эту игру для GameCube\Wii на русский язык, о котором будет сказано позже. И к сожалению, данный фактор влияет и на возможность перевода этой игры на PlayStation 3 в псевдо-HD варианте.

Первая проблема перевода


Но всё же, рассказ идёт про версию игры для Playstation 3.

И данная версия игра имеют одну особенность в основных игровых данных, мешающая переводу игры – контрольная сумма в основных архивах.

Если изучить игры Capcom на Playstation 3, явно требующие перевода, то мы видим одну и ту же проблему – контрольная сумма в игровых архивах. Она встречается не только в Resident Evil 4, но и в так же всеми любимыми среди фанатов Resident Evil: Code Veronica. И самое интересное то, что контрольная сумма в игровых архивах встречается только на консоли PlayStaion 3. Именно из-за этого многие проекты Capcom так и не были переведены на русский язык для Playstation 3.

К сожалению, от игры к игре, алгоритм расчёта и размер контрольной суммы отличался. Разбор алгоритма расчёта контрольной суммы в одной игре, никак не помогал в разборе другой игры от Capcom. Но, повезло тем играм, где контроля контрольной суммы не было вовсе.

Вернёмся к основной теме – контрольная сумма данных в Resident Evil 4 на Playstation 3.

Игра была построена на базе HD переиздания PC версии, что влекло за собой новый игровой движок и новый формат хранения данных. То, что игра была построена на PC версии сыграло большую роль в разборе алгоритма расчёта контрольной суммы, но об этом позже.
Все игровые данные прятались за неким форматом LFS. Он из себя представлял информацию о сжатом файле и о количестве секций, на которые он разделён. Для PC и Xbox360 каждая секция сжималась алгоритмом, который в сообществе ромхакеров называется “xmemdecompress”. Это алгоритм сжатия, который Microsoft внедрила в SDK Xbox360. Данный алгоритм сжатия базируется на алгоритме сжатия LZX.

Но тут в игру вступает Playstation 3. К большому сожалению, она не использовала данный алгоритм сжатия для секций, она хранила данные в «сыром виде», но, для каждой секции добавились новые 4 байта – контрольная сумма. И любой изменённый байт ломал загрузку игры, т.к. файл не проходил проверку.

Именно тут я приступил к разбору алгоритма расчёта контрольной суммы. 4 байта намекали на то, что алгоритм расчёта контрольной суммы может быть из семейства обычного CRC32. Но расчёт CRC32 со стандартными значениями полинома, выходного XOR и начального значения выводил результат, не соответствующий тому, что я видел в файле после каждой секции.
Больше не было выбора, как закинуть исполняемый файл в дизассемблер и попробовать найти кусок кода, отвечающий за расчёт контрольной суммы. Основная идея лежала в том, чтобы отключить проверку вовсе, а не пытаться разобрать алгоритм расчёта контрольной суммы.

Потратив несколько дней на изучение исполняемого файла Playstation 3 в дизассемблере, я так и не смог найти кусок кода, отвечающего за расчёт контрольной суммы. Тут повлияло много факторов, как мой малый опыт в разборе кода на таком низком уровне, так и сложность самой архитектуры Cell, используемой в Playstation 3.

В один момент я подумал, что версия игры для PC построена на том же игровом движке, что и на Playstation 3, и, возможно, там мог остаться кусок кода, отвечающий за расчёт контрольной суммы.
В PC версии игры в игровых архивах не было контрольной суммы секций, как это было в Playstation 3 версии игры, но, надеясь на удачу, я решил изучить исполняемый файл PC версии игры. С помощью нескольких манипуляций я смог найти функцию, отвечающую за чтение основных игровых архивов. Немного попрыгав по функциям внутри, я заметил весьма интересный кусок кода, который был похож на расчёт чего-то. Это и был тот самый алгоритм расчёта контрольной суммы.

Как я и говорил ранее, всё было очень похоже на обычный CRC32, но входные данные для начала расчёта отличались от тех, которые были предположены по умолчанию алгоритмом CRC32.

Всё оказалось весьма просто, полином соответствовал значению по умолчанию – 0x4C11DB7, но значение выходного XOR и начальное значение для расчёта отличались от стандартных параметров.

Если в стандартном алгоритме CRC32 начальное значение и значение выходного XOR соответствует 0xFFFFFFFF, то в текущем алгоритме расчёта контрольной суммы эти значения соответствовали 0.

Вроде бы простые правки в начальных значениях стандартного алгоритма расчёта контрольной суммы, но додуматься до этого невозможно.

Вторая проблема перевода


Разобрав, по факту, самую сложную часть, можно было бы разбирать то, что хранится за этим архивом и переводить игру, но не всё так просто.

Ранее я упоминал о том, что версия для GameCube\Wii не переведена до сих пор из-за определённого фактора, а версия для Playstation 3 почему-то не сжимает секции в основных архивах, как это делается в версии для PC и Xbox360. Вся проблема заключается в специфичном для Gamecube, Wii и Playstation 3 сжатии, которое до сих пор никто не разобрал.

Gamecube версия игры использовала на то время весьма сильное и специфичное сжатие, которое никто не разобрал до сих пор и именно из-за этого игра не переведена на эту прекрасную платформу. Можно понять, почему для GameCube версии использовалось это сжатие. Диски на Gamecube имели размер в 1.4гб (против 4.7 на PS2), и игра еле умещалась на 2 диска. Версия для Wii была аналогична версии для Gamecube, т.к. эти платформы имели идентичную архитектуру, и портирование не требовало много человеко-ресурсов. Но, почему Capcom решила использовать внутри LFS для Playstation 3 формат игровых данных с GameCube, используя тоже сжатие? Этот вопрос до сих пор остаётся открытым.

Мои попытки разобрать алгоритм сжатия приводили к краху, но, может, когда-нибудь…

Заключение


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

Если Вам интересна тематика, то пишите в комментарии, в багаже ещё много подобных историй :)

Комментарии (7)


  1. Loggus66
    31.07.2021 10:49
    +1

    Если Вам интересна тематика

    Интересна. Текстуры шрифтов, их нарезка движком, трансляция текста из ресурсов игры в отображаемое на экране, плюс что приходится делать при несовпадении количества символов в разных алфавитах.


    1. Shegorat
      31.07.2021 21:39

      В старых играх текстура шрифта представляет собой DDS-текстуру с сеткой 16x16 (по сути вся таблица ASCII), где каждый байт соответствует своему индексу на текстуре.

      В новых играх текстуры могут быть больше. Либо вообще свои текстуры под конкретный алфавит, как это в MGRR (там вообще всё очень страшно по структуре)

      Часто вместе с текстурой идёт символьная таблица с указанием кода символа и его положением на текстуре:

      struct {
      	wchat_t code;
        int32_t left;
        int32_t top;
        int32_t width;
        int32_t height;
        // доп. данные типа кернинга и т.д.
      } symbol;

      Обычно применяются прямые координаты, реже полярные (опять же MGRR).

      Всё зависит от конкретного движка и игры


  1. nikolau
    31.07.2021 12:22

    Интересен вот какой вопрос - например, исходная строка текста на английском в игре занимает одну длину, а на русском другую. Вот если на русском строка длиннее, как ее вставить и заменить в игре? Просто в hex редакторе не получится.


    1. Shegorat
      31.07.2021 21:30

      Просто в hex-редакторе менять строки неправильно. Тем более, что русский текст обычно представляется в UTF-8 кодировке, а там уже другое количество байт на символ.

      Зачастую текст игры представлен в виде некоторого бинарного текстового файла, состоящего их массива элементов. Типа такого

      struct {
        int32_t len;  // может быть int8_t либо int16_t, в зависимости от макс. длины строк трок
        char    text[len + 1]; // в конце терминирующий \0
      } text_node;

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

      Иногда текст хранится в json или xml. Тогда переводить проще, но всё равно необходимо писать парсер, чтобы вытащить только текст, без прочей шелухи, и затем обратно его вставить.

      Редко, когда текст зашит в exe файл, тогда там приходится применять разные хаки для перевода, и это намного более трудоемкий процесс, и в играх с защитой он практически невозможен.

      И в общем-то, чтобы это всё расписать не хватит 10 статей. Есть много различных движков, и в каждом своя система локализации.


  1. Shegorat
    31.07.2021 14:38
    +2

    И, к сожалению, в статье не приведены ни примеры реверс-инжиниринга алгоритмов, ни та структура архивов LFS, ни формат текста, и особенности представления данных на разных платформах.

    Насчет GameCube/Wii предполагаю, что там какая-то вариация алгоритма LZMA. LZX, LZO, zlib не дадут нужной степени сжатия, а больше на тот момент и не было ассиметричных алгоритмов с высокой степенью сжатия. Но я могу и ошибаться


  1. Qetzlcoatl
    31.07.2021 23:27

    А как же тогда на русский были переведены 3 Zelda - Skyward Sword, The Wind Waker и Twilight Princess, раз на GameCube/Wii такие сложности с распаковкой?


    1. TTEMMA Автор
      31.07.2021 23:28
      +1

      Проблемы конкретно с Resident Evil 4