Now Reading
Salt Lake 2002 – not sufficient reminiscence/crashes to desktop after beginning error (Home windows 7) VOGONS

Salt Lake 2002 – not sufficient reminiscence/crashes to desktop after beginning error (Home windows 7) VOGONS

2023-05-22 12:46:44

Hello!
I personal an unique, US copy of this recreation and whereas wanting by means of my previous CDs a couple of weeks in the past, I wished to take it for a spin, having performed this recreation the final time many-many years in the past.
Once I tried putting in and working the sport now, I encountered precisely the identical points as you probably did.
As somebody who’s occupied with previous software program, disassembly and reverse engineering, I’ve determined to embark on a little bit of a journey and attempt to debug and discover out precisely what causes these errors on fashionable programs. Whereas I am there, I figured I would additionally make a submit about it right here for anybody who ever runs into these actual issues sooner or later and is about their root trigger.

This is the very brief rationalization of the 2 issues with their fast options, mainly TL;DR:

  1. Not sufficient reminiscence when putting in:
    Because of the installer calling a really previous Home windows operate, on programs the place you will have “an excessive amount of” RAM (greater than 4GiB), it thinks you will have a detrimental quantity of bodily reminiscence out there, which is lower than the minimal required 64MB, so the installer will abort.

    Resolution:
    You’ll be able to simply copy the sport information to your laborious drive, with out having to run the installer in any respect. The required directories are Films/, Music/, SOUNDS/, and the required information are FILES.IMG, COMM.OGG and SaltLake2002.exe. You probably have the US construct of the sport like I do, I like to recommend changing the SafeDisc-protected SaltLake2002.exe with beha_r’s or my mounted EXE so that you just will not have to put in the SafeDisc driver (which does not work on newer programs anyway and is an enormous safety gap) and you will not must mount the disc to run the sport.

    If you do not have the US construct or insist on desirous to undergo the installer process and permitting long-deprecated weak drivers onto your system, you possibly can simply repair the installer script file (setup.inx) itself to not abort the set up for you.
    For this, I’ve connected the mounted setup.inx under – simply copy all information from the disc to a neighborhood listing in your laborious drive, change setup.inx with the one I’ve supplied and run the installer from there. The installer will not care that it is not working from a disc.
    You can too get my modified setup.inx from right here:
    https://mega.nz/file/IghmiQbI#IvfeRlTsICPajkN … YT02M-6j2SfC81Q

  2. Crashing to desktop after recreation launch:
    When the sport is checking how a lot usable video reminiscence you will have, it corrupts this system stack attributable to a bug that’s solely triggered if in case you have “an excessive amount of” (greater than what the sport devs anticipated) VRAM. The corrupted reminiscence results in an unrecoverable crash.

    Resolution:
    Simply use a contemporary mounted EXE to run the sport.
    beha_r’s could be downloaded from any of those hyperlinks:
    (edit by Dominus: eliminated warez web site hyperlinks)

    Mine could be downloaded from the attachments or from right here:
    https://mega.nz/file/Y1ASAAbJ#jJPr19th3pICz7Y … oOkMk7k-GDUSHr0

That was the TL;DR. Listed below are the technical particulars for anybody :

Not sufficient reminiscence when putting in

When the sport installer queries your system for a way a lot bodily RAM it has, it makes use of an historical, lengthy deprecated Home windows API known as GlobalMemoryStatus that mainly cannot count above 4GiB due to storing the result in a 32-bit integer.

So if in case you have extra RAM than 4GiB, this API will return a detrimental quantity attributable to integer overflow, as even Microsoft mentions in their API docs. In flip, when the installer script receives this detrimental quantity from Home windows after which compares it to 64MB (as that’s the minimal reminiscence requirement for the sport), it’s going to discover that this detrimental quantity of reminiscence is lower than that and can abort set up with the “There may be not sufficient reminiscence out there to put in this software. Setup will now exit.” error message.
Fj0GwOI.png

The installer script was coded in a method that it sadly does not acknowledge when it has obtained an clearly bogus detrimental quantity for the out there RAM. The sport builders most likely did not count on that the sport could be run on programs utilizing greater than 4GiB of reminiscence. And even when they did – and will have used the extra future-proof API (GlobalMemoryStatusEx) which on the time was already out there – they most likely opted for this one as a substitute, as a result of the newer API just isn’t supported on Home windows 95 and 98.

