Steam Deck · моддинг · ИИ-агент

Как ИИ-агент сам поставил русскую озвучку Path of Exile 2 на Steam Deck

Народная озвучка рассчитана на Windows: установщик на .NET с патчером бандлов через Oodle. А Steam Deck — это Linux. Задача звучала просто: «поставь, я не хочу трогать руками». На деле — пять слоёв граблей Wine, и финальная победа на Proton 9.

полный автомат · по SSH 2026-06-28 патч ~1,5 мин Proton 9.0 + .NET 8 NikiForge
Нужна простая инструкция без терминала? Эта страница — технический разбор «как оно устроено». Если вы просто хотите поставить озвучку на свой Steam Deck — есть пошаговая инструкция для человека с готовым скриптом: poe2-ustanovka-deck.html.
Эпилог. После того как этот патч был поставлен через Proton, автор озвучки выпустил нативную Linux-сборку патчера (с настоящим liboo2core.so). Прогнал её на чистой базе — установка пошла сразу на 98% CPU, без Proton, заменив все 47934 файла за минуту и дав индекс бит-в-бит как у Proton-версии. Игра с ней запустилась идеально — проверено в живой игре. Так что боевой способ теперь — нативный (он и описан в инструкции для человека). Отдельный урок: патчить нужно только на чистую игру — патч поверх уже пропатченной (двойной) ломает загрузку. А разбор ниже остаётся ценным: ntsync-дедлок .NET в свежем Proton переносится на любые .NET-утилиты под Wine.
Что ставили. Народную русскую озвучку Path of Exile 2 — платный авторский проект NGC404, скачать у автора: boosty.to/ngc404. Это не аддон Steam Workshop, а ручной патч игровых бандлов: набор инструментов PatchBundle3 (.NET 8 + нативная oo2core.dll — Oodle-компрессия от RAD/Epic) плюс архив audio.zip с 47 902 файлами .ogg русской речи. Установщик автора — install.bat, рассчитан строго на Windows.

Почему это было нетривиально

Linux
Дек, а патчер — Windows .NET
143 ГБ
игра; ошибка = переустановка
0
экрана — всё по SSH, вслепую
2,9 ГБ
озвучки впечатать в бандлы

Steam Deck работает на SteamOS (Arch Linux), Windows-игры на нём запускает Proton (обёртка над Wine). Установщик озвучки — консольное приложение .NET 8, которое лезет в игровые бандлы и пересобирает их через Oodle. Запустить такое на Linux-консоли удалённо, без графики и без права на ошибку (рядом 143 ГБ боевой установки) — и есть вся соль.

Что внутри установщика

Первым делом — разбор архива, чтобы понять механизм, а не действовать вслепую. Внутри:

Ключ — внутри install.bat. Там видна точная команда (это первоисточник, не догадка):

# из install.bat автора — что он реально запускает:
pushd tools
PatchBundle3.exe  "<путь>\Bundles2\_.index.bin"  "<путь>\Audio.zip"

То есть: патчер принимает индекс бандлов игры и zip с озвучкой, дописывает аудио в бандлы и переписывает индекс. Бэкапит он только _.index.bin — значит откат сводится к возврату индекса.

Архитектура решения

Раз патчер — это .NET-консоль под Windows, на Деке его нужно запустить через Wine/Proton, обеспечив три вещи:

Пошагово — как это делалось

1

Файлы на Дек

Архивы озвучки (три тома RAR, 2,7 ГБ) копируются на Дек по локальной сети. Целостность — сразу проверкой томов.

scp PoE2_NGC404.part*.rar  deck@<IP-Дека>:poe2voice/
ssh deck@<IP> 'cd poe2voice && unrar t "PoE2_NGC404.part1.rar"'   # All OK
2

Распаковка и бэкап индекса

Распаковываем архив, затем — обязательный бэкап индекса игры со сверкой md5. Это точка отката.

unrar x -o+ "PoE2_NGC404.part1.rar" x/        # → tools/ + audio.zip
GAME="$HOME/.local/share/Steam/steamapps/common/Path of Exile 2"
cp -v "$GAME/Bundles2/_.index.bin"  ~/poe2voice/_.index.bin.ORIGINAL.bak
3

Portable .NET 8 рядом с патчером

Патчер — это net8.0, Microsoft.NETCore.App 8.0 (консоль, без WPF — значит хватит базового рантайма). Берём portable-zip и распаковываем; dotnet.exe найдёт рантайм рядом.

curl -sL "https://builds.dotnet.microsoft.com/dotnet/Runtime/8.0.28/dotnet-runtime-8.0.28-win-x64.zip" -o dn.zip
mkdir dotnet && cd dotnet && unzip -q ../dn.zip   # dotnet.exe + shared/
4

Запуск патчера через Proton

Главный вызов. Пути к файлам Wine видит через диск Z: (это корень Linux-ФС). Рабочая папка — tools/, чтобы рядом оказалась oo2core.dll.

