Tekken 3 з нуля менш ніж за тиждень: вайб-реверсинг PlayStation-гри і ремейк на Godot
Автор: n0madic · 1 жовтня 2026

Як ми з Claude Code розібрали диск 1998 року, знайшли в оригіналі понад 60 багів і зібрали повноцінний ремейк, який збігається з оригіналом кадр у кадр.
З чого все почалося
26 вересня я дав Claude Code одне велике завдання. Звучало воно приблизно так: «Створи з нуля на сучасному рушії ремейк Tekken 3 для PSX. Для початку розбери оригінал на дисках, задокументуй усі формати даних, 3D-моделі, анімацію, звуки, ефекти і всю ігрову логіку».
Код я майже не писав. Моя роль зводилася до трьох речей: ставити цілі, ухвалювати рішення на розвилках і дивитися на результат очима гравця. Усе інше робив агент. Нижче розповідаю, як це виглядало зсередини, де ШІ виявився на висоті, а де помилявся.
Для масштабу. У репозиторії зараз приблизно 45 тисяч рядків GDScript у 215 файлах, 35 тисяч рядків Python-інструментів реверсингу, ще 5 тисяч у конвертері дисків, 35 документів Markdown про формати й логіку гри та понад 170 тестів. Головна сесія Claude Code, у якій відбулися дослідження і перші п'ять віх, налічує понад 46 тисяч повідомлень агента і близько 23 тисяч викликів інструментів.
Етап 1. Вайб-реверсинг: як розібрати гру без вихідного коду
Реверсинг гри для PlayStation складається з двох задач. Перша: зрозуміти формати файлів (моделі, анімації, текстури, звук, відео). Друга, значно важча: зрозуміти логіку, тобто що саме відбувається за один кадр бою, як рахується удар, як «думає» CPU-суперник.
Інструменти. Агент сам розгорнув конвеєр:
- Ghidra у headless-режимі з сигнатурами бібліотеки PsyQ (вона розпізнала 929 бібліотечних функцій) і скрипти, що імпортують бінарний файл та всі десять оверлеїв гри;
- Unicorn (емулятор MIPS-процесора) плюс власна програмна модель співпроцесора GTE, який у PlayStation рахує 3D-геометрію;
- Python-скрипти, що розпаковують диски у робоче дерево, і декомпільований C.
Головна ідея методу. Нам не потрібен повний емулятор PlayStation. Потрібна можливість запускати окремі функції оригінального машинного коду й порівнювати їхній результат з нашою реалізацією. Тому для кожної підсистеми агент ішов за одним циклом: прочитати декомпіляцію, написати Python-порт, прогнати оригінальну функцію в Unicorn на тисячах випадкових вхідних даних, порівняти пам'ять побайтово. Поки є хоч один розбіжний байт, формат або правило в документації мають статус inferred, а не confirmed.
Так, наприклад, анімаційний конвеєр (49 каналів, сплайни) збігається з грою біт у біт на 314290 кадрах усіх 22 банків рухів. Камера звірена на 86058 кадрах. Весь ШІ суперника портований і перевірений у 19 сценаріях без жодної розбіжності.
Проблеми реверсингу
- Декомпілятор часто бреше. Оверлеї гри (код режимів і екранів вантажиться в одні й ті самі адреси) Ghidra розбирає погано: зливає сусідні функції, «згортає» таблиці констант. Рішення: для складних місць агент переходив на дизасемблер capstone й портував з асемблера, а не з декомпіляції.
- Агент мав власну пам'ять. Контекстне вікно закінчувалося в головній сесії близько 67 разів. Усе, що було б втрачено, агент записував у файли пам'яті та в
CLAUDE.md: конвеєр, відомі пастки, ухвалені рішення. Без цього робота розсипалася б після першого ж «забуття». - Емулятор теж має свої «характери». Unicorn при діленні на нуль повертає не те, що справжній R3000 (там −1 або +1). Довелося вчити верифікатори до цього. А спроба переписати код оригіналу «на льоту» через хуки просто валила емулятор у segfault, і з'явилося правило: ніколи не лізти в код через хуки.
- Оригінальні пастки. У деяких місцях гра читає з адреси 0 (дзеркало RAM на консолі) або використовує значення регістра, що залишилося «з BIOS». Це не відтворюється без реальної консолі, тому такі місця окремо позначені й описані.
Побічний продукт: реєстр багів оригіналу
Я попросив фіксувати кожен знайдений дефект у game-bugs.md: де, що робить оригінал, який ефект, як виправити. На сьогодні там 61 запис. Кілька з них:
- AI-суперник «пам'ятає» удари, але порівнює їх лише з нульовим слотом історії з восьми, тож «навчання» працює не так, як задумано;
- розблокування персонажів у аркаді пораховано циклом по бітових позиціях замість маски: після 11-го пройденого персонажа прогрес просто «не рухається»;
- у Tekken Ball суперник мав би віддавати перевагу найменш зустрічаним персонажам, але через помилку сортування вибір рівномірний;
- у Tekken Force таймери ворогів 16-бітні: після ~9 хвилин на одному рівні всі вони вважаються «прострочені»;
- цикл очищення результатів десять разів чистить один і той самий запис, бо покажчик не просувається (на щастя, без наслідків).
Політика проста: баги без впливу на гру не відтворюємо, баги, що впливають на геймплей, залишаємо за замовчуванням і виправляємо однією опцією Gameplay fixes. Так «автентичний» режим лишається автентичним.
Етап 2. Вибір рушія і план
Коли документація дозріла, я спитав: який сучасний рушій найкраще підійде для файтингу, щоб він був відкритим, легким і запускався на десктопі, мобільних і в вебі. Агент запропонував варіанти, ми обговорили, і я вибрав Godot 4 із GDScript. Головний аргумент: C#-проєкти Godot 4 не експортуються у веб, а веб був у цілях.
Перед будь-яким кодом ми написали великий план реалізації: архітектура, шари, рішення щодо графіки, віхи M0–M5. Дві ключові ідеї:
- Ядро симуляції без Node-ів. Детермінована гра на 60 Гц, цілі числа і фіксована кома (4096 одиниць кута на оберт, 4.12), ніяких float-ів і фізики рушія. Усе, що виходить назовні (звуки, ефекти, вібрація), виходить подіями кадру. Презентація лише читає стан.
- Код не дослівний, а ідіоматичний, але підтверджений «золотими трасами». Python-порти, перевірені проти оригіналу, генерують еталонні записи кадрів. Godot-тести програють ті самі входи через ядро і звіряють стан поле за полем щокадру. Один із підсумків: 32 повні матчі збігаються з оригіналом по кожному полю бійців, камері, таймеру, генераторах випадкових чисел, звукових і вібраційних викликах.
Етап 3. Godot-порт: віхи
- M0, фундамент. Python-конвертер диска в формати Godot, тестова сцена з одним бійцем.
- M1, заставка. NAMCO PRESENTS, відео, титульний екран, демонстраційний бій на сцені. Збігається з оригінальним кодом, прогнаним в емуляторі, кадр у кадр: 3549 кадрів на кожен із трьох сценаріїв.
- M2, бій. Два гравці, усі персонажі, усі арени.
- M3, CPU і гра. Меню, аркада з Огром, результати, рейтинг, Theater, збереження.
- M4, Tekken Force і Tekken Ball.
- M5, платформи. Android, iOS, веб, Windows, Linux, macOS, сенсорне керування, DualShock/DualSense, пакети текстур.
Зручна робота з агентом виглядала так: я пишу «реалізуй повністю M3 з плану», а далі переглядаю результат, граю й скидаю недоліки: «боєць не падає за ринг», «Esc не працює в Command List», «аптечка не підбирається».
Цікаві проблеми порту
- Моделі не вдалося покласти в glTF. У ігрових моделях кути швів є сумішшю 50/50 двох частин тіла, які в одній позі ніколи не збігаються. Тому кожен кут зберігає дві локальні позиції, а шейдер змішує їх сам. Анімації запечені по кадрах, сплайни Godot не обчислює.
- «Пластикові» волосся Law і Lei. Причина виявилася в Godot: навіть слабкий specular на чорних полігонах підсилюється членом Френеля до ~70 %. В оригіналі освітлення суто дифузне (Gouraud через GTE), тож спекуляр прибрали.
- Музика стартувала двічі, HUD з'являвся поверх сцени з Огром. Ці дрібниці агент теж виловлював через порівняння з поведінкою оригінального коду.
- Один диск замість двох. Агент побудував систему «відповідностей адрес» (
layouts/<реліз>.json): будь-який з трьох релізів (Japan Rev.1, оригінальний Japan, USA) переводиться в адресний простір Rev.1, і гра збирається з будь-якого одного. У репозиторії лежать лише числа і хеші, жодних даних Namco.
Рефакторинг: «а чи не дослівний це переклад?»
Тривалий час я працював із кодом, не заглядаючи всередину. Потім зазирнув і побачив, що значна частина ремейку виглядає як дослівний переклад декомпільованого коду: константи на кшталт 0x800B9554, умови типу slot == 0xD41. Я запитав, чи є сенс переписати це ідіоматично.
Агент відповів неочікувано чесно: переписувати ядро «по-годотівськи» (Node, сигнали, Tween, float) не слід, бо це зламає детермінізм. Натомість варто замінити магічні числа на enum-и і імена та розбити монолітні функції. Тести-траси тут страхують: після кожного кроку проганяється повний набір, і всі поля мають збігатися з оригіналом на кожному кадрі. Так і зробили: понад два десятки комітів «refactor: …» ввели Character, GameMode, RoundState, BankType, імена бітів станів, кнопок, умов гілок рухів. Поведінка не змінилася, а читати код стало набагато легше.
Яка роль ШІ і де закінчується вайб
Що працювало добре:
- Ітерації з верифікацією. Кожен твердий факт підтверджувався запуском оригінального коду. Це найкращий «запобіжник від галюцинацій»: якщо агент вигадав правило, порівняння пам'яті це покаже.
- Пам'ять і документація як продовження мислення.
CLAUDE.md, файли пам'яті та Markdown-документи дозволили пережити десятки обнулень контексту. - Розбиття на віхи й «виправ усі зауваження». Короткі команди типу «реалізуй M4», «виправити все»,
/code-reviewдали на диво багато.
Де потрібна людина:
- Рішення. Рушій, політика щодо багів, «один інтерфейс замість двох», заборона на дублі ресурсів тощо. ШІ радить, але «куди» вирішує автор.
- Очі гравця. Агент надійно порівнює числа, але не бачить, що бій «не відчувається». Усі скарги на кшталт «гори як шпалери», «дригання у ролику» і «бачу пластик» прийшли від мене.
- Контроль якості роботи. Агент помилявся і сам: перший план виправлень включав складну емуляцію кешу завантажень лише заради зупинки музики, і я його відхилив; код-рев'ю знаходило падіння екрана, якого тести «не помічали». Після цього з'явилося правило в пам'яті: «ніколи не відновлювати копію поверх змін, зроблених не мною».
Висновки
- Вайб-реверсинг працює, якщо є «оракул». Оригінальний машинний код, який можна запускати й порівнювати, перетворює галюцинації на помилки тестів. Без такого оракула ми б мали гарні, але неперевірені документи.
- Сильні межі й тести важливіші за красиві промпти. Шари «ядро / презентація», детермінізм і золоті траси зробили можливими і рефакторинг, і масові виправлення.
- Записуйте контекст у файли. Для агента довгий проєкт це серія знайомств із нуля.
CLAUDE.mdі пам'ять роблять із нього колегу. - Автор лишається директором. ШІ написав майже все, але сенс проєкту (точний, а не «схожий») задала людина.
Проєкт не містить і не поширює ігрових даних: потрібен власний образ диска, а конвертер сам перетворює його на ресурси ремейка. Усі дані Namco лишаються у вас.