The installer is instructed to do these checks through its compiled InstallShield script (aka. InstallScript) file, which could be discovered on the disc as a file known as setup.inx.

This is a snippet of how this installer script appears to be like like – it must be decompiled from the setup.inx file utilizing an InstallShield script decompiler device, I’ve used SID (sexy installshield decompiler by sn00pee):
44uqFig.png

You could find the code for the decompiled setup.inx file right here:
https://paste.gg/p/anonymous/ffedbaae71b5427a … b22c72f55be2b62

function_0 is the entry level, that is the place the prerequisite checks are finished for the installer. On line 926 (or offset @00004D99:0007), there’s this snippet:

@00004D99:0007   label_4d99:
@00004D9B:0021 GetSystemInfo(185, local_number1, local_string3);
@00004DAC:0009 local_number6 = (local_number1 < 64000);
@00004DBB:0004 if(local_number6) then // ref index: 1
@00004DC7:0021 function_234("ERROR_MEMORY");
@00004DDC:0006 local_string12 = LASTRESULT;
@00004DE6:0021 MessageBox(local_string12, -65534);
@00004DF4:0002 abort;
@00004DF8:000B endif;

GetSystemInfo being known as with 185 as its first argument executes the next code:

@0000A495:0005   label_a495:
@0000A497:000D local_number5 = (local_number1 = 185);
@0000A4A6:0004 if(local_number5) then // ref index: 1
@0000A4B2:0021 function_190(local_number2);
@0000A4BB:0006 local_number3 = LASTRESULT;
@0000A4C5:0005 goto label_aa2d;
@0000A4CE:0005 endif;

Which in flip executes function_190 and returns its consequence.
function_190 is the place the precise RAM querying occurs utilizing the outdated GlobalMemoryStatus API:

@0000B3B4:0009   label_b3b4:
@0000B3B6:0022 operate NUMBER function_190(local_number1)
@0000B3B6 NUMBER local_number2;
@0000B3B6
@0000B3B6 OBJECT local_object1;
@0000B3B6 start
@0000B3BF:001A local_number2 = &local_object1;
@0000B3C9:0020 GlobalMemoryStatus(local_number2); // dll: KERNEL32.dll
@0000B3D2:0035 local_object1.nTotalPhys;
@0000B3E6:0006 local_number2 = LASTRESULT;
@0000B3F0:0011 local_number1 = (local_number2 / 1024);
@0000B3FF:0027 // return coming
@0000B403:0023 return 0;
@0000B40C:0026 finish; // checksum: 4d013b

The GlobalMemoryStatus name populates a struct (local_object1) with details about the machine’s RAM. The nTotalPhys/dwTotalPhys struct member comprises the entire bodily reminiscence current within the machine in bytes. The code divides it by 1024 to get the entire RAM of the machine in kibibytes after which returns that worth.
Again to the code on line 926 (or offset @00004D99:0007), the entire put in RAM in KiB will get saved contained in the local_number1 variable right here:

@00004D99:0007   label_4d99:
@00004D9B:0021 GetSystemInfo(185, local_number1, local_string3);

After which will get in comparison with 64 000 KiB (rather less than 64 MiB):

@00004DAC:0009      local_number6 = (local_number1 < 64000);

Since this comparability is completed with signed numbers – and as already talked about, GlobalMemoryStatus returns detrimental numbers if there are greater than 4GiB of RAM – this if-statement will consider to true on fashionable machines with excessive RAM capability. This can in flip present the message field with the “not sufficient reminiscence out there” message and abort the set up:

@00004DBB:0004      if(local_number6) then // ref index: 1
@00004DC7:0021 function_234("ERROR_MEMORY");
@00004DDC:0006 local_string12 = LASTRESULT;
@00004DE6:0021 MessageBox(local_string12, -65534);
@00004DF4:0002 abort;
@00004DF8:000B endif;

