narzoul / DDrawCompat

DirectDraw and Direct3D 1-7 compatibility, performance and visual enhancements for Windows Vista, 7, 8, 10 and 11
BSD Zero Clause License
952 stars 70 forks source link

Falcon 4.0 and Total Air War crash exiting 3D #339

Open ajalberd opened 3 months ago

ajalberd commented 3 months ago

I first want to say I absolutely love this program.

Falcon 4.0 vanilla works beautifully using DDrawCompat in 2d menu and 3d space, but exiting from 3d back to 2d menu results in the following error ingame: image "Failed to create front buffer"

Total Air War also crashes exiting 3d space. Attached are both logs, to recreate either is simple. Falcon 4.0 start a campaign and exit the mission. Total Air War enter and exit any mission. DDrawCompat-Falcon4_108i2.log DDrawCompat-F22.DAT.log DDrawCompat-F22.DAT.log

Here's two F-22 logs one crashes in 3d one in 2d menu.

narzoul commented 2 months ago

Falcon 4.0 vanilla works beautifully using DDrawCompat in 2d menu and 3d space, but exiting from 3d back to 2d menu results in the following error ingame

Based on your logs, it's not vanilla Falcon 4.0. I can't reproduce the issue with the vanilla version. You seem to be using the unofficial 1.08i2 patch, which has the same issue for me, even with native ddraw.

The problem is caused by a difference in how DirectDraw handled the exclusive mode synchronization objects in Windows 95/98 compared to newer versions of Windows. You need to use Microsoft Application Compatibility Toolkit (ACT) and apply the EmulateDirectDrawSync fix to the executable file (Falcon4_108i2.exe) to fix it.

In theory, simply using Windows 98 compatibility mode should also include this fix, but it includes a lot of others too, which causes the game to crash on my end. The ACT method is preferred anyway.

Total Air War also crashes exiting 3d space.

Again, you don't seem to be using the normal version of the game, but some total conversion mod(?) called "Total Air War 2.0" I assume. I spent quite some time tracking a working installer down, but couldn't find the supposed patches for it anywhere, so I didn't install any of those.

I don't get the same error log as you (maybe you need EmulateDirectDrawSync for this game too), but I get random crashes caused by insufficient stack space (only 32KB!) allocated for threads by the game. I'd recommend hex editing the F22.DAT file (which is the actual game executable file here) to remove this limitation and use the system default 1MB stack size instead.

You can use the following replacement: 81 E5 00 F0 FF FF 55 -> 6A 00 90 90 90 90 90 It should only appear once in the file, and should work for the original game also.

In the original game version, I had some other random crashes too, but they don't seem to be related to DDrawCompat.

ajalberd commented 2 months ago

Wow. Thank you for this insanely detailed reply! I'm sorry I didn't provide all the necessary info. Are you a big flight sim fan yourself?

Yes, sorry to mention this was the 1.08i2 patch.

With regards to TAW, I'm using TAW 2.30 from here: http://krishty.com/taw/TAW_230_Install.exe

Edit: I didn't see the EmulateDirectDrawSync option but now I do. Will report back.

narzoul commented 2 months ago

I did some more research into how stack space is allocated, and my previous claims were not entirely accurate.

Stack size is controlled by two parameters at thread creation: commit size and reserve size. Commit size is the initial size of the stack, and reserve size is the maximum size that it can dynamically grow to. The commit size increases as the stack grows, which also increases the physical memory usage. The reserve size takes up only virtual memory, allocated in full when the stack is created.

The default commit and reserve sizes are specified in the IMAGE_OPTIONAL_HEADER32 PE header of the executable file. The usual values are 4KB for commit and 1MB for reserve, but for F22.DAT, different values are used. These can be inspected by the dumpbin tool (part of the Visual Studio installation and can be used from its Developer Command Prompt):

dumpbin /headers "C:\DiD\F-22 Total Air War 2.0\f22.dat"

Relevant part of the output:

OPTIONAL HEADER VALUES
(...)
               0 checksum
               2 subsystem (Windows GUI)
               0 DLL characteristics
           10000 size of stack reserve
           10000 size of stack commit
(...)

The values are hexadecimal, so the actual size is 64KB for both reserve and commit.

F22.DAT uses the CreateThread function to create a thread, specifying the dwStackSize parameter as 32768. This sets the commit size to 32KB and the reserve size to the default 64KB, which seems to be insufficient and can cause stack overflows.

