Перейти до вмісту
EN

Завантаження перекладу в рантаймі: від розпакованої гри до ASI

Ця сторінка описує, як переклад потрапляє в гру, які функції рушія для цього знайдено і як змінювався спосіб постачання від 0.1.0 до 1.1.10. Загальну картину модулів див. в architecture.md.

Етап 1: розпакована гра (0.1.0–0.1.5)

Розділ «Етап 1: розпакована гра (0.1.0–0.1.5)»

Рання гіпотеза проєкту: гра читає файли з STUFF/TEXT/ поверх GAME.DAT. Перша спроба встановити переклад так нічого не змінила. Реальна поведінка: рушій читає звичайні файли лише тоді, коли відповідного DAT немає на диску. Тому ранні версії вимагали розпакувати всі чотири DAT у теку гри (десятки тисяч файлів, кілька гігабайтів), перейменувати архіви на *.DAT.original_backup і накласти переклад. Пакет важив 182 МБ, а встановлення було незручним і ризикованим.

Етап 2: UAP і перехоплення читання (0.1.6–0.1.7)

Розділ «Етап 2: UAP і перехоплення читання (0.1.6–0.1.7)»

Ідея: лишити оригінальні DAT, а потрібні файли віддавати рушію з пам’яті. Для цього дизасемблюванням знайдено функції ресурсної підсистеми (адреси для Steam EXE з image base 0x400000):

VA Роль
0x6B44F0 відкриття ресурсу: з DAT або за файловим шляхом
0x6B46D0 перевірка існування
0x6B4750 розмір за шляхом
0x6B7440 завантаження цілого ресурсу (з прямою гілкою читання DAT)
0x6B0250 розмір відкритого ресурсу
0x6AA040 відкриття буфера пам’яті як ресурсу (memory stream)
0x6B55A0 читання
0x6B04C0 позиціювання
0x6B38F0 закриття

Плагін ставить x86 detour на ці функції. Для відомих шляхів (STUFF/TEXT/ENGLISH.TXT, шрифти, титри) він створює рідний memory stream рушія над даними з UAP, тож гра читає їх своїми ж функціями. Усі інші ресурси проходять через trampoline до оригінального коду.

Дві знахідки, без яких це не працювало:

  • CSV читався в обхід відкриття. Прототип із трьома перехопленнями вже показував український основний текст і шрифт, але TRC.CSV лишався англійським. Причина — окрема гілка прямого читання DAT у 0x6B7440. Додано окреме перехоплення цієї функції.
  • Розмір на один байт менший. Memory stream рушія зберігає inclusive end, а 0x6B0250 повертає end − start. Для наших буферів розмір коригується до довжини запису UAP; чужі дескриптори не чіпаються.

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

У 0.1.7 до цього додалася підміна DDS у буфері читання (функція 0x6B55A0, вкрадені інструкції 51 8B 44 24 08). Подробиці — у textures.md.

Перед будь-якою зміною плагін перевіряє:

  • SHA-256 LEGOIndy.exe (c12f7348…332e5e5fd);
  • початкові байти кожної функції, яку перехоплює;
  • SHA-256 кожного запису UAP.

Якщо хоч щось не збігається, переклад не вмикається, а гра працює як оригінал. Це перевірено тестами з відсутнім і пошкодженим UAP: усі шість функцій і 76 PC-вказівників лишаються незмінними.

Етап 3: ASI-плагін (0.1.8 і далі)

Розділ «Етап 3: ASI-плагін (0.1.8 і далі)»

У 0.1.6–0.1.7 наша dinput8.dll була proxy-бібліотекою: надавала експорти DirectInput, завантажувала системну бібліотеку й передавала їй виклики. Мінус — з іншим модом, якому теж потрібен dinput8.dll, переклад не співіснував.

З 0.1.8 dinput8.dll — офіційний Ultimate ASI Loader 9.7.4 без змін, а переклад — плагін scripts/UKRAINIAN.asi. Перед вибором перевірено код саме тега v9.7.4, а не README гілки master:

  • після завантаження ASI loader викликає недекорований експорт InitializeASI;
  • зі стандартним DontLoadFromDllMain=1 запуск плагінів відкладається до викликів гри, і loader враховує захисну секцію Steam .bind.

Наш плагін:

  • у DllMain лише запам’ятовує модуль, уся робота — в InitializeASI;
  • через InitOnceExecuteOnce ініціалізується рівно один раз, навіть якщо інший мод викличе InitializeASI повторно;
  • закріплює себе в пам’яті до кінця процесу, бо таблиці гри й trampolines посилаються на його код і дані;
  • шукає UKRAINIAN.uap поруч із собою, а не в робочій теці;
  • пише журнал у scripts/legoindy-ua-pc.log;
  • імпортує лише KERNEL32 і bcrypt (SHA-256), експортує лише InitializeASI.
