Ich habe einen schleichenden Verdacht ...
Ich kann eine Videokarten-Erkennungsroutine von hier aus riechen. Das hat nichts mit Windows und \ oder DirectDraw zu tun (teilweise auch, aber nicht so wie Sie denken). Es ist nur ein altes Spiel, das Annahmen macht, die nicht länger gültig sind. Es ist nicht ungewöhnlich. Zum Beispiel stürzt Oni-Spiel auf modernen Videokarten ab :
Dieses Problem wurde auf den Überlauf eines bestimmten Textpuffers zurückgeführt, der die OpenGL-Erweiterungen in der
startup.txt
Datei auflistet . Als Oni geschrieben wurde, war der Speicherauszug der OpenGL-Erweiterungsliste viel kürzer und die Entwickler erlaubten keinen größeren Speicherauszug. Moderne Grafikkarten verursachen fast immer diesen Überlauf.
Wir müssen tiefer gehen
Ich besitze zwar nicht Nascar Heat 2002, aber ich habe eine NASCAR Heat-Demo heruntergeladen, und es ist genau das gleiche Problem. Also habe ich meinen Debugger und den Disassembler entfernt und einen Abend damit verbracht, herauszufinden, was mit dem Spiel nicht stimmt.
Das Spiel besteht eigentlich aus zwei ausführbaren Dateien, die über Semaphore miteinander kommunizieren : die ausführbare Hauptdatei ( NASCAR Heat Demo.exe
in meinem Fall) und die eigentliche Spiel-Engine ( .\run\race.bin
). Die Videokarten-Erkennungsroutine befindet sich im race.bin
. Beim Start des Spiels wird die ausführbare Hauptdatei race.bin
in den Windows-TEMP-Ordner kopiert heat.bin
und von dort ausgeführt. Wenn Sie versuchen, umbenennen race.bin
zu race.exe
und führen Sie es, sucht es nach Semaphore, die durch das Hauptprogramm erstellt werden soll, und wenn es nicht zeigt diese Meldung gefunden:
Nach einer Demontage und einem kurzen Blick auf die String-Referenzen habe ich einen Funktionsaufruf gefunden, der eine vid: 0 meg card (reported:0.523438)
Nachricht ausgibt. Es ist eigentlich ein Teil des Verfahrens zur Erkennung der Speichergröße einer Videokarte, das im Pseudocode so aussieht (zu stark vereinfacht):
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 } }
Für Interessierte ist hier der tatsächliche Kontrollfluss der Funktion von der IDA mit meinen Kommentaren. Vollbild auf Klick.
Nun ist es an der Zeit, sich genau anzusehen, was in diesem Verfahren passiert. Ich habe einen klassischen Break & Enter-Trick benutzt (den ersten Befehl am race.bin
Einstiegspunkt mit gepatcht int3
), gestartet NASCAR Heat Demo.exe
und darauf gewartet, dass der Debugger auftaucht. Und dann wurden die Dinge klar.
Die von GetVidMemSizeFromDirectDraw()
is 0xFFFF0000
( 4294901760 bytes = 4095MB
) zurückgegebene Videospeichergröße hat nichts mit der Realität zu tun (sollte auf meinem PC 1 GB betragen). Es stellt sich heraus, dass DirectDraw für die moderne Videocard- / PC-Architektur nicht gut geeignet ist
Mit dem Wachstum von physischen Speichern, sowohl RAM als auch VRAM, hat diese API auch Probleme beim Kopieren, da sie 32-Bit-DWORD-Zählungen der Größe in Byte zurückgibt.
und neigt dazu zu berichten, wie es sich anfühlt :
Sie haben ein System mit mindestens 1 GB Videospeicher und 4 GB oder mehr Systemspeicher (RAM).
Sie führen das Direct-X-Diagnosetool aus und meldet, dass auf der Registerkarte der Registerkarte "Ungefährer Gesamtspeicher" unerwartet wenig vorhanden ist.
Möglicherweise werden auch Probleme mit einigen Spielen oder Anwendungen angezeigt, bei denen Sie nicht die höchsten Detaileinstellungen auswählen können.
Die API, die DXDiag verwendet, um den Systemspeicher anzunähern, wurde nicht für Systeme in dieser Konfiguration entwickelt
Bei einem System mit 1 GB Videospeicher werden die folgenden Werte mit dem zugehörigen Systemspeicher zurückgegeben:
╔═══════════════╦═══════════════════════════════════╗ ║ System Memory ║ Reported Approximate Total Memory ║ ╠═══════════════╬═══════════════════════════════════╣ ║ 4GB ║ 3496MB ║ ║ 6GB ║ 454MB ║ ║ 8GB ║ 1259MB ║ ╚═══════════════╩═══════════════════════════════════╝
In meinem Fall wird also nur der Wert gemeldet, der fast in die 32-Bit-Ganzzahl passt. Und da laufen die Dinge schlecht. Erinnere dich an diese Zeile?
RawVidMemSize = RawVidMemSize + 614400
Es wird so:
RawVidMemSize = 4294901760 + 614400 (= 4295516160)
Und 4295516160
ist 548865
mehr als 32-Bit-Wert ( 0xFFFFFFFF = 4294967295
). Also der Integer-Überlauf und das Endergebnis ist 548864
. Das Spiel glaubt nun, dass meine Vidmem-Größe 536 KB groß ist und sich weigert zu laufen.
Sie können dies in diesem Online-Emulator für x86-Assemblys selbst überprüfen . Geben Sie den Code ein, klicken Sie in der rechten linken Ecke Windows
und aktivieren Sie das Registers
Kontrollkästchen. Klicken Sie auf die Step
Schaltfläche und beobachten, wie 0xFFFF0000
in EAX - Register wird 0x00086000
mit Carry
Flagge. Wenn Sie auf den Registerwert klicken, wird zwischen der Hex- und Dezimaldarstellung einer Zahl gewechselt.
mov eax, 0xFFFF0000 add eax, 0x96000
Wie kann ich das beheben?
DirectDraw wird wahrscheinlich niemals einen Wert zurückgeben, der größer als 32-Bit-Ganzzahl ist (der mit 1 begrenzt werden kann, unabhängig von der tatsächlichen Speichergröße). Die einfachste Möglichkeit, dieses Problem zu beheben, besteht darin, die RawVidMemSize = RawVidMemSize + 614400
Operation aus dem Code zu entfernen. t löst den Überlauf aus, in der ausführbaren Datei sieht es so aus:
- Montage-Mnemonic:
add eax, 96000h
- Aktuelle Opcodes (Hex):
0500600900
Um es zu entfernen, müssen wir es durch NOP- Anweisungen (hex :) ersetzen 90
. Ich kenne den Datei-Offset bereits, aber in Ihrer ausführbaren Datei kann es anders sein. Glücklicherweise ist Hex-String 0500600900
in meinem race.bin
und wahrscheinlich in Ihrem einzigartig . Holen Sie sich den Hex-Editor (ich empfehle HxD : es ist kostenlos, portabel und einfach zu verwenden) und öffnen Sie Ihre bin
Datei.
Führen Sie eine Hex-String-Suche aus:
Sobald ein Hex-String gefunden wurde
Ersetzen Sie es mit 90
Speicher die Datei. HxD erstellt automatisch eine Sicherungskopie der Datei, die Sie wiederherstellen können, wenn etwas schief geht.
In meinem Fall war das genug und ich konnte das Spiel starten. So heat.log
sieht es nach dem Patch aus:
21.33.564: Entwurf: Direktentnahme mit aticfx32.dll (AMD Radeon HD 5800 Series) erstellt.
21.33.564: Entwurf: Version
0.0.0.0 21.34.296: VID : 64- Meg-Karte (gemeldet: 4095.937500 )
21.34.296: VID: using AGP-Texturen (3231), gesamt: 64
21.34.305: vid: Dreifachpuffer eingeschaltet
Wenn Ihre Datei zufällig mehrere Vorkommen enthält 0500600900
, ersetzen Sie die erste und versuchen Sie, das Spiel zu starten. Wenn dies nicht funktioniert, stellen Sie die Datei aus der Sicherung wieder her und versuchen Sie es als Nächstes. Ersetzen Sie nicht alles auf einmal, dies ist keine gute Idee.
Es wurde auch bestätigt, dass derselbe Fehler in Viper Racing existiert . Viper Racing verwendet eine etwas andere (ältere?) Version der Game Engine als Nascar, der Bug ist jedoch derselbe: Auch er versucht, 614400
Bytes zur Videospeichergröße hinzuzufügen . Die zu durchsuchenden Werte unterscheiden sich, da der Compiler in diesem Fall entschieden hat, keine Register zu verwenden und nur auf die Variable vom Stapel zuzugreifen, dh:
- Montage-Mnemonic:
add [esp+18h+var_14], 96000h
- Aktuelle Opcodes (Hex):
8144240400600900
Viel Spaß beim Fahren!
- Dies ist eine dieser Annahmen, über die ich gesprochen habe.