The patch I posted previously replaces this dwStackSize parameter with 0, which sets the commit size to the default value from the PE header, i.e. 64KB, but in itself should not fix the reserve size. However, the docs also claim that if the commit size is greater than or equal to the reserve size, then the reserve size is rounded up to the nearest integer multiple of 1MB. So in this case, because the default commit size matches the default reserve size, the actual reserve size is bumped from 64KB to 1MB. This is what ultimately fixes the issue.

Another way to fix it is to edit the PE headers in the executable file with the editbin tool (also part of Visual Studio, usable from its Developer Command Prompt):

editbin /stack:1048576 "C:\DiD\F-22 Total Air War 2.0\f22.dat"

This increases the default reserve size to 1MB, so now even if dwStackSize is not patched, only the commit size will be 32KB, but the reserve size will be 1MB.

The new header values can be confirmed by using dumpbin /headers again:

OPTIONAL HEADER VALUES
(...)
          36ED6E checksum
               2 subsystem (Windows GUI)
               0 DLL characteristics
          100000 size of stack reserve
           10000 size of stack commit
(...)

Note that editbin also updates the checksum value, but this is not really necessary. The stack reserve size can also be manually changed by hex editing F22.DAT at offset 0xD0 and replacing the DWORD value there (remember that it uses little-endian encoding): 00 00 01 00 -> 00 00 10 00

It's probably safer to use editbin, as the offset may be different for other executables, but the same method might be useful to fix other games with stack overflow errors.

Note: the stuttering in the menus can be fixed by putting ThreadPriorityBoost=on in DDrawCompat.ini.


Sources: CreateThread function Thread Stack Size IMAGE_OPTIONAL_HEADER32 structure DUMPBIN Reference EDITBIN Reference

ajalberd commented 2 months ago

Wow. Thank you so much for these insights! There still exists some people out there that tinker with the F22.DAT, although I am not one of them, they would love to see this comment.

EmulateDirectDrawSync and ThreadPriorityBoost=on fixed everything else, too. Thank you for the help! This also fixed Falcon 4.0 with the iBeta patch.

In the future, is EmulateDirectDrawSync something that can be brought to this program, or can only Windows control this behavior?

ajalberd commented 2 months ago

Additionally, for both Falcon 4.0 and Total Air War, the HUD seems to "shrink" with the scale of resolution. Is there any setting I'm missing to set to avoid this problem?

Screenshot (8) Screenshot (7)

narzoul commented 2 months ago

Are you a big flight sim fan yourself?

Not at all, I haven't played any flight sims since I was a kid (F-19 Stealth Fighter).

Additionally, for both Falcon 4.0 and Total Air War, the HUD seems to "shrink" with the scale of resolution. Is there any setting I'm missing to set to avoid this problem?

Do you mean the lines on the HUD? I'm guessing those are drawn as line segment primitives with Direct3D, and unfortunately their width cannot be specified, so they're always 1 pixel wide, which looks thinner at higher resolutions.

I might be able to come up with some workaround. Easiest would be to simply replicate the lines NxN times where N is the scale factor, but it would remain aliased then. More complex solution would be to reimplement the feature using triangles, but it's a lot of effort, so probably won't be done anytime soon, if ever. I'm also not sure how these lines behave if the Z coordinates are not all the same, so some research would be needed...

ajalberd commented 2 months ago

Wow. Well the small community thanks you a ton for your work on this amazing wrapper. It and dgvoodoo2 bring these old games back to life, for sure. Mig Alley would be impossible without it!

Yep, the lines on the HUD.

I'm guessing those are drawn as line segment primitives with Direct3D, and unfortunately their width cannot be specified, so they're always 1 pixel wide, which looks thinner at higher resolutions.

That makes sense. I guess it depends on how each game draws the HUD. Thanks for replying. You can close this one.

ajalberd commented 1 month ago

Hey @narzoul , I just want to add an additional comment. On another Win11 AMD+AMD machine, I did not need the ACT method for Falcon 4.0 whatsoever. It worked seamlessly.

However, the campaign / Tactical Engagement menu suffers some nasty lag, even with the ThreadPriorityBoost=on. You can recreate this by going to campaign or TE menu, and clicking on any button - it will take several seconds to register. Just curious your thoughts about this. And thank you again for this excellent program.

narzoul commented 1 month ago

I can't reproduce this on my Radeon RX480. Does CpuAffinity=all help? If not, attach debug logs without the CpuAffinity setting.

ajalberd commented 1 month ago

I can't reproduce this on my Radeon RX480. Does CpuAffinity=all help? If not, attach debug logs without the CpuAffinity setting.

That was it! Thanks a lot.