Fixing this may be finished simply with SID. Altering the comparability into one thing that might be far much less prone to occur on an precise (fashionable) PC is what I’ve opted for. The way in which I’ve patched it’s to alter the less-than operator to an equals operator:

@00004DAC:0009      local_number6 = (local_number1 = 64000);

This manner the setup will solely abort if somebody has precisely 64000 KiB RAM out there, which could be very unlikely, as even in case you had been to have 64 megs of system reminiscence put in, function_190 would most likely return round 65536 KiB, thus it will cross the test.
You could find my patched setup.inx file right here within the attachments or on the hyperlink above within the TL;DR.

Crashing to desktop after recreation launch

Now this one is trickier.

The crash itself is of kind EXCEPTION_ACCESS_VIOLATION (error code 0xC0000005), which implies this system is attempting to learn or write reminiscence at an invalid reminiscence location. The crash logs additionally disclose what the deal with of the problematic instruction is: 0x00000000. This implies the appliance expects CPU directions to be at reminiscence deal with 0x00000000, however since that’s zero, it will not be capable of execute any directions from there and goes to only shut down itself.

One thing on older machines undoubtedly did forestall these crashes from occurring, as I clearly bear in mind enjoying this recreation on my previous XP desktop PC with none points.

Figuring this one out appeared tougher, so I’ve utilized the assistance of VirtualBox to create a clear virtualized surroundings for researching what precisely causes the crash. What additionally helped tremendously is that I’ve observed early on that beha_r’s mounted EXE doesn’t crash; the sport could be performed flawlessly when utilizing it.

Initially I assumed – like possibly your self – that one thing concerning the OS model itself is the set off for the crash. To verify this assumption, I created a digital machine with Home windows XP SP3, and tried working the sport in it. To my shock, it nonetheless crashed!
Simply to substantiate it is not the DRM inflicting it, I’ve additionally downloaded an previous crack for the sport (by D°N $iMoN) and it crashed even with that one each within the VM and in addition on my fashionable Home windows 10 machine.

I then began completely different parameters I may change. After some experimenting, I discovered that the crash could be managed with the quantity of VRAM that’s allotted for the digital machine. Once I allotted 128MiB, it was at all times crashing. With 64MiB, it just about by no means did.

The decision the sport is launched at additionally influences this. Decrease resolutions want much less VRAM to crash the sport, whereas greater ones want extra. This is slightly desk that demonstrates the minimal essential VRAM for the sport to crash, primarily based on the guide checks I’ve finished in my VM:
– 640×480: 68MiB
– 800×600: 70MiB
– 1024×768: 74MiB
– 1152×864: 76MiB

Any worth at or above these VRAM values on the given decision will crash the sport. So mainly if you wish to run the sport at 1024×768, simply set your VM’s VRAM capability to 73MiB, and you can run the sport simply nice.

And with that, two questions come up – how the hell does extra out there VRAM in a GPU trigger a crash like that? And the way did beha_r repair it?

Since beha_r’s repair has been out there for some time now, I began in search of solutions to the second query first.
I contacted beha_r and he was form sufficient to share a earlier than and after EXE with me in order that I may see precisely what he modified within the unique executable.

Moreover clearly patching out the operate name liable for the CD test, he additionally modified one other operate’s stack. By reverting his CD test patch to solely embody the stack modification within the executable, I used to be in a position to verify that this stack trick was certainly the one that truly mounted the crash.
The change he is finished is simply merely to extend the stack measurement by 4 bytes for the operate at deal with 0x00531C30:

sub_531C30+0
prev.: mov eax, 108Ch
fixd.: mov eax, 1090h (+4)

sub_531C30+203 (531E33)
prev.: add esp, 108Ch
fixd.: add esp, 1090h (+4)

sub_531C30+214 (531E44)
prev.: add esp, 108Ch
fixd.: add esp, 1090h (+4)

So it appears extra space is given to this operate on the stack.
Since this operate is clearly the principle focal point within the quest to determine why the crash happens, I’ve determined to undergo the painstaking technique of decompiling it. Fortuitously it is not that vast and it did not take that lengthy, but it surely nonetheless did take a while to correctly identify, annotate and clarify every little thing.

