Windows 8.1 DirectDraw и совместимость со старыми играми
Я пытаюсь запустить старую игру (Nascar Heat 2002) на своем ноутбуке с Windows 8.1. Проблема, с которой я сталкиваюсь, заключается в том, что игра вылетает перед запуском, и в журналах сообщается, что видео-диск недоступен. Это файл журнала:
41.37.114: data directory: C:\Program Files (x86)\Hasbro Interactive\NASCAR Heat\Data\
41.37.115: Config Dir: C:\Program Files (x86)\Hasbro Interactive\NASCAR Heat\
41.37.274: ddraw: created directdraw with aticfx32.dll (AMD Radeon HD 8650G + HD 8600/8700M Dual Graphics)
41.37.274: ddraw: version 0.0.0.0
41.40.904: vid: 0 meg card (reported:0.523438)
41.40.904: vid: using AGP textures (1397), total: 0
41.40.905: unsupported: 0 megs of vram"
Насколько я могу судить, версия DirectDraw с Windows 8.1 не совместима со старыми играми, такими как эта. Я пробовал использовать библиотеки WineD3D, среди других оболочек / хаков ddraw, но безрезультатно. Итак, мой вопрос заключается в следующем: есть ли способ заставить эмулируемое количество vram (на моей карте есть видеопамять) либо в Windows, либо в оболочке ddraw, чтобы гарантировать, что эта игра обнаружит это? Я обновил драйверы катализатора до последней версии и имею среду выполнения Microsoft DirectX 9.0c для конечных пользователей
1 ответ
У меня есть скрытое подозрение...
Я чувствую запах рутинной процедуры обнаружения видеокарт. Это не имеет ничего общего с Windows и \ или DirectDraw (ну, частично, но не так, как вы думаете). Это просто старая игра, делающая предположения, которые больше не действительны. Это не редкость. Например, игра Oni вылетает на современных видеокартах:
Эта проблема была связана с переполнением определенного текстового буфера - тот, который перечисляет расширения OpenGL в
startup.txt
файл. Когда был написан Oni, дамп списка расширений OpenGL был намного короче, и разработчики не допускали большего дампа. Современные видеокарты почти всегда вызывают это переполнение.
Нам нужно идти глубже
У меня нет Nascar Heat 2002, но я скачал демоверсию NASCAR Heat, и она демонстрирует точно такую же проблему. Так что я откинул свой отладчик и дизассемблер и провел вечер, пытаясь выяснить, что не так с игрой.
На самом деле игра состоит из двух исполняемых файлов, взаимодействующих друг с другом через семафор: основной исполняемый файл (NASCAR Heat Demo.exe
в моем случае) и собственно игровой движок (.\run\race.bin
). Процедура обнаружения видеокарты находится в race.bin
, На запуске игры основные исполняемые копии race.bin
в папку Windows TEMP как heat.bin
и запускает его оттуда. Если вы попытаетесь переименовать race.bin
в race.exe
и запустите его, он найдет семафор, который должен быть создан основным исполняемым файлом, и, если он не найден, отобразит это сообщение:
После разборки и быстрого просмотра ссылок на строки я обнаружил вызов функции, который печатает vid: 0 meg card (reported:0.523438)
сообщение. На самом деле это часть процедуры определения размера памяти видеокарты, которая в псевдокоде выглядит следующим образом (упрощенно):
RawVidMemSize = GetVidMemSizeFromDirectDraw()
// Add 614400 bytes (600Kb - 640x480 mode?) to vidmem size (what for?!)
RawVidMemSize = RawVidMemSize + 614400
if (RawVidMemSize < 2000000)
{
MemSize = 0
}
else
{
if (RawVidMemSize < 4000000)
{
MemSize = 2
}
if (RawVidMemSize < 8000000)
{
MemSize = 4
}
if (RawVidMemSize < 12000000)
{
MemSize = 8
}
if (RawVidMemSize < 16000000)
{
MemSize = 12
}
if (RawVidMemSize < 32000000)
{
MemSize = 16
}
if (RawVidMemSize < 64000000)
{
MemSize = 32
}
if (RawVidMemSize > 64000000)
{
MemSize = 64
}
}
Для тех, кто интересуется, вот фактический поток управления функцией от IDA с моими комментариями. Полноразмерное изображение по клику.
Теперь пришло время посмотреть, что происходит внутри этой процедуры. Я использовал классический трюк break & enter (пропатчил первую инструкцию на race.bin
точка входа с int3
), запущен NASCAR Heat Demo.exe
и ждал появления отладчика. И вот, когда все стало ясно.
Размер видеопамяти возвращается из GetVidMemSizeFromDirectDraw()
является 0xFFFF0000
(4294901760 bytes = 4095MB
) и это не имеет ничего общего с реальной вещью (должно быть 1 Гб на моем ПК). Оказывается, что DirectDraw не очень подходит для современной архитектуры видеокарт \ ПК
С ростом физической памяти, как RAM, так и VRAM, у этого API также возникают проблемы с копированием, поскольку он возвращает 32-битные значения DWORD размера в байтах.
и имеет тенденцию сообщать обо всем, на что это похоже:
У вас есть система с 1 ГБ или больше видеопамяти и 4 ГБ или больше системной памяти (ОЗУ).
Вы запускаете инструмент диагностики Direct-X, и он сообщает, что у вас есть неожиданно низкий объем приблизительной общей памяти на вкладке дисплея.
Вы также можете столкнуться с проблемами в некоторых играх или приложениях, не позволяющих выбрать самые подробные настройки.
API, который DXDiag использует для аппроксимации системной памяти, не был разработан для обработки систем в этой конфигурации
В системе с 1 ГБ видеопамяти следующие значения возвращаются с соответствующей системной памятью:
╔═══════════════╦═══════════════════════════════════╗
║ System Memory ║ Reported Approximate Total Memory ║
╠═══════════════╬═══════════════════════════════════╣
║ 4GB ║ 3496MB ║
║ 6GB ║ 454MB ║
║ 8GB ║ 1259MB ║
╚═══════════════╩═══════════════════════════════════╝
Так что в моем случае он просто сообщает значение, которое почти вписывается в 32-разрядное целое число. И вот тут дела идут плохо. Помните эту строку?
RawVidMemSize = RawVidMemSize + 614400
Становится так:
RawVidMemSize = 4294901760 + 614400 (= 4295516160)
А также 4295516160
является 548865
может обрабатывать более 32-битное значение (0xFFFFFFFF = 4294967295
). Следовательно, целочисленное переполнение и конечный результат 548864
, Так что теперь игра думает, что мой размер vidmem составляет колоссальные 536 КБ и отказывается запускаться.
Вы можете проверить это самостоятельно в этом онлайн-эмуляторе сборки x86. Введите код ниже, в правом левом углу нажмите Windows
и проверить Registers
флажок. Нажмите на Step
кнопка и смотреть, как 0xFFFF0000
в EAX регистр становится 0x00086000
с Carry
флаг. Если вы нажмете на значение регистра, он будет переключаться между шестнадцатеричным и десятичным представлением числа.
mov eax, 0xFFFF0000
add eax, 0x96000
Как мне это исправить?
DirectDraw, вероятно, никогда не вернет значение, которое может обработать больше, чем 32-разрядное целое число (кажется, что оно ограничено, чтобы соответствовать размеру памяти. Поэтому самый простой способ решить эту проблему - удалить RawVidMemSize = RawVidMemSize + 614400
операция из кода, поэтому он не будет вызывать переполнение. В исполняемом файле это выглядит так:
- Мнемоника сборки:
add eax, 96000h
- Фактические коды операций (шестнадцатеричные):
0500600900
Чтобы удалить его, нам нужно заменить его инструкциями NOP (hex: 90
). Я уже знаю смещение файла, но оно может отличаться в вашем исполняемом файле. К счастью, шестнадцатеричная строка 0500600900
уникален в моем race.bin
и наверное в твоем. Так что получите hex-редактор (я рекомендую HxD: он бесплатный, портативный и простой в использовании) и откройте bin
файл.
Выполните поиск в шестнадцатеричной строке:
Как только шестнадцатеричная строка найдена
Замени это 90
Сохраните файл. HxD автоматически создаст резервную копию файла, которую вы сможете восстановить, если что-то пойдет не так.
В моем случае этого было достаточно, и я смог начать игру. Вот как heat.log
выглядит после патча:
21.33.564: ddraw: создан directdraw с помощью aticfx32.dll (AMD Radeon HD 5800 Series)
21.33.564: ddraw: версия 0.0.0.0
21.34.296: видео: 64 Мб (сообщено: 4095.937500)
21.34.296: vid: использование текстур AGP (3231), всего: 64
21.34.305: vid: тройной буфер включен
Если ваш файл случайно будет содержать несколько случаев 0500600900
, замените первый, затем попробуйте запустить игру и, если не работает, восстановите файл из резервной копии и попробуйте затем. Не заменяйте все сразу, это не очень хорошая идея.
Также было подтверждено, что такая же ошибка существует в Viper Racing. Viper Racing использует немного другую (более старую?) Версию игрового движка, чем Nascar, но ошибка та же: она тоже пытается добавить 614400
байтов к объему видеопамяти. Значения для поиска отличаются, потому что в этом случае компилятор решил не использовать регистры и просто получил доступ к переменной из стека, то есть:
- Мнемоника сборки:
add [esp+18h+var_14], 96000h
- Фактические коды операций (шестнадцатеричные):
8144240400600900
Приятного вождения!
- Это одно из тех предположений, о которых я говорил.