Перевірка 0.1.8 Результат
Основні тексти в пам’яті 598/598
CSV у пам’яті 194/194
PC-рядки 38/38; 76 вихідних і 38 активних вказівників
Потоки UAP (read/size/seek/EOF/close, 32 повторні відкриття) 21/21
Другий незалежний ASI завантажився й відпрацював разом із нашим
Повторний виклик InitializeASI одна ініціалізація
UAP відсутній чи пошкоджений гра оригінальна
Невідомий EXE пропуск без патчів

Сумісність з іншими модами

Розділ «Сумісність з іншими модами»

ASI знімає конфлікт за ім’я dinput8.dll, але не всі конфлікти:

  • ENGLISH.TXT, TRC.CSV і шрифти з UAP підміняються цілими файлами. Текстові моди (наприклад, Archeologist Edition змінює назви в english.txt) потребуватимуть злиття.
  • Мод, що змінює ті самі DDS, матиме пріоритет: плагін не впізнає змінених пікселів і пропустить підміну.
  • Loose-файли в CHARS/ чи LEVELS/ при активних DAT не діють; таким модам потрібна розпакована гра або редиректор.
  • Будь-яка зміна EXE на диску, зокрема LAA-патч, змінює SHA-256, і плагін вимикається.

Комбінації зі сторонніми модпаками наживо не перевірялися.

Мова системи: англійська (Велика Британія) чи англійська (США)

Розділ «Мова системи: англійська (Велика Британія) чи англійська (США)»

Під час перевірки на Steam Deck автор помітив, що частина тексту лишається англійською, хоча той самий пакет на Windows-ПК працював повністю. Причина виявилася не в адресах пам’яті й не в Proton як такому.

  • LEGOIndy.exe визначає мову через GetUserDefaultLangID. У бінарнику окремо прописано stuff\text\american.txt.
  • Для англійської (США) гра спершу читає ENGLISH.TXT, а потім поверх нього AMERICAN.TXT. Це надбудова лише з 18 рядків з американським написанням («Artifact», «Theater», підказки про лопату тощо). Крім того, гра бере колонку AMERICAN з TRC.CSV (усі діалоги збереження й завантаження, «Press a key or controller button…») і AMERICAN_CREDITS.TXT замість наших титрів.
  • Proton на Steam Deck за замовчуванням працює як en-US. На ПК автора мова системи інша, тому гра обирала британський варіант, і все було перекладено. Параметр запуску LANG=en_GB.UTF-8 на Deck не допоміг, тож на вибір мови в Proton він не впливає.

Рішення (1.1.10): плагін віддає будь-який файл, який є в пакеті, тож нативний код змінювати не довелося. У UKRAINIAN.uap додано перекладені AMERICAN.TXT і AMERICAN_CREDITS.TXT, а TRC.CSV отримує переклад в обидві англійські колонки, ENGLISH і AMERICAN. Шаблони американських файлів один раз беруться з видобутої гри (manifests/game-files.json); у наступних релізах вони вже будуть у базовому пакеті. Значок рейтингу ESRB на американському стартовому екрані свідомо не перемальовано: це знак рейтингової організації. Текст самого екрана (T2748) перекладено.

Урок діагностики: пауза перед катсценами

Розділ «Урок діагностики: пауза перед катсценами»

Після переходу на UAP автор помітив паузу близько 15 секунд перед катсценами. Логічна підозра падала на плагін. Контрольні запуски показали інше:

  1. Без UKRAINIAN.asi пауза лишилася.
  2. Без dinput8.dll (у процесі — системна DINPUT8.dll) пауза лишилася.
  3. Порівняння з маніфестом Steam-депо виявило, що бракує 279 файлів теки audio (883 МБ: OGG катсцен, музика). Їх помилково прибрав старий сценарій міграції з розпакованої гри.

Після відновлення аудіо (перевірка цілісності Steam) катсцени запрацювали нормально, а переклад повернули. Висновок, який увійшов у процес: інструменти встановлення для розробника звіряють контрольні суми файлів гри, яких не можна чіпати (EXE, DAT, binkw32.dll, тека audio), до й після копіювання.

Для швидкого тестування в тій діагностиці існував режим автоматичного завершення катсцен (за відкритою інструкцією спільноти змінювався предикат 0x6A0250). Він лишився експериментом і не входить до релізу.

Повні докази — в архіві досліджень автора.