Я заменил диалоги в Animal Crossing на нейросеть, взломав память GameCube. animal crossing.. animal crossing. dolphin.. animal crossing. dolphin. gamecube.. animal crossing. dolphin. gamecube. llm.. animal crossing. dolphin. gamecube. llm. nintendo.. animal crossing. dolphin. gamecube. llm. nintendo. python.. animal crossing. dolphin. gamecube. llm. nintendo. python. Блог компании BotHub.. animal crossing. dolphin. gamecube. llm. nintendo. python. Блог компании BotHub. Игры и игровые консоли.. animal crossing. dolphin. gamecube. llm. nintendo. python. Блог компании BotHub. Игры и игровые консоли. искусственный интеллект.. animal crossing. dolphin. gamecube. llm. nintendo. python. Блог компании BotHub. Игры и игровые консоли. искусственный интеллект. Ненормальное программирование.. animal crossing. dolphin. gamecube. llm. nintendo. python. Блог компании BotHub. Игры и игровые консоли. искусственный интеллект. Ненормальное программирование. Программирование.

Animal Crossing. Прославившаяся своей обаятельной, но в конечном счёте до боли повторяющейся болтовнёй. Вновь запустив этот классический хит для GameCube, я ужаснулся… жители по‑прежнему твердят те же самые фразы, что и двадцать три года назад. Ну уж нет, пора это менять.

Проблема? Игра работает на Nintendo GameCube — консоли 24-летней давности с процессором PowerPC на 485 МГц, 24 мегабайтами оперативки и полным отсутствием интернета. Она была создана — и технически, и концептуально — как замкнутый остров в офлайне.

И вот история о том, как я протянул кабель от 2001-го в сегодняшний день, заставив винтажную приставку разговаривать с облачным ИИ, не изменив ни строчки кода оригинальной игры.

Делегируйте рутинные задачи вместе с BotHub! По ссылке вы можете получить 100 000 бесплатных капсов и приступить к работе с нейросетями прямо сейчас.

Куки: «О боже, Джош :)! Мне приснился такой странный сон, будто всё, что мы делаем, — это игра! Арфер!»

Куки: «О боже, Джош :)! Мне приснился такой странный сон, будто всё, что мы делаем, — это игра! Арфер!»

Первая преграда: как заговорить с игрой 🗣️

Мой старт оказался удивительно удачным. В ту самую неделю, когда я взялся за проект, сообщество энтузиастов завершило гигантский труд — полную декомпиляцию Animal Crossing. Вместо того чтобы таращиться в ассемблер PowerPC, я получил доступ к вполне читаемому коду на C.

Копнув глубже, я быстро наткнулся на ключевые функции в файле m_message.c. Вот он, центр всей диалоговой системы! Простейший тест подтвердил: я могу перехватить вызов функции и подменить внутриигровой текст на свою строку.

Взгляд на декомпилированную систему диалогов (C):

// Фрагмент исходного кода Animal Crossing после декомпиляции
// Функция, которая меняет внутренности сообщения в диалоговой системе.
// С этого места я и начал угонять текст.

extern int mMsg_ChangeMsgData(mMsg_Window_c* msg_p, int index) {
    if (index >= 0 && index < MSG_MAX && mMsg_LoadMsgData(msg_p->msg_data, index, FALSE)) {
        msg_p->end_text_cursor_idx = 0;
        mMsg_Clear_CursolIndex(msg_p);
        mMsg_SetTimer(msg_p, 20.0f);
        return TRUE;
    }

    return FALSE;
}

Просто победа, правда? Но заменить статический текст — одно дело. А вот как вживую подсунуть в игру данные от внешнего ИИ?

Первая мысль: добавить сетевой вызов. Но это означало бы писать с нуля целый сетевой стек для GameCube (TCP/IP, сокеты, HTTP) и вживлять его в движок, который для сети никогда не предназначался. Даже не вариант.

Вторая идея: воспользоваться функциями эмулятора Dolphin, чтобы писать файл на хост‑машине. Игра бы оставляла там «запрос», мой Python‑скрипт читал бы его, дергал LLM и возвращал «ответ». Увы, запертая в песочнице среда GameCube не смогла получить доступ к файловой системе хоста. Очередной тупик.

Прорыв: «почтовый ящик» в памяти 📬

Решение пришло из классики моддинга: межпроцессное взаимодействие (IPC) через общую память. Смысл был прост — выделить кусок RAM GameCube в роли «почтового ящика». В этот ящик мой внешний Python‑скрипт может напрямую записывать данные, а игра — считывать их.

