Dark Souls на iPhone: какие баги в Madeira пришлось исправить, чтобы ПК-версия вообще заработала
С конца сентября я запускал PC-версии Dark Souls на iPhone Air через эмулятор Madeira. Remastered теперь проходится с экранным геймпадом, а в Dark Souls III на экспериментальной сборке медианный FPS в тяжёлых сценах вырос с 20 примерно до 36,7. По ходу работы накопились 5 принятых и 11 открытых pull request’ов в Madeira, Wine, FEX и DXMT, а также репозиторий со скриптами, который я теперь публикую.
Сразу уточню: телефон у меня один (iPhone Air с бета-версией iOS 27.2), тестер тоже один — я сам, и все приведённые цифры сняты именно на этом устройстве. Игры у меня собственные, купленные. В репозитории нет ни игровых файлов, ни редакторов сохранений, ни средств обхода защиты, и подобные материалы я принимать не буду.
Что это такое
Madeira запускает Windows-программы для x64 прямо на iPhone. Под капотом работают три слоя:
-
FEX-Emu на лету (через JIT) транслирует x86-64 в ARM64;
-
Wine предоставляет игре привычный Windows API;
-
DXMT преобразует вызовы Direct3D 11 в Metal.
Для JIT на телефоне без джейлбрейка я использую StikDebug: встроенному помощнику Madeira нужен отдельный App ID, а скрипт подписи этот помощник убирает.
Проект сильный, но по принципу «поставил и запустил» тяжёлую игру не запустить. Надо знать, с какими правами переподписывать приложение, какие из десятков параметров конфигурации действительно на что-то влияют и как по логу определить, из-за чего именно произошёл сбой. Эти неочевидные знания я зафиксировал, а рутину автоматизировал.
Dark Souls Remastered: восемь препятствий подряд
Работа шла с 27 сентября по 19 октября 2026 года. Каждый шаг выглядел одинаково: один запуск, один лог, одна правка. Лог разбирает небольшой скрипт tools/analyze-log.py. Он показывает, что Madeira на самом деле приняла из окружения, в каком порядке загрузились модули, какие возникли исключения (вместе с регистрами), запустились ли графика, окно и звук и чем всё завершилось.
Полная таблица с причинами сбоев находится в docs/bugs‑and‑fixes.md. Здесь расскажу о самых запоминающихся.
Падение при старте, c0000018. У exe-файла фиксированная база лежит ниже 64 МБ, а ему требуется окно по адресу 0x140000000, которое не выделялось. В апстриме это уже исправили (Madeira #111), мне оставалось лишь подтянуть изменение.
Падение после первых записей в код. Игру защищает слой, который по ходу работы переписывает собственный код, и именно здесь всё рушилось. Причина — утечка в Wine: на обратном пути [exec-req] в ntdll для ARM64EC не вызывался leave_syscall_callback(), из-за чего флаг «мы внутри системного вызова» оставался поднятым. Исправление уместилось в одну строку — wine #18, оно принято.
Падение в steamclient64, c0000005. Здесь история любопытнее. Клиент Steam обрезает HMODULE до 32 бит, а на iOS нельзя отобразить память ниже границы в 4 ГБ. Поэтому модуль располагается по высокому адресу, и после усечения указатель никуда не ведёт. Я добился того, что образу с «суженной» базой выдаётся низкое имя, а VirtualQuery помечает свободной только ту память, которую действительно можно занять. Это Madeira #385, пока он остаётся открытым. Из-за этого моя сборка для телефона представляет собой апстрим плюс один пока не принятый коммит, без которого DSR падает на старте.
Падение при поиске папки настроек. В Wine-профиле C:\users\ попросту отсутствовала папка AppData. Теперь нужные каталоги создаёт скрипт (device.py profile fix), и его же вызывает apply-config.sh.
Геймпад: три бага в одной цепочке
Эта часть отняла больше всего времени: симптомы были разными, а виноваты оказались три независимых места.
-
Игра не обнаруживает геймпад. В её папке лежит собственный
xinput1_3.dll, и загрузчик выбирал его вместо встроенного. Решение: загрузчик теперь отдаёт предпочтение встроенному XInput (wine #19, принят). -
Игра геймпад обнаруживает, но не реагирует на него. Окно игры не было активным, а на iOS активировать его некому. Теперь
win32uделает главное окно процесса активным, пока оно опрашивает XInput (Madeira #149, принят). -
Касание мимо экранной кнопки превращалось в клик мышью. Теперь, пока на экране показана раскладка геймпада, такие касания как мышь не учитываются (Madeira #150, принят).
Помогли два инструмента: адреса возврата в диагностике и журналирование цепочки нажатий клавиш. По ним удалось найти разрыв. Подобные диагностические патчи теперь всегда у меня под рукой.
Вылет через 35 секунд в меню настроек
Виновником оказался собственный сторожевой механизм эмулятора. Он накопительно считал обработанные записи самомодифицирующегося кода, а в меню настроек игра переписывает код на каждом кадре, по кругу обходя семь клавиш. До порога в 2000 счётчик добирался примерно за полминуты, после чего процесс убивался. Теперь учитываются только подряд идущие одинаковые сбои в рамках одного потока (Madeira #148, принят).
После этих правок меня упомянули в Settings › Credits самой Madeira: «Controller fixes … and a crash‑guard fix».
Из этой серии я вынес одно правило: проверяй то, что реально загружено, а не то, что записано в конфиге. Размер образа и базовый адрес в логе обманывают реже, чем файл настроек. И второе: чистая сборка на CI надёжнее «сборки из того, что было под рукой». Одна из моих сборок, собранная из смеси старых и новых компонентов, падала в signal_set_full_context, а чистая сборка с CI — нет.
Dark Souls III: с 20 до 36,7 FPS
DS3 заметно тяжелее. Steam в ней устанавливается изнутри Madeira (Madeira Dock) с отдельным аккаунтом. На заставках игра выдаёт 60 FPS, а в бою на настройках по умолчанию — 17–20.
Дальше началась кропотливая работа с замерами. Я разбирал логи по фазам: FPS, загрузка GPU, частоты ядер, количество draw-вызовов на кадр. Парсеры лежат в tools/ds3perf/, а выводы с пометками «факт» и «версия» собраны в docs/perf/ — это 22 заметки на русском языке.
Телефон перегревается и снижает частоты
Это самая неприятная находка из DS3-POWER.md. В одной из длинных сессий в первые секунды игры P-ядра работали на 2,8 ГГц и обеспечивали около 34 FPS. Примерно на 59-й секунде включилось ограничение: частота опустилась к 1,8 ГГц, а медианный FPS упал до 20,6. На 175-й секунде iOS объявила термальное состояние serious, но на результат это уже не повлияло. Поэтому любую оптимизацию нужно измерять только на горячем телефоне — иначе цифры получаются красивыми, но бесполезными.
Что сработало
Наборы изменений я обозначал буквами. Вот таблица из docs/compatibility.md, где указана медиана в тяжёлых сценах:
|
Шаг |
FPS |
Что изменилось |
|---|---|---|
|
базовый набор C2 |
~17-20 |
разделение пулов, отказ от копирования образа, пометка «осиротевших» блокировок |
|
G1 |
21,5 |
облегчённая диагностика, синхронизация внутри процесса, быстрый путь критических секций для x64, окна записи для «горячих» страниц кода |
|
H2 |
36,7 |
повторное использование буферов и списков команд в DXMT, быстрый TLS, облегчённые SRW-блокировки, QoS потоков по имени |
Для H2 в тяжёлых сценах медиана составила 36,7 FPS, а p10 — 28,8 в первые минуты 12-минутного прогона. После прогрева, когда P-ядра работают на 1,31 ГГц, держится 32–34 FPS. При этом GPU загружен примерно на 88%: теперь производительность ограничивает именно он, а не процессор.
А теперь об ограничениях. Цифра 36,7 — это совокупный результат нескольких изменений. Вклад каждого по отдельности я не измерял, и в каждом PR об этом сказано прямо. Для G1 был выполнен 26-минутный прогон без зависаний с медианой 21,5.
Что я проверил и отверг:
-
MetalFX: стало хуже;
-
pool-low: стало хуже; -
пропуск записей самомодифицирующегося кода с теми же байтами: не имеет смысла, поскольку в 93% случаев записи байты меняют.
Отдельно о DXMT. Выяснилось, что он создаёт около тысячи Metal-буферов на кадр и примерно 50 раз за кадр выделяет и освобождает по 2 МБ под списки команд. Два ключа (DXMT_DYN_KEEP и DXMT_CMDLIST_KEEP_BLOCK) устраняют эту лишнюю возню; оба по умолчанию выключены (dxmt #42, #43).
Чёрный экран и зависание в DS3
Это два отдельных бага DXMT, оба описаны в DS3-CRASH.md и DS3-FREEZE.md.
-
Чёрный экран: на запрос описания SRV для «всех мип-уровней» DXMT возвращал
MipLevels = -1. Игра воспринимает это число как количество уровней, выделяет под него массив и падает. Теперь возвращается разрешённое значение (dxmt #40). -
Зависание DS3: бэкпорт исправления из 3Shain/dxmt, где циклы маршалинга аргументов отрисовки для GS/TS не продвигались вперёд (dxmt #41).
Геймпад и сохранения в DS3 я всесторонне не проверял — в репозитории это отмечено в разделе «не тестировалось».
Что есть в репозитории
Репозиторий madeira‑kit — это обвязка вокруг цикла «сборка, подпись, установка, настройка, запуск, чтение лога, одна правка». Форка Madeira здесь нет: исправления я отправлял в апстрим, а в каталоге patches/ лежат лишь справочные копии.
-
unpack.sh,swap-dll.sh,sign.sh,install.sh: распаковать сборку, подложить патченые DLL, переподписать собственным сертификатом и установить с сохранением данных приложения; -
doctor.sh: предстартовая проверка (телефон виден, приложение установлено, свободно не менее 3 ГБ, копия игры на месте); -
apply-config.sh: загрузитьmadeira.cfg, прочитать его обратно и сверить, создать недостающие папки профиля; -
pull-log.shиanalyze-log.py: забрать лог и сразу его разобрать; -
готовые профили для DS1 и DS3 в
configs/.
Минимальный путь выглядит так:
git clone https://github.com/meshoklv/madeira-kit && cd madeira-kit
cp scripts/config.example.env scripts/config.env && $EDITOR scripts/config.env
scripts/unpack.sh ~/Downloads/Madeira-build.zip && scripts/sign.sh && scripts/install.sh
scripts/doctor.sh
scripts/apply-config.sh configs/ds1-play.cfg
# смахнуть Madeira, включить JIT в StikDebug, запустить игру
scripts/pull-log.sh first-run
Вам понадобятся Mac с Xcode command line tools, iPhone, подключённый по USB, сертификат и профиль для вашего bundle id, а также pip3 install pymobiledevice3. Entitlements на расширенную память и расширенное виртуальное адресное пространство требуют платного аккаунта разработчика.
Если вы собираете Madeira на GitHub Actions самостоятельно, все подводные камни описаны в docs/guide.md: LLVM только в виде статических библиотек, TUNE_CPU=cortex-a78 для FEX, заглушка для ri_page_wait_time_mach. Windows-часть DXMT этот CI не пересобирает.
Чего я не знаю
-
Скрипты в репозитории — это мой рабочий конвейер, из которого я удалил личные идентификаторы. После этого я проверил синтаксис, но целиком на устройстве их не прогонял.
-
Другие модели iPhone и версии iOS не проверялись, как и свежий клон репозитория на второй машине.
-
FPS и нагрев в самой Dark Souls Remastered я не измерял — могу сказать лишь, что в неё можно играть.
-
Все цифры по DS3 получены на одном телефоне и небольшом числе прогонов, чаще всего — одном прогоне на конфигурацию.
-
Бесплатный аккаунт разработчика может не предоставить нужных прав.
Как помочь проекту
Если у вас другой iPhone или iPad, пришлите отчёт о совместимости: модель, версию iOS, игру, конфиг и результат. Принимаются профили с измеренными показателями. Не принимаются кряки, файлы игр и всё, что обходит защиту.
Часть кода и патчей я писал вместе с ИИ-ассистентом (Claude) — в коммитах апстрим-PR это видно по строке Co-Authored-By.
Репозиторий: github.com/meshoklv/madeira‑kit; скрипты и документация распространяются по лицензии MIT, а патчи следуют лицензиям тех проектов, для которых написаны. Issues и PR открыты. Заметки и новые сборки я публикую в канале t.me/meshoklv. Если статья показалась вам полезной, звезда на репозитории поможет другим его найти.
Mike Meshok
Assassin’s Creed Black Flag Resynced получит новый режим, подтвердили разработчики
Ueni Trading повторно откроет предзаказ на часы по мотивам Code Vein II 26 октября
Shadow of the Tomb Raider неожиданно появилась на Switch 2, и теперь на консоли можно сыграть во всю трилогию
Бывший продюсер объяснил, почему EA может продать Mass Effect после закрытия BioWare
Вышел трейлер ретро-RPG Final Fantasy Resonance от Square Enix
Модель показала косплей Шэдоухарт из Baldur’s Gate 3, жрицы богини тьмы Шар
Xbox запустила тестирование платных пакетов времени для облачного гейминга