First post, by Oerg866

User metadata
Rank Newbie

Hi guys,

I've been trying to get Shadows of the Empire to run on some of my older machines here, the subject in question is a PIII 933 on a BX board, 256MB and a SB Vibra 16C. Running Windows 98 SE stock. I'm trying a lot of different cards and the game just won't work properly.

The closest match to "proper" I was able to achieve was a Riva TNT2 PCI with drivers 6.13. Game runs okay, but oscillates between 60fps and 30fps (what it's supposed to run at) quite a lot, especially in higher resolutions.

The same drivers with a GeForce 2 GTS results in a black screen with hourglass cursor (can still move the cursor, but numlock is unresponsive.)

Running the Riva TNT2 with newer drivers makes it run increasingly worse. At first the menu and cutscenes start flickering, then at one point (I think it was post-12.xx drivers) it refuses to load textures completely.

Matrox Mystique has a lot of missing features. Game runs though.

ATI Rage Pro AGP 2X works, but lacks features and texture transparency and is quite slow at times.

Matrox cards like G450 and G550 have the same black screen with hourglass issue.

I've installed softice to figure out what's happening, it gets stuck in a loop of which the exit condition is that a register that's being shifted to the right stops being 0. That's not even physically possible which is a bit of a thinker. I've modified memory to nop out the JZ and it crashed straight to desktop. I've yet to find motivation to load up IDA to see where that code comes from but that just smells like BS to me. But this issue has been consistent across a lot of machines here.

Compare that to my i9 with a 980Ti running Windows 10, plop in the CD, install it (with the installshield replacement), runs right out of the box. No issues whatsoever, fixed at 30 fps regardless of monitor refresh rate or anything. Doesn't even complain about my two screens. I didn't even update it to version 1.1. It just *works*. I am flabbergasted.

Any help is appreciated.


Reply 3 of 9, by Oerg866

User metadata
Rank Newbie


Well I am getting closer. 3rd time this point is reached the memory area of 16 bytes is completely empty on the crashing machine, but filled with... things on my main PC. 4th time as well. If I manually copy the contents of those 16 bytes by hand before that specific section is executed, well...


I still don't know what this code does and why the memory is empty when the GeForce 2 is in, but I guess I can maybe look where the 3rd and 4th calls are coming from and hack in a quick fix....

Reply 4 of 9, by derSammler

User metadata
Rank l33t

Remindes me of Rogue Squadron, which has a similar bug. It reads from an illegal memory location after completing a missing. That works fine will all supported 3d cards, but not with others. So either same bug due to same engine, or, *cough*, intentionally done to discredit "non-supported" 3d cards..?

Reply 5 of 9, by leileilol

User metadata
Rank l33t++

I'd say it's more of an oversight and a combination of early generation 3d card drivers varying in quality too much. There's many old games that visually break on better drivers.

to give another example of this,, Quake3 had skybox edge clamp issues on ATI Radeon cards because Nvidia and 3dfx's GL ICDs had different clamping behavior for GL_CLAMP and didn't properly GL_CLAMP_TO_EDGE (that would wrap instead). much naiveness from assuming nvidia's driver behavior being "proper" had occured, painting ATI Radeon as the "buggy one" while they're actually doing it right. Source ports that "fix" this by changing to the latter end up breaking skyboxes on these old drivers (at least the 3dfx ones anyway).


Reply 6 of 9, by Oerg866

User metadata
Rank Newbie

I've managed to construct a patch. See attachment or the link below.


This fixes the black screen + cursor freeze for Shadows of the Empire on a lot of old PCs.

Technical details:

The game get stuck at the following code (0xA50AB in the file):

.text:004A5C95 loc_4A5C95:                             ; CODE XREF: sub_4A5AE0-145CCj
.text:004A5C95 ; .text:00491536j
.text:004A5C95 xor ecx, ecx
.text:004A5C97 mov esi, [ebp+58h]
.text:004A5C9A test esi, 1
.text:004A5CA0 jnz short loc_4A5CAD
.text:004A5CA2 loc_4A5CA2: ; CODE XREF: sub_4A5AE0+1CBj
.text:004A5CA2 shr esi, 1
.text:004A5CA4 inc ecx
.text:004A5CA5 test esi, 1
.text:004A5CAB jz short loc_4A5CA2

On a working machine, the memory area of ebp+58h always has 16 bytes (4 DWORDs) of feasible data present. On an old PC running graphics hardware newer than the game was made for (I guess about 1999+) starting with the 4th call there are 4 empty DWORDs (00 00 00 00) instead. If you understand assembly code you can imagine why that doesn't bode well for the above code.

I then checked the contents on those calls on my working machine (i9-7900X, GTX 980 Ti, Windows 10 out of all things) and manually copied them over by using SoftICE and a BPX on 4A5C95 and doing that twice made the game start and run perfectly.

I have no clue what the whole subroutine does, I imagine it evaluates some hardware capabilities or something, but not through standardized methods (I've never done graphics programming in my life so pardon if this sounds uneducated) or APIs but through very direct and/or deprecated methods. It would explain why the Riva TNT2 gives proper results while the GeForce 2 GTS does not - using the same driver version. It would also offer a feasible explanation as to why the game works perfectly on modern systems running Windows 8 / 10 as is the case on my machine, because there's a big emulation layer on top to handle those old calls that wants to stay as compatible as possible.

I then had to construct a patch to fix this issue. I figured an easy workaround would be to check if the first DWORD is 0 and in that case just copy the previously manually obtained data there, otherwise continue as-is. I had to find a location in the executable that is big enough for that purpose.

The area of 0x90910 to 0x90940 (0x491510 in .text) was full of padding with occasional 'ret' instructions (empty functions). I traced those back to where they're called and NOPed those calls so I would end up with a large area to put a bit of patch code in.

I then checked in the program flow how it'd end up at the offending loop and traced that back to this:

.text:004A5B3C loc_4A5B3C:                             ; CODE XREF: sub_4A5AE0+4Ej
.text:004A5B3C test al, 1
.text:004A5B3E mov dword ptr [ebx], 1
.text:004A5B44 mov eax, [ebp+54h]
.text:004A5B47 mov [ebx+4], eax
.text:004A5B4A mov dword ptr [ebx], 1
.text:004A5B50 mov eax, [ebp+54h]
.text:004A5B53 mov [ebx+4], eax
.text:004A5B56 jz loc_4A5C95
.text:004A5B5C xor ecx, ecx
.text:004A5B5E mov esi, [ebp+58h]
.text:004A5B61 test esi, 1
.text:004A5B67 jnz short loc_4A5B74

I made a new 'patch' label and made the jz point to that instead.

.text:004A5B56                 jz      patch

My patch code looks like this:

.text:00491510 patch:                                  ; CODE XREF: sub_4A5AE0+76j
.text:00491510 cmp dword ptr [ebp+58h], 0
.text:00491514 jnz loc_4A5C95
.text:0049151A mov dword ptr [ebp+58h], 0FF00h
.text:00491521 mov dword ptr [ebp+5Ch], 0FF0000h
.text:00491528 mov dword ptr [ebp+60h], 0FF000000h
.text:0049152F mov dword ptr [ebp+64h], 0
.text:00491536 jmp loc_4A5C95

All other code is unaltered. It seems the issue is fixed for my purposes at least.

Hope you enjoy it!


  • Filename
    File size
    3.49 MiB
    File license
    Fair use/fair dealing exception

Reply 7 of 9, by markiemarcus

User metadata
Rank Newbie

I've had similar problems in other games (double frame rate in Die Hard Trilogy and Wipeout XL) and the easiest solution I could come up with was creating a custom resolution using Powerstrip 3.20 for 800x600 at 30Hz, then lock with Vsync. Any game that runs too fast I use this resolution and everything else at 1024x768 60Hz. Most of these games don't seem to have an option to select refresh rate and it was the best compromise I could find that worked. In Windows XP using RTSS works well also.

Input latency seems normal for 30fps.

On a CRT the flicker would be crazy, but on an LCD it isn't an issue.

Reply 8 of 9, by Ace

User metadata
Rank Oldbie

I tried using 640x480 at 30Hz myself on Powerstrip for Shadows of the Empire, but there was so much delay in the video output, I couldn't play the game. It's not input lag; in my case, the game's audio and video were way out of sync with the audio far ahead of the video. Audio was synced properly to my controller inputs with the video following. At this point, I think the game would need a proper patch to cap the framerate at 30FPS. On Windows XP and up, it's possible to use RivaTuner to cap the framerate to 30FPS, which works exactly as intended, but on Windows 9x, it's pretty messy.

Creator of The Many Sounds of:, a collection of various DOS games played using different sound cards.

Reply 9 of 9, by markiemarcus

User metadata
Rank Newbie

Will definitely have a look at SOTE and see if I can reproduce the same problem; I had only tested this with the games mentioned. Running an ATI AIW 9000 with Vsync forced in the ATI control panel.