Оригинальный Xbox известен тем, что имел всего 64 мегабайта оперативной памяти, чего даже в то время не всегда хватало играм. В недавнем подкасте о слиянии Bethesda и Xbox директор Bethesda Game Studios Тодд Говард рассказал о том, что именно из-за нехватки памяти и для ее освобождения Morrowind иногда перезагружала Xbox незаметно для пользователя. Долгие внутриигровые загрузки — это как раз то, о чем идет речь.
Это и натолкнуло на рассуждения о том, как же происходил этот процесс. Ведь тихая перезагрузка игровой консоли все еще подразумевает ребут памяти, видеокарты, центрального процессора, кэша и вообще всей системы.
Однако в оригинальном Xbox практически все работало с разрешениями уровня для ядра, что давало определенную свободу программистам и, вероятно, помогало хранить состояние игры где-то в памяти или на жестком диске. И когда игра перезагружалась, она просто возвращалась в том же виде из последнего сохранения.
Теперь настало время доказать это.
Для этого нам понадобятся три вещи:
Копия игры;
Комплект разработчика Xbox (XDK):
Декомпилятор.
В качестве последнего будем использовать IDA Pro.
В первую очередь установим игру на девкит и запустим ее. Для удобства возьмем файл сохранения из Интернета, чтобы понаблюдать, как он будет загружаться посредством экрана загрузки.
Основная сложность в том, что на самом деле мы не знаем, перезагружалась ли система в это время. Сам Тодд Говард упоминал, что на время перезагрузки на экране оставалась картинка, сохраненная во фреймбуфере. Поэтому совершенно неочевидно, происходит ли что-то за кадром.
Но если использовать Xbox Neighborhood для тихой перезагрузки Xbox, мы увидим, что консоль действительно перезагружается — и сразу после этого игра попадает на момент экрана загрузки.
Отсюда вопрос: как все это работает?
Можно припомнить, что раньше существовала конкретная функция в API Xbox, которая могла запускать исполняемый файл Xbox из уже существующего и просто передавать аргументы из старого файла в новый. Если взглянуть на дашборд Xbox, такой как Evolution X, он запускается прямо из исполняемого файла Xbox, но также производит тихую перезагрузку.
Эта часть API Xbox — XLaunchNewImage. Согласно документации, она перезагружает консоль для запуска другого файла XBE с DVD-диска.
При вызове XLaunchNewImage игра должна иметь минимальную активность. Не должно ничего происходить в других потоках, никаких асинхронных задач, записи на жесткий диск и т. д.
Вот мы и получили достаточно весомое доказательство того, что Xbox перезапускается в процессе исполнения Morrowind, при этом сохраняя состояние игры и загружаясь из сохранения. Теперь выясним, как же это работает.
Запустим игру на девките. В этом случае XLaunchNewImage должна перезапустить консоль. Теперь загрузим сохраненную игру с жесткого диска, но перед этим задействуем инструмент под названием Xbox File Event Viewer. Он будет отслеживать и выводить на экран создание каждого нового файла для чтения или записи.
Установим фильтр на названии morrowind.xbe. Если будет происходить запуск файла с таким именем, скорее всего, в этом вызове будет задействована XLaunchNewImage, которая и производит перезапуск консоли для исполнения другого файла XBE с диска.
Теперь запустим Xbox File Event Viewer и наш файл сохранения. Как мы видим, происходят множественные чтения файла morrowind.xbe. Это не обязательно говорит о перезапуске консоли, но наверняка указывает на то, что происходит активность XLaunchNewImage.
Теперь используем инструмент для реверс-инжиниринга и декомпилируем исполняемый файл morrowind.xbe. Для этого загрузим его в IDA Pro. И для начала поищем в нем непосредственно упоминание morrowind.xbe.
Первое, что мы видим в результатах поиска — функцию получения доступа к файлу. Декомпилируем этот кодовый блок.
По-видимому, выделенный путь и исполняет XLaunchNewImage — ведь, как мы видим в документации, один из требуемых для этого параметров — строка для адреса XPE, а второй — данные о запуске. Это вполне себе коррелирует с функцией sub_23AB90. Переименуем ее в XLaunchNewImage.
Но прежде, чем утверждать, что дело раскрыто, взглянем еще раз на код. Стоит обратить внимание на это условие.
Если байт byte_3A3209 имеет значение true, выполняется один кусок кода, в противном случае — другой, заканчивающийся исполнением команды XLaunchNewImage.
Теперь поищем перекрестные ссылки для этого байта. Нас интересует переменная, которая отвечает за перезапись.
Перейдем к ней.
Один из двух байтов здесь говорит о том, чтобы если флаг «No reboot on new game» = 1 в файле Morrowind.ini, тогда этот байт равен true. Что касается второго, искомого байта, он становится true, если выполняется условие «No reboot on load game» = 1 в Morrowind.ini.
Если открыть Morrowind.ini, мы увидим, что оба значения «No reboot on new game» и «No reboot on load game» установлены в нуле, а значит — игра будет перезагружаться на Xbox. Но на девкитах для отладки, когда игра разрабатывалась Bethesda, скорее всего, они имели значения единицы, поскольку те использовали 128 МБ вместо 64.
Итак, мы получили подтверждение того, что консоль перезагружается на экранах загрузки. Но также мы открыли то, что это происходит не только при загрузке, но и создании новой игры, что тоже весьма любопытно. Взглянем теперь на это условие, когда NoRebootOnNewGame = false.
В этом месте происходит загрузка состояния, в котором игра была перед сохранением и перезапуском:
Мы убедились, что Xbox действительно перезагружается для очистки памяти во время экранов загрузки Morrowind, и вместо того, чтобы возвращаться к главному экрану игры, сразу переходит на экран загрузки и вызывает последнее сохранение. Достигается это с помощью процедуры под названием XLaunchNewImage — части API Xbox, которую можно использовать для тихой перезагрузки консоли и запуска исполняемого файла. Но чего Говард не упомянул, так это того, что то же самое происходит при старте новой игры.
Причина такого поведения в том, что проще загрузить игру заново при загрузке сохранения, чем пытаться выгрузить все игровые ассеты в памяти из текущей игры. Это достаточно изящный трюк, который, вероятно, в свое время значительно упростил жизнь команде разработчиков.