Как перевести Chronicles of the Wolf, не вскрывая архивы?
(пост без картинок, но видео игрового процесса внизу)
Иногда желание поиграть в игру на родном языке разбивается о суровую реальность: разработчики упаковали текст в архивы, зашифровали их алгоритмами, от которых плачут даже криптографы, и намертво вшили в движок.
Распаковать такое это адский труд. А запаковать обратно так, чтобы игра не вылетела - занятие для людей со стальными нервами.
Но что, если скажем вам, что игру вообще не нужно распаковывать? Что если можно написать «шпиона», который будет сидеть в оперативной памяти, ловить тексты за миллисекунду до его появления на экране и незаметно подсовывать вместо него русский?
Именно такой путь мы прошел недавно. Делимся историей о том, как пара строк на ассемблере, борьба с «призраками» в памяти и магия C++ превратились в полноценный мод-перевод.
Шаг 1. Детективная работа (Ищем, где прячется текст)
Любая игра, какой бы сложной она ни была, перед отрисовкой текста должна положить его в оперативную память. Вооружившись отладчиком, начал искать.
Но проблема в том, что тут текст не лежит в памяти статично. Движок игры постоянно выделяет под него новые, случайные кусочки памяти. Но удалось поймать момент, когда текст только-только формируется. Все дороги вели к одной функции в машинном коде:
``
push ebx
push esi
push edi
mov edi, edx ; <--- Вот тут в EDX наш текст!
mov ebx, ecx
``
План был надежен, как швейцарские часы:
- Поставить игру на паузу (брейкпоинт) в этом месте.
- Прочитать текст.
- Записать его.
- Отпустить игру дальше.
НО реальность ударила по тормозам. В прямом смысле. Игра вызывает эту функцию сотни раз в секунду. Использование классических остановок заставляло игру зависать намертво. Нужен был способ ловить текст на лету, не останавливая игру.
Шаг 2. Ловушка для текста (и откусанные слова)
Был написал Lua-скрипт прямо внутри Cheat Engine. Он внедрял крошечный кусочек кода прямо в игру. Теперь игра сама, без пауз, складывала копии текстов в выделенный мной "безопасный буфер", а скрипт раз в 100 миллисекунд неторопливо записывал их в файл game_texts_dump.txt
.
Запустил игру, побегал по меню и открыл свой файл. Но вместо красивых предложений увидел это:
In case of resizing In WINDOWED mode, to restore the original size, press: SHIFT.
ing In WINDOWED mode, to restore the original size, press: SHIFT.
WED mode, to restore the original size, press: SHIFT.
l size, press: SHIFT.
Что за чертовщина?
Оказалось, игровой движок парень экономный. Когда он высчитывает, как перенести длинный текст на новую строку, он не копирует его заново. Он просто сдвигает указатель вперед и снова вызывает функцию копирования. В итоге ловился не только целые предложения, но и их «хвосты».
Пришлось научить скрипт уму-разуму: прежде чем сохранить фразу, он проверял, не является ли она обрубком уже известного предложения.
И вот, у нас на руках оказался чистый дамп всего текста игры!
Шаг 3. Битва Хешей и Невидимые Призраки
Текст собран, переведен тестово в соседнем окошке. Теперь главное это подмена.
Сравнивать длинные строки (например, "Нажмите кнопку Start") посимвольно в момент отрисовки кадра это значит убить производительность игры. Поэтому программисты используют хеширование — превращают любое слово в уникальное число-отпечаток.
Например, слово START получает код 0E0C2133.
Идея проста: игра вычисляет хеш -> находит его в словаре -> заменяет оригинальный текст на русский.
Написан скрипт, загрузился перевод, запустил... И ничего не произошло. Текст остался оригинальным.
Пришлось устроить проверку: вывели в консоль хеши, которые считал словарь, и хеши, которые видела игра в тот же самый момент:
- [СЛОВАРЬ] Хеш: 0E0C2133 | Текст: 'START'
- [ИГРА] Хеш: CF22CD2F | Текст: 'START'
Хеши не совпадали! Но почему? Слово-то одно и то же!
Заглянув в сырые байты памяти, мы раскрыли преступление. Игровой движок подсовывал процессору не просто слово START. К нему могли быть прилеплены невидимые байты форматирования (теги цвета, маркеры переноса) или банальные пробелы на конце.
Заглянув в сырые байты памяти, мы раскрыли преступление. Игровой движок подсовывал процессору не просто слово START. К нему могли быть прилеплены невидимые байты форматирования (теги цвета, маркеры переноса) или банальные пробелы на конце.
Кроме того, алгоритм, который использовался изначально, ломался на границах 32-битных чисел.
Переписав код на пуленепробиваемый промышленный алгоритм FNV-1a, который использует только математическое умножение и операцию XOR.
Бинго! Хеши совпали. Текст в игре стал русским.
Шаг 4. Взросление: от скрипта к настоящему моду (DLL)
Делать перевод, для запуска которого людям нужно качать отладчик, вставлять скрипты и жать кнопки это моветон. Мод должен работать по принципу «кинул в папку с игрой и забыл».
Пришло время перенести всё на C++ в Visual Studio и создать DLL-библиотеку.
Код написан был несложный, повторяющий тоже самое что и наш ранний скрипт.
Теперь, когда игра запускается, наша DLL за долю секунды сканирует всю оперативную память, находит куда ставить ловушку.
Финальные штрихи
Чтобы мод стал хорошим, добавили еще пару вещей:
- Подмена шрифта: Зачастую в играх нет поддержки кириллицы. Поставили еще один хук (перехват) на системную функцию Windows CreateFontIndirectW, заставив игру всегда использовать шрифт, в котором есть русские буквы(благо у игры был такой шрифт).
- Горячие клавиши: Нажатием F5 переводчики могут перезагрузить текстовый файл прямо во время игры и сразу увидеть изменения на экране, а по F6 включается режим выгрузки новых, еще не переведенных текстов в дамп-файл.
Итог
То, что начиналось как попытка возиться со сложными архивами игры, превратилось в изящный C++ плагин. Он не трогает оригинальные файлы игры, работает молниеносно, обходит защиты и вставляет текст прямо в воздухе, между недрами игрового движка и вашим монитором.
Реверс-инжиниринг игр это не всегда про читы и бессмертие. Иногда это просто желание заставить игру заговорить на твоем родном языке, и путь к этой цели похож на решение потрясающей технической головоломки.
P.S.: на словах кажется что делов на пару часов или день, на деле же недели работы и изучение кода игры.
chronicles of the wolf
разбор
Владимир Комарович
Спасибо, нравятся такие бэкстэйджи они же "за кадром".
Apr 11 10:53