It did repay ultimately, as I used to be in a position to make sense of every little thing in its logic, permitting me to current it to you right here in its full glory:
https://paste.gg/p/anonymous/45427fd1d16a4b5d … 73313e29c24f899

You can even compile this your self utilizing the DirectX 8 SDK in case you change the this references with constants and supply legitimate IDirect3DDevice8 and HWND objects.

As you possibly can see, this operate is mainly a VRAM capability check. It returns the usable quantity of video reminiscence in MiB.
This leads us again to the primary query – how does a considerable amount of VRAM crash the sport right here? Properly, there is a bug on this operate. See in case you can spot it your self.

Prepared?

Okay, right here goes:

The sport builders made two errors right here, and the mixture of those two errors causes the crash.

The primary mistake: apparently they by no means anticipated (or did not care) that GPUs may transcend 512 * 128K == 64MiB VRAM capability. They relied on DirectX to ultimately throw a D3DERR_OUTOFVIDEOMEMORY consequence when creating as much as 512 surfaces and textures to correctly calculate how a lot VRAM is offered for them to make use of,
mainly at all times anticipating a failure, an eventual exhaustion of video reminiscence.

They thought 64MiB could be a lot for a VRAM overload check as most client graphics playing cards on the time of growth (2001) had round 16-32MiB VRAM. Even the sport’s system necessities point out a 32MiB GPU for optimum efficiency. Curiously, higher-end playing cards that had 128MiB VRAM had already come out in 2002, so this bug may have popped up for some individuals already solely mere months after launch.

Funnily sufficient, the usable VRAM quantity this operate calculates does not even seem to affect something; it does get saved, however does not have an effect on gameplay or playability/recreation stability in any respect, even when it is 0. In truth, beha_r’s repair makes the sport imagine it has 0 MiB usable VRAM, but it nonetheless works nice.

See Also

Nonetheless the opposite mistake is a way more extreme one: they tousled the bounds checks for BOTH arrays. The dummy texture and floor arrays had been each outlined as able to holding 512 pointers every, nonetheless their respective loops overrun each buffers with one further iteration when an excessive amount of VRAM is offered.
It’s because the devs had been supposed to make use of < as a substitute of <= operators within the do..whereas loops:

do {
...
} whereas ( numOfCreatedSurfaces <= 512 && !createResult );

Arrays in C/C++ are zero-indexed, which means that dummySurfaces‘ first merchandise is dummySurfaces[0], and its final merchandise is dummySurfaces[511], not dummySurfaces[512]; that one factors past the tip of the array. With every profitable texture/floor creation, this index is incremented, nonetheless it will get incremented one extra time – so 513 instances as a substitute of the meant 512 – because of the less-or-equal (<=) bounds test.

For the dummyTextures array, this is not too large of a deal, the merchandise dummyTextures[512] – which truly factors to the 513th factor of the array, thus is exterior of it – solely factors to the subsequent array’s first merchandise in reminiscence, dummySurfaces[0]. That’s overwritten by its personal loop anyway in a while.

The issue is with dummySurfaces[512], as dummySurfaces is the final array within the operate’s stack. When this final array is overrun, past its final merchandise, the operate’s personal return deal with on the stack is overwritten with a pointer to a clean floor.

Resulting from how the stack is structured, the return deal with goes after all of the variables and buffers outlined on the stack. It simply so occurs that the final buffer outlined on the stack is overrun, corrupting the return deal with that comes good after it.

Whereas this final +1 floor is NULL’ed out, the unique return deal with (that might inform the operate the place to return after it completed executing) is misplaced eternally and has been changed with zeroes. That is why when the operate finishes, it thinks it was known as from 0x00000000, so it tries to go there to proceed execution, however as that’s invalid reminiscence, the sport crashes with an EXCEPTION_ACCESS_VIOLATION.

(For those who do not fairly perceive what all this return deal with stuff is about, watch this video, it explains it a lot better than I do right here.)

The builders most likely solely examined the sport with GPUs having as much as 64MiB video RAM. In that case, it doesn’t matter what decision they run the sport at, it’s going to at all times run out of video reminiscence when creating the 512 textures and surfaces, thus not reaching the problematic 513th iteration.

