Windows 8.1 DirectDraw 和与旧游戏的兼容性

Cal*_*b C 3 video directx vram windows-8.1

我正在尝试在我的 Windows 8.1 笔记本电脑上运行一个旧游戏(Nascar Heat 2002)。我遇到的问题是游戏在启动前崩溃,并且在日志中报告没有可用的视频内存。这是日志文件:

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"
Run Code Online (Sandbox Code Playgroud)

据我所知,包含 Windows 8.1 的 DirectDraw 版本与诸如此类的旧游戏不兼容。我曾尝试使用 WineD3D 的库,以及其他 ddraw 包装器/黑客,但无济于事。所以我的问题是:有没有办法在 Windows 或 ddraw 包装器中强制模拟数量的 vram(我的卡确实有视频 ram),以确保该游戏检测到它?我已更新到最新的催化剂驱动程序并拥有 Microsoft DirectX 9.0c 最终用户运行时

bea*_*ker 8

我有一个潜移默化的怀疑...

我可以闻到视频卡检测程序从这里变坏了。这与 Windows 和\或 DirectDraw 无关(部分确实如此,但与您的想法不同)。这只是一个旧游戏做出了不再有效的假设。这并不罕见。例如 Oni 游戏在现代显卡上崩溃

此问题已被追溯到特定文本缓冲区的溢出 -startup.txt 文件中列出了 OpenGL 扩展的那个缓冲区。在编写 Oni 时,OpenGL 扩展列表转储要短得多,开发人员不允许更大的转储。现代显卡几乎总是导致这种溢出。

我们需要更深入

我没有 Nascar Heat 2002,但我下载了NASCAR Heat Demo,它显示了完全相同的问题。所以我打开了我的调试器反汇编器,花了一个晚上试图找出游戏出了什么问题。

游戏实际上由两个可执行文件组成,通过信号量相互通信:主要的可执行文件(NASCAR Heat Demo.exe在我的例子中)和实际的游戏引擎 ( .\run\race.bin)。显卡检测例程在race.bin. 在游戏启动时,将主要的可执行文件复制race.bin到 Windows TEMP 文件夹heat.bin并从那里运行它。如果您尝试重命名race.binrace.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
        }
}
Run Code Online (Sandbox Code Playgroud)

对于感兴趣的人,这里是来自 IDA 的函数的实际控制流和我的评论。全尺寸图像onclick。

显卡内存大小检测

现在是时候实际看看这个过程中发生了什么。我使用了一个经典的 break & enter 技巧(用 修补了race.bin入口点的第一条指令int3),启动NASCAR Heat Demo.exe并等待调试器弹出。这就是事情变得清晰的时候。

从返回的显存大小GetVidMemSizeFromDirectDraw()0xFFFF00004294901760 bytes = 4095MB),它是无关与真品(应该是1Gb的我的电脑)。事实证明DirectDraw 不太适合现代显卡\PC 架构

随着物理内存 RAM 和 VRAM 的增长,此 API 也遇到了应对问题,因为它返回大小为字节的 32 位 DWORD 计数。

并且倾向于报告任何感觉

您的系统具有 1GB 或更大的视频内存,以及 4GB 或更大的系统内存 (RAM)。

您运行 Direct-X 诊断工具,它会报告显示选项卡上的近似总内存量意外低。

您可能还会看到某些游戏或应用程序的问题,不允许您选择最高细节设置。

DXDiag 用于近似系统内存的 API 并非旨在处理此配置中的系统

在具有 1GB 视频内存的系统上,以下值与关联的系统内存一起返回:

?????????????????????????????????????????????????????
? System Memory ? Reported Approximate Total Memory ?
?????????????????????????????????????????????????????
? 4GB           ? 3496MB                            ?
? 6GB           ? 454MB                             ?
? 8GB           ? 1259MB                            ?
?????????????????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

所以在我的情况下,它只是报告几乎适合 32 位整数的值。这就是事情变得糟糕的地方。还记得这条线吗?

?????????????????????????????????????????????????????
? System Memory ? Reported Approximate Total Memory ?
?????????????????????????????????????????????????????
? 4GB           ? 3496MB                            ?
? 6GB           ? 454MB                             ?
? 8GB           ? 1259MB                            ?
?????????????????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

变成这样:

RawVidMemSize = RawVidMemSize + 614400
Run Code Online (Sandbox Code Playgroud)

并且429551616054886532 位以上的值可以处理 ( 0xFFFFFFFF = 4294967295)。因此整数溢出,最终结果为548864。所以现在,游戏认为我的 vidmem 大小高达536KB并且拒绝运行。

您可以在此在线 x86 汇编仿真器中自行检查。输入下面的代码,在左上角单击Windows并选中Registers复选框。点击Step按钮,看如何0xFFFF0000EAX寄存器成为0x00086000具有Carry标志。如果您单击寄存器值,它将在数字的十六进制和十进制表示之间切换。

mov    eax, 0xFFFF0000
add    eax, 0x96000
Run Code Online (Sandbox Code Playgroud)

我如何解决它?

DirectDraw 可能永远不会返回超过 32 位整数可以处理1 的值(无论实际内存大小如何,它似乎都被限制了。所以解决这个问题的最简单方法是RawVidMemSize = RawVidMemSize + 614400从代码中删除操作,所以它不会t 触发溢出。在可执行文件中,它看起来像这样:

  • 汇编助记符: add eax, 96000h
  • 实际操作码(十六进制): 0500600900

要删除它,我们需要用NOP指令(十六进制:)替换它90。我已经知道文件偏移量,但它在您的可执行文件中可能有所不同。幸运的是,十六进制字符串0500600900在我的race.bin和可能在你的都是独一无二的。因此,获取十六进制编辑器(我推荐HxD:它是免费的、便携的且易于使用)并打开您的bin文件。

进行十六进制字符串搜索:

十六进制字符串搜索

一旦找到十六进制字符串

找到十六进制字符串

将其替换为 90

修补!

保存文件。HxD 将自动创建文件的备份,如果出现问题,您可以恢复。

就我而言,这就足够了,我可以开始游戏了。以下是heat.log补丁的处理方式:

21.33.564:ddraw:使用 aticfx32.dll(AMD Radeon HD 5800 系列)创建 directdraw
21.33.564:ddraw:版本 0.0.0.0
21.34.296:vid:64 meg 卡(报告:4090.93.237
使用:4095.93237)AGP 纹理 (3231),总计:64
21.34.305:vid:三重缓冲开启

如果您的文件偶然包含多次出现的0500600900,请替换第一个,然后尝试开始游戏,如果不起作用,从备份中恢复文件并尝试下一步。不要一次更换所有东西,这不是一个好主意。

也已确认Viper Racing 中存在相同的错误。Viper Racing 使用的游戏引擎版本与 Nascar 略有不同(较旧?),但错误是相同的:它也尝试将614400字节添加到视频内存大小。要搜索的值不同,因为在这种情况下,编译器决定不使用寄存器而只从堆栈访问变量,即:

  • 汇编助记符: add [esp+18h+var_14], 96000h
  • 实际操作码(十六进制): 8144240400600900

快乐驾驶!

  1. 这是我一直在谈论的假设之一。