export STEAM_COMPAT_DATA_PATH="$HOME/poe2voice/pfx9"
export STEAM_COMPAT_CLIENT_INSTALL_PATH="$HOME/.steam/steam"
mkdir -p "$STEAM_COMPAT_DATA_PATH"
PROTON="$HOME/.steam/steam/steamapps/common/Proton 9.0 (Beta)/proton"
IDX='Z:\home\deck\...\Path of Exile 2\Bundles2\_.index.bin'
ZIP='Z:\home\deck\poe2voice\x\audio.zip'
"$PROTON" run "$HOME/poe2voice/dotnet/dotnet.exe" PatchBundle3.dll "$IDX" "$ZIP"
5

Проверка результата

Успех виден по трём признакам: код выхода 0, изменившийся md5 индекса и выросший на объём озвучки Bundles2.

# индекс ДО:  1b04e942…   ПОСЛЕ: cef9efd2…   → изменился = патч лёг
# Bundles2:  127 209 МБ → 129 830 МБ   (+2,6 ГБ озвучки)
# PATCH_EXIT=0,  время патча ~1,5 минуты

Пять граблей — и как они решились

Прямой путь не сработал ни разу. Самое ценное — как именно патчер сопротивлялся и чем это лечилось. Диагностика шла по wchan (на каком системном вызове спит процесс), CPU и росту файлов.

СимптомНастоящая причинаРешение
Закачка ~2 ч на файлBoosty-CDN из РФ без VPN тянет «через одно место»включить VPN — скорость выросла в разы
SSH рвётся на 255Wi-Fi Дека в power-save рвёт долгие сессииsystemd-run --user — патч живёт юнитом, переживает обрыв
Патчер висит, CPU 0%ntsync в Proton Experimental 11 дедлочит .NET на старте (главный поток спит на ntsync_schedule)уйти с Experimental на Proton 9.0 (зрелый Wine 9.x без ntsync)
Патчер молчит, 0 выводачерез pipe stdout .NET буферизуется блоками — ничего не виднозапуск под PTY (псевдотерминал через Python) — вывод стал виден
Proton 9 падает сразутребует, чтобы папка префикса уже существовалаmkdir -p префикса перед запуском
Развязка. Корнем был именно Proton. На Experimental 11 (с новым механизмом синхронизации ntsync) .NET-патчер намертво вис на старте — все потоки засыпали, CPU 0%. Стоило переключить на Proton 9.0 — и он сразу пошёл на 96% CPU, прочитал audio.zip, начал писать бандлы. Урок: для .NET-утилит под Proton берите зрелую версию, а не Experimental.

Готовый скрипт

Итоговый run_patch.sh — собирает все уроки разом: отключение быстрой синхронизации, Proton 9, создание префикса, бэкап и сверка индекса. Запускается через systemd-run --user --unit=poe2patch bash run_patch.sh, чтобы пережить обрыв SSH.

#!/bin/bash
exec > /tmp/patch.log 2>&1
cd "$HOME/poe2voice/x/tools" || exit 9
export STEAM_COMPAT_DATA_PATH="$HOME/poe2voice/pfx9"
export STEAM_COMPAT_CLIENT_INSTALL_PATH="$HOME/.steam/steam"
export WINEDEBUG=-all
# отключаем быструю синхронизацию — она дедлочила .NET
export PROTON_NO_NTSYNC=1 PROTON_NO_FSYNC=1 PROTON_NO_ESYNC=1
mkdir -p "$STEAM_COMPAT_DATA_PATH"
PROTON="$HOME/.steam/steam/steamapps/common/Proton 9.0 (Beta)/proton"
GAME="$HOME/.local/share/Steam/steamapps/common/Path of Exile 2"
IDX='Z:\home\deck\.local\share\Steam\steamapps\common\Path of Exile 2\Bundles2\_.index.bin'
ZIP='Z:\home\deck\poe2voice\x\audio.zip'

echo "md5 ДО:  $(md5sum "$GAME/Bundles2/_.index.bin" | cut -d' ' -f1)"
"$PROTON" run "$HOME/poe2voice/dotnet/dotnet.exe" PatchBundle3.dll "$IDX" "$ZIP"
echo "PATCH_EXIT=$?"
echo "md5 ПОСЛЕ: $(md5sum "$GAME/Bundles2/_.index.bin" | cut -d' ' -f1)"

Важные предупреждения

Не запускайте «Проверить целостность файлов» в Steam. Steam увидит изменённые бандлы как «повреждённые», перекачает оригиналы и сотрёт озвучку. Это же — аварийный способ всё вернуть, если что-то пошло не так.
Обновление игры сбросит озвучку. Любой патч PoE2 перезапишет бандлы — озвучку нужно будет поставить заново (повтор скрипта, ~2 минуты). Бэкап индекса держите рядом.

Итог

озвучка установлена
~1,5 мин
сам патч (Proton 9)
47 902
файла речи в бандлах
5
слоёв граблей пройдено

Windows-патчер на .NET, рассчитанный на ручную установку под Windows, был запущен на Linux-консоли Steam Deck — удалённо, без графики, полностью автоматически. Главным врагом оказался не сам патчер, а экспериментальная синхронизация в свежем Proton; зрелый Proton 9.0 всё решил. Метод переносится на любые .NET-утилиты, которые нужно прогнать под Proton на Деке.