Я заменил диалоги в Animal Crossing на нейросеть, взломав память GameCube - 2

Сердце интерфейса «почтового ящика» (Python):

# Это мост. Функции читают и пишут данные в RAM GameCube через Dolphin.
GAMECUBE_MEMORY_BASE = 0x80000000
 
def read_from_game(gc_address: int, size: int) -> bytes:
    """Читает блок памяти по виртуальному адресу GameCube."""
    real_address = GAMECUBE_MEMORY_BASE + (gc_address - 0x80000000)
    return dolphin_process.read(real_address, size)
 
def write_to_game(gc_address: int, data: bytes) -> bool:
    """Записывает блок данных по виртуальному адресу GameCube."""
    real_address = GAMECUBE_MEMORY_BASE + (gc_address - 0x80000000)
    return dolphin_process.write(real_address, data)

Так я нащупал путь вперёд. Но вместе с ним появился новый, кропотливый квест: я превратился в археолога памяти. Нужно было найти точные и стабильные адреса для активного текста диалога и имени текущего собеседника.

Для этого я написал собственный сканер памяти на Python. Процесс превратился в однообразный ритуал:

  • Разговаривал с жителем. В тот миг, когда появлялось окно диалога, я замораживал эмулятор.

  • Сканировал. Скрипт прогонял все 24 миллиона байт RAM GameCube в поисках строки текста с экрана (например, Hey, how's it going?).

  • Сверял. Часто находилось несколько адресов. Тогда я размораживал игру, заводил беседу с другим жителем и искал уже его имя, чтобы понять, какой блок памяти отвечает за активного собеседника.

После многих часов разговоров, заморозок и сканирования я наконец вычислил ключевые адреса: 0x8129A3EA для имени говорящего и 0x81298360 для буфера диалога. Теперь я мог надёжно определять, кто именно говорит, и — что важнее — вписывать собственный текст прямо в окно диалога.

А что насчёт сетевого адаптера GameCube? 🌐

Да, у GameCube был официальный Broadband Adapter (BBA). Но Animal Crossing поставлялась без каких‑либо сетевых примитивов: ни сокетов, ни протоколов на уровне движка. Чтобы использовать BBA здесь, пришлось бы колхозить крошечный сетевой стек и патчить игру так, чтобы она его вызывала. Это значило: внедрять хуки в движок, планировать асинхронный ввод‑вывод, обрабатывать таймауты и повторы — всё внутри кода, который даже не подозревал о существовании сети.

  • Хуки в движке: перехватывать безопасные точки в цикле сообщений, чтобы отправлять и принимать пакеты.

  • Драйвер/протокол: реализовать минимальный интерфейс UDP/RPC поверх BBA.

  • Надёжность: справляться с таймаутами, повторами и неполными чтениями, не зависая при этом в анимации и UI.

Я заменил диалоги в Animal Crossing на нейросеть, взломав память GameCube - 3
Я заменил диалоги в Animal Crossing на нейросеть, взломав память GameCube - 4

Я выбрал «почтовый ящик» RAM, потому что он детерминированный, не требует ни ядра, ни драйверов и остаётся полностью внутри границ эмулятора — без какого‑либо бинарного сетевого стека. Хотя справедливости ради: схема с BBA абсолютно реальна. Более того, это была бы классная задача для будущего — например, для запуска на реальном железе через Swiss + homebrew.

Минимальная RPC‑обёртка для гипотетического BBA‑шима (C):

#include <stdint.h>

/* Минимальная RPC-структура для гипотетического BBA-шима */
typedef struct {
    uint32_t magic;    // 'ACRP'
    uint16_t type;     // 1=запрос, 2=ответ
    uint16_t length;   // длина полезной нагрузки
    uint8_t  payload[512];
} RpcMsg;

int ac_net_send(const RpcMsg* msg);          // отправка через BBA
int ac_net_recv(RpcMsg* out, int timeoutMs); // опрос с таймаутом

Хостовый UDP‑мост, упрощённая версия (Python):

import socket, json
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(("0.0.0.0", 19135))
while True:
    data, addr = sock.recvfrom(2048)
    msg = json.loads(data.decode("utf-8", "ignore"))
    # ... вызов Writer/Director LLM ...
    reply = json.dumps({"ok": True, "text": " Hi from the cloud!"}).encode() // "Привет из облака!"
    sock.sendto(reply, addr)

Разговор на тайном языке игры 🤫

С воодушевлением я попробовал записать в память Hello, world — и… игра зависла. Персонажи продолжали анимированно шевелиться, но диалог не двигался дальше. Так близко — и так далеко.

Оказалось, что я отправлял обычный текст. А Animal Crossing на таком не говорит. У неё свой собственный язык — закодированный, с особыми управляющими символами.

Представьте себе HTML: браузер не просто показывает слова, а распознаёт теги вроде <b>, чтобы сделать текст жирным. Так же и тут. Специальный байт‑префикс CHAR_CONTROL_CODE сообщает движку игры: «Следующий байт — это не буква, а команда».

Эти команды управляют всем: цветом текста, паузами, звуковыми эффектами, эмоциями персонажей и даже завершением разговора. Если не передать код <End Conversation>, игра будет вечно ждать команду, которая так и не придёт. Вот почему всё зависало.

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

Я написал энкодер и декодер на Python. Декодер умел читать сырой участок памяти и переводить его в удобочитаемый вид, а энкодер — брать мой текст с самодельными тегами и превращать его обратно в точную последовательность байтов, понятных GameCube.

Небольшая часть управляющих кодов, которые пришлось кодировать/декодировать (Python):

# Пример набора управляющих кодов для Animal Crossing
CONTROL_CODES = {
    0x00: "<End Conversation>",
    0x03: "<Pause [{:02X}]>",        # напр., <Pause [0A]> — короткая пауза
    0x05: "<Color Line [{:06X}]>",  # напр., <Color Line [FF0000]> — красный
    0x09: "<NPC Expression [Cat:{:02X}] [{}]>", # вызвать эмоцию
    0x59: "<Play Sound Effect [{}]>",  # напр., <Play Sound Effect [Happy]>
    0x1A: "<Player Name>",
    0x1C: "<Catchphrase>",
}

# Магический байт, сигнализирующий: дальше идёт команда
PREFIX_BYTE = 0x7F

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

Создание мозга ИИ 🔮

Когда канал связи заработал, настал момент для самой весёлой части — построения ИИ.

Сначала я решил поручить всё одной LLM: и сочинять диалоги, и держаться в характере, и вставлять технические управляющие коды. Итог вышел хаотичным: искусственный интеллект пытался одновременно быть писателем и программистом, но в обоих ролях проседал.

Решение оказалось простым: я разделил процесс на два модуля — Писателя и Режиссёра.

  • ИИ‑писатель. Его единственная задача — креатив. Он получает подробное досье на персонажа (я собрал их, скрапнув фанатскую вики Animal Crossing) и пишет диалог, который смешной, соответствует его характеру и уместен в текущем контексте.

  • ИИ‑режиссёр. Он берёт чистый текст от Писателя и работает чисто технически: решает, как поставить сцену. Добавляет паузы для драматизма, выделяет слова цветом, подбирает эмоцию на лице или звуковой эффект в точный момент.

Это разделение сработало идеально.

Я заменил диалоги в Animal Crossing на нейросеть, взломав память GameCube - 5

Эффекты «саморазвития» 🤭

Сначала я подключил лёгкую новостную ленту. И буквально через несколько минут жители начали вплетать заголовки в свою болтовню — без промптов, просто на основе контекста.

Митци: «Новости? Европейские лидеры собираются встретиться с Трампом и Зеленским!»

Митци: «Новости? Европейские лидеры собираются встретиться с Трампом и Зеленским!»

Потом я дал им крошечный общий блок памяти для сплетен: кто что сказал, кому и как это восприняли. Предсказуемо всё вылилось в движение против Тома Нука.

Куки: «В городе всё идёт прекрасно, но иногда мне кажется, что Том Нук типа забирает все колокольчики!»

Куки: «В городе всё идёт прекрасно, но иногда мне кажется, что Том Нук типа забирает все колокольчики!»

А потом я вспомнил, что источником новостей у меня была Fox News.

Куки: «Женщина была убита во время ограбления в „синем городе“!»

Куки: «Женщина была убита во время ограбления в „синем городе“!»

Теперь игра превратилась во что‑то странное, уморительное и слегка тревожное :)

Весь код проекта — от интерфейса работы с памятью до энкодера диалогов и логики подсказок для ИИ — лежит на GitHub. Это, пожалуй, один из самых трудных и при этом самых захватывающих экспериментов, которые я когда‑либо делал: сплав реверс‑инжиниринга, ИИ и пристрастия к классике.

Автор: dmitrifriend

Источник

Rambler's Top100