Now, you is perhaps questioning: how does this relate to the sport decision and why the precise quantity of VRAM “wanted” to crash the sport will increase with greater resolutions? Properly, I used to be kinda stumped on this for an extended time than I would prefer to admit, however the reply is painfully easy: the upper the decision, the extra VRAM Home windows itself and some other GPU-utilizing functions use. The textures and surfaces at all times take up the identical quantity of VRAM; it is every little thing else (that the video reminiscence is shared with) that wants extra sources because the decision is elevated.

Going again to the code, we will see that the way in which this was mounted by beha_r is fairly easy and easy: he simply created 4 bytes extra space on the stack for that further floor iteration to jot down into, making the final array be capable of retailer 513 pointers as a substitute of 512, sparing the operate’s return worth from getting overwritten:

  DDSURFACEDESC2 surfaceDescriptor; // [esp+5Ch] [ebp-1080h] BYREF
IDirect3DTexture8 *dummyTextures[512]; // [esp+D8h] [ebp-1004h] BYREF
IDirectDrawSurface7 *dummySurfaces[513]; // [esp+8D8h] [ebp-804h] BYREF

The dummyTextures buffer continues to be overflown, however because it overflows into the dummySurfaces buffer, it is not a problem. And dummySurfaces doesn’t get overrun anymore.
This may be finished with none issues, as there are a couple of bytes of padding between features on the stack, and taking these 4 bytes off of the padding does not trigger any points to the subsequent operate in reminiscence.

For my part (and what most likely the devs meant as properly), one of the best ways to repair this might be to repair the do..whereas loops to not iterate over the buffer’s bounds:

do {
...
} whereas ( numOfCreatedSurfaces < 512 && !createResult );

Or higher but, because the return worth of this operate is not even used that a lot and the sport runs simply nice even with a return worth of 0, your entire operate name may simply be patched out. It is solely known as from one place. On fashionable programs, there are absolutely greater than 64MiB graphics reminiscence out there anyway.

Earlier than:

.textual content:0052D863                 mov     ecx, esi
.textual content:0052D865 mov [esi+604h], edi
.textual content:0052D86B mov [esi+608h], edi
.textual content:0052D871 name sub_531C30 ; problematic VRAM-testing operate name
.textual content:0052D876 cmp eax, edi
.textual content:0052D878 mov [esi+594h], eax

After:

.textual content:0052D863                 mov     ecx, esi
.textual content:0052D865 mov [esi+604h], edi
.textual content:0052D86B mov [esi+608h], edi
.textual content:0052D871 mov eax, 40h ; operate name utterly patched out, program is informed it has 64 MiB VRAM to make use of
.textual content:0052D876 cmp eax, edi
.textual content:0052D878 mov [esi+594h], eax

I patched the sport executable on this style and did not run into any points. You could find it connected under or on the hyperlink above.

One may argue that the devs made a 3rd mistake: they may’ve simply merely used DirectX’s builtin VRAM-querying features to get the out there VRAM quantity, corresponding to GetAvailableTextureMem or GetAvailableVidMem as a substitute of attempting to cram a bunch of clean textures and surfaces into the VRAM on every recreation startup. My guess could be that they went down this route as they deemed this extra dependable than these features? Not likely certain.

Perhaps a few of the unique builders like Derek Pettigrew or Simon Morris might be requested if these guys are nonetheless round and somebody had been in a position to come up with them.

Both method, this was my evaluation of the difficulty. The mounted EXE I’ve connected and linked to patches out the VRAM-check operate name proven above solely. Perhaps sparing that further few milliseconds is efficacious to somebody, however in any other case, the beha_r patch ought to work simply completely nice for everybody else. =)

Unsure if anybody will ever even learn this as Salt Lake 2002 is sort of an obscure recreation and it is not even that good.
But when a minimum of one individual did learn this and located it useful/loved it, it was definitely worth the effort.
Large shoutout to beha_r for his unique mounted EXE and for his assist, my evaluation would’ve taken for much longer with out him!

Source Link

What's Your Reaction?
Excited
0
Happy
0
In Love
0
Not Sure
0
Silly
0
View Comments (0)

Leave a Reply

Your email address will not be published.

2022 Blinking Robots.
WordPress by Doejo

Scroll To Top