VOGONS


DllMain Thread

Topic actions

First post, by UCyborg

User metadata
Rank Member
Rank
Member

Let's dedicate this thread to the games and applications that fail to initialize graphics because of that awful practice of doing it in DllMain. If you know such an application/game, you can post about it here and I can try to patch it, but no guarantees. There probably aren't many of them out there. So far, the following has been patched:

Games:

Benchmarks:

TODO list:

  • <Make a suggestion!>

Right now, I'm fiddling with Soulbringer (GOG version) and having little trouble, I set the breakpoint on DLL entry point and it isn't hit, I set it on DDraw.dll exports any they aren't hit...it does throw out a message box complaining that it can't enumerate display devices and pauses there. I also see a bunch of these scrolling through the stack, so hopefully it gets me somewhere:

DXGI ERROR: CreateDXGIFactory cannot be called from DllMain. [ MISCELLANEOUS ERROR #76: ]

Speaking of GOG, would be nice if they patched all games they sell thoroughly. From Soulbringer page: ATI/AMD notice: ATI/AMD cards are not supported for this title.

Last edited by UCyborg on 2017-01-23, 22:48. Edited 3 times in total.
Arthur Schopenhauer wrote:

A man can be himself only so long as he is alone; and if he does not love solitude, he will not love freedom; for it is only when he is alone that he is really free.

Reply 2 of 39, by Dege

User metadata
Rank l33t
Rank
l33t
UCyborg wrote:

DXGI ERROR: CreateDXGIFactory cannot be called from DllMain. [ MISCELLANEOUS ERROR #76: ]

If nothing else, you can install Windows SDK and enable the DX11 Debug layer along with debug-breaks on error messages.
So, when you launch the game from the debugger then it breaks when DXGI dumps the error message. From then, you can unwind the callstack to see where it came from.
Back then when Soul Bringer was reported, I tried to quickly patch it but as far as I remember, it wasn't easy because the renderer dll of the game has an internal c-style interface with various functions, and when I tried to call the same DDraw init from the renderer dll Init () then it turned out
that some information (data?) was needed from the result of DllMain. In other words, there was a dependency between DllMain and Init which was not easy to resolve (I emphasize, if I remember right, it was long ago 😀 ), so I gave up and continued on dgVoodoo.

UCyborg wrote:

Speaking of GOG, would be nice if they patched all games they sell thoroughly. From Soulbringer page: ATI/AMD notice: ATI/AMD cards are not supported for this title.

😀
It's nice...

spiroyster wrote:

Why is this an awful practice?
What are you debugging and how are you doing it?

When DllMain is being called by the OS loader (or from LoadLibrary) then the process is in kind of a "critical section" (an internal lock count is incremented) which can easily cause deadlock if any of the WinAPI funtions is called. It's not an obscure thing, documented even in MSDN.
In fact, DllEntryPoint is useful only (designed) for running the dll's global constructors (which also shouldn't do anything, only initializing needed things with constants, like zeroing memory or sg like that) and storing the module handle for later usage.

Reply 3 of 39, by UCyborg

User metadata
Rank Member
Rank
Member

GOG version patches, which take care of aspect ratio and cinematic issues, don't work when we put dgVoodoo in game folder, but dgVoodoo can take care of aspect ratio (select 4:3 resolution in game, set Scaling mode in dgVoodoo to Stretched, keep Aspect Ratio). They also don't work with DX7 modes (stretched image). EAX enabled executable also doesn't work, just crashes, seems like they haven't patched it like the non-EAX one. I do have working Creative ALchemy installation, including EAX Unified, which other games are able to use just fine for querying EAX capabilities. Parts of the game also stutter a little.

So I went on and patched the retail version files, put dgVoodoo in the folder and things look mostly fine at first glance. 256-color software mode works as well as DX6, which allows 32-bit color mode (though game textures are only 16-bit I think). DX7 modes result in black screen, music plays, the game runs, just nothing is shown. Seems to run smoothly. EAX .exe works as good as the other one.

The game really doesn't like AMD cards out-of-the-box, there is another issue in addition to DllMain. Impossible to even reach main menu as long as it's running via native Direct3D, no matter what. Get dgVoodoo in its place and it works!

One oddity that remains when ran through dgVoodoo is the intro cinematic, it runs in a small borderless window on the top-left and the taskbar flickers like crazy when said window appears when scaling mode is set to Unspecified. If dgVoodoo is allowed to control fullscreen/windowed state, borders appears around it and there is no taskbar flickering. If I set Stretched, keep Aspect ratio, it appears fullscreen, but there is just blackness (just like with native DirectX). GOG version has a patch for that that only works with native DirectX. Similar problem exists with a brief screen with game logo that displays after the intro cinematic, it appears in a small borderless window in the top-left corner.

Launcher can be buggy, probably the best to use it to set resolution then launch the game via one of the other executables. Can run via native DirectX 6 or 7 with some fiddling on NVIDIA graphics card, best not to try though.

TL;DR This was all over the place. It's a buggy game out-of-the box, less buggy when you get it running via dgVoodoo. You can download patched binaries and try it out with dgVoodoo if you'd like.

Soulbringer.png
Filename
Soulbringer.png
File size
597.11 KiB
Views
6110 views
File license
Fair use/fair dealing exception
Arthur Schopenhauer wrote:

A man can be himself only so long as he is alone; and if he does not love solitude, he will not love freedom; for it is only when he is alone that he is really free.

Reply 4 of 39, by UCyborg

User metadata
Rank Member
Rank
Member

About that issue I had, it was just OllyDbg being quirky, sometimes it doesn't pick up on a loaded library unless you pause the execution.

As for how I do it, in a nutshell, I use OllyDbg to put 2 codecaves in 2 places. OllyDbg is assembly-level analysing debugger, so all the code is written in x86 ASM.

One place is inside the function that acts as a DLL entry point (DllMain), the branch that gets executed when DLL in question is being loaded. From there, I jump to a small piece of code I put in the empty space of code section:

CPU Disasm
Address Hex dump Command Comments
10061BC0 |> 833D F88F0810 CMP DWORD PTR DS:[10088FF8],1 ; Check the variable that says if we've been called before
10061BC7 |. C705 F88F0810 MOV DWORD PTR DS:[10088FF8],1 ; We're being called, set the variable to 1
10061BD1 |.- 0F85 D33EFFFF JNE 10055AAA ; If variable hasn't been set to 1 yet when we've checked it, bail out
10061BD7 |. A1 F8840810 MOV EAX,DWORD PTR DS:[100884F8] ; If it has, execute original instruction from where we've jumped here
10061BDC \.- E9 613EFFFF JMP 10055A42 ; And continue as usual

This just prevents the code in DllMain from being executed when OS calls it, so we have to do it ourselves. In this example, variable at 10088FF8 is located at the end of DLL .data section, where global variables go. Generally, there is varying amount of unused space left with different binaries. If more space is needed, it's possible to create an extra section.

The next snippet resides in whatever executable module uses LoadLibrary function to load the problematic DLL, we make the code cave (a jump) somewhere after the LoadLibrary call. Then, we start with pushing the function arguments for DllMain in reverse order onto the stack, note that at this point, register EAX holds the address (handle) of the loaded DLL:

CPU Disasm
Address Hex dump Command Comments
02128B54 /> 6A 00 PUSH 0 ; lpvReserved = NULL
02128B56 |. 6A 01 PUSH 1 ; fdwReason = DLL_PROCESS_ATTACH
02128B58 |. 50 PUSH EAX ; hinstDLL
02128B59 |. 8B70 3C MOV ESI,DWORD PTR DS:[EAX+3C] ; Get the relative address of IMAGE_OPTIONAL_HEADER
02128B5C |. 8B4C06 28 MOV ECX,DWORD PTR DS:[EAX+ESI+28] ; Get the relative address of DLL entry point from IMAGE_OPTIONAL_HEADER
02128B60 |. 8D3401 LEA ESI,[EAX+ECX] ; Calculate the actual address of DLL entry point (DllMain)
02128B63 |. FFD6 CALL ESI ; Call it
02128B65 |. 8B53 28 MOV EDX,DWORD PTR DS:[EBX+28] ; Execute whatever we've overwritten by writing a jump to here
02128B68 |. 8D73 28 LEA ESI,[EBX+28]
02128B6B \.- E9 6E55FEFF JMP 0210E0DE ; Continue as usual

And that's the whole magic behind my patches. The similar can be done if DLL makes dangerous calls in DllMain when it's being unloaded by calling it right before FreeLibrary.

I also use Relocation Section Editor. Whenever you add an instruction in a DLL that contains the absolute address, the address of the said address must be referenced in the relocation table of the DLL so the OS can adjust those addresses accordingly when the DLL is loaded since DLLs are not loaded at constant/fixed address in memory. Obviously, you must make appropriate changes also when you've changed the instruction that contained the absolute address before if needed, depending on the change.

Generally, when programming your own DLL, you're going to make some of its functions publicly available. While you're at it, might as well write the special initialization function if needed and require consumers of the DLL to call it before they make calls to other functions. Much better than relying on DllMain, which just causes headaches in practice since it wasn't designed for such purposes.

Arthur Schopenhauer wrote:

A man can be himself only so long as he is alone; and if he does not love solitude, he will not love freedom; for it is only when he is alone that he is really free.

Reply 5 of 39, by spiroyster

User metadata
Rank Oldbie
Rank
Oldbie

@UCyborg & @Dege

Many thanks for the clarification on these. Mucho respecto to your low-level skills there 😀.

Didn't realise DllMain was such an issue. Personally I diverged away from using Dll's in this manner as I didn't like this tighter coupling between application and dll, opting instead for the dll to fully understand how it should manage its own contexts. This gives rise to a design in which there are usually 3 main C-style function prototype along the lines of.. 'bool XXXInit()', 'bool XXXKill()' and 'void* XXXInstance()' to get a handle to 'service' provided by the dll (be it singleton, or in a multi-context managed by dll), called by the application after it has a handle (from LoadLibrary()). I'm hoping you'll say this is the 'better' way than using DllMain as an initialisation routine? Not that it makes a difference to how someone else decided to write it. o.0

Forgive my naivety here, So if I'm understanding the process right, it is not possible to create a kinda of complex middleware like a message broker which can hook into the calls from Application, and give better control over the context? This can't solve the Soulbringer problem since you don't know what information is returned (and what is done) by the DllMain routine (which may have to be temporarily persisted somewhere) irrelevant of you bypassing it, and then some weird race condition that means the hooks occur before DllMain is called? If I'm understanding the problem here?

UCyborg, many thanks for that little snippet 😀. I wouldn't mind giving this a go on something, I've 'stack smashed' and 'overflowed' my own programs before, but the never something from somebody else. I find it quite scary having to enter a mass of code of which I have no idea of its flow o.0.

Sorry, one more (series of) question(s)...
So a code cave is different/safer than a smash since flow is simply redirected within a valid frame (rather than jumping to somewhere else in the stack)? And you use OllyDbg to insert the jump/cave thus being able to jump to your own injected code (added by OllDbg in the 'globals' section, or insert another section into the dll)? The patch you do is specifically for the dgVoodoo? or this patch so that it can run at all with later api's?
Why was this once considered safe (enough for the original authors to do stuff in DllMain), but now causing problems?

Many thanks for you time. 😀

Reply 6 of 39, by UCyborg

User metadata
Rank Member
Rank
Member
spiroyster wrote:

Didn't realise DllMain was such an issue. Personally I diverged away from using Dll's in this manner as I didn't like this tighter coupling between application and dll, opting instead for the dll to fully understand how it should manage its own contexts. This gives rise to a design in which there are usually 3 main C-style function prototype along the lines of.. 'bool XXXInit()', 'bool XXXKill()' and 'void* XXXInstance()' to get a handle to 'service' provided by the dll (be it singleton, or in a multi-context managed by dll), called by the application after it has a handle (from LoadLibrary()). I'm hoping you'll say this is the 'better' way than using DllMain as an initialisation routine? Not that it makes a difference to how someone else decided to write it. o.0

Yes, this is exactly how you should it. The main point is, you don't put the initialization/cleanup code in DllMain under DLL_PROCESS_ATTACH/DLL_PROCESS_DETACH, but in XXXInit/XXXKill.

spiroyster wrote:

Forgive my naivety here, So if I'm understanding the process right, it is not possible to create a kinda of complex middleware like a message broker which can hook into the calls from Application, and give better control over the context? This can't solve the Soulbringer problem since you don't know what information is returned (and what is done) by the DllMain routine (which may have to be temporarily persisted somewhere) irrelevant of you bypassing it, and then some weird race condition that means the hooks occur before DllMain is called? If I'm understanding the problem here?

If it can be done, it must require some trickery. I know you could intercept calls to LoadLibrary to be able to tamper with a loaded DLL, but the ideal DLL wouldn't do anything in DllMain. But if it does, that DLL will already run some code before you even get its handle from LoadLibrary, at which point it might be too late to do whatever you wanted to do. If it has special initialization routine, you can hook it as soon as you got the handle with the help of GetProcAddress function.

And while you probably could hook calls to Windows APIs the DLL in question makes before you load it, its DllMain will be called when the loader lock is held, so, possible deadlocks. These guys put it well: http://help.autodesk.com/cloudhelp/2017/ENU/M … B6650477CF1.htm

Writing correct DllMain() functions is difficult. MSDN describes a series of restrictions, but these are so often ignored that Microsoft had to relax these restrictions by adding IJW ("It Just Works") technology.

I've read somewhere some time ago that even GetModuleHandle is forbidden, though it's possible to use it without negative consequences.

spiroyster wrote:

UCyborg, many thanks for that little snippet 😀. I wouldn't mind giving this a go on something, I've 'stack smashed' and 'overflowed' my own programs before, but the never something from somebody else. I find it quite scary having to enter a mass of code of which I have no idea of its flow o.0.

Copied from patched Max Payne 1 DLLs, not meant to be directly copy pasted somewhere, just as a demonstration of the concept. You have to piece the rest of the puzzle together yourself. Soulbringer version also has some checks in place that make my magic work only if the specific DLL is loaded, since it uses a common function for library loading, which also loads DLLs not related to rendering.

spiroyster wrote:

So a code cave is different/safer than a smash since flow is simply redirected within a valid frame (rather than jumping to somewhere else in the stack)? And you use OllyDbg to insert the jump/cave thus being able to jump to your own injected code (added by OllDbg in the 'globals' section, or insert another section into the dll)?

Actually, there isn't much involving the stack with these patches. Think of it like this, I'm simply modifying it as it was originally written in assembly. But because I don't have assembly sources, I can't just insert the new line and put some extra code in the middle of the function (sometimes you can if there is enough space, it's a bit cumbersome task though) so I write a jump that jumps down there in the empty part of code section, occupied by zeros, where I put the extra instructions and the jump back to where I've come from.

One thing about DllMain, when you open a DLL in OllyDbg, the code you see right away as the entry point isn't really the one written in DllMain by the programmer, the one programmer wrote is called as one of the subroutines there.

I did manage to write some patches for Drakan: Order of the Flame as part of my AiO Patch that don't jump to the empty section of code, simply because there was enough space to do so. For example, one original function looks like this:

CPU Disasm
Address Hex dump Command Comments
00462BE0 /. 51 PUSH ECX
00462BE1 |. 56 PUSH ESI
00462BE2 |. 8B7424 0C MOV ESI,DWORD PTR SS:[ARG.1]
00462BE6 |. 33C0 XOR EAX,EAX
00462BE8 |. 57 PUSH EDI
00462BE9 |. 85F6 TEST ESI,ESI
00462BEB |. 8BF9 MOV EDI,ECX
00462BED |. 894424 08 MOV DWORD PTR SS:[LOCAL.0],EAX
00462BF1 |. 75 2D JNZ SHORT 00462C20
00462BF3 |. 8B4F 04 MOV ECX,DWORD PTR DS:[EDI+4]
00462BF6 |. 8D4424 08 LEA EAX,[LOCAL.0]
00462BFA |. 50 PUSH EAX ; /parg => OFFSET LOCAL.0
00462BFB |. 68 7F660440 PUSH 4004667F ; |cmd = FIONREAD
00462C00 |. 51 PUSH ECX ; |socket => [ARG.ECX+4]
00462C01 |. FF15 60934700 CALL DWORD PTR DS:[<&WS2_32.#10>] ; \WS2_32.ioctlsocket
00462C07 |. 83F8 FF CMP EAX,-1 ; CONST FFFFFFFF => SOCKET_ERROR
00462C0A |. 75 08 JNE SHORT 00462C14
00462C0C |. FF15 64934700 CALL DWORD PTR DS:[<&WS2_32.#111>] ; [WS2_32.WSAGetLastError
00462C12 |. 8BF0 MOV ESI,EAX
00462C14 |> 8B4424 08 MOV EAX,DWORD PTR SS:[LOCAL.0]
00462C18 |. 85C0 TEST EAX,EAX
00462C1A |. 75 04 JNZ SHORT 00462C20
00462C1C |. 85F6 TEST ESI,ESI
00462C1E |. 74 09 JZ SHORT 00462C29
00462C20 |> 8B17 MOV EDX,DWORD PTR DS:[EDI]
00462C22 |. 50 PUSH EAX
00462C23 |. 56 PUSH ESI
00462C24 |. 8BCF MOV ECX,EDI
00462C26 |. FF52 50 CALL DWORD PTR DS:[EDX+50]
00462C29 |> 5F POP EDI
00462C2A |. B8 01000000 MOV EAX,1
00462C2F |. 5E POP ESI
00462C30 |. 59 POP ECX
00462C31 \. C2 0400 RETN 4

The modified version:

CPU Disasm
Address Hex dump Command Comments
00462BE0 /. 51 PUSH ECX
00462BE1 |. 56 PUSH ESI
00462BE2 |. 8B7424 0C MOV ESI,DWORD PTR SS:[ARG.1]
00462BE6 |. 33C0 XOR EAX,EAX
00462BE8 |. 57 PUSH EDI
00462BE9 |. 85F6 TEST ESI,ESI
00462BEB |. 8BF9 MOV EDI,ECX
00462BED |. 894424 08 MOV DWORD PTR SS:[LOCAL.0],EAX
00462BF1 |. 75 33 JNZ SHORT 00462C26
00462BF3 |. 8B4F 04 MOV ECX,DWORD PTR DS:[EDI+4]
00462BF6 |. 8D4424 08 LEA EAX,[LOCAL.0]
00462BFA |. 50 PUSH EAX ; /parg => OFFSET LOCAL.0
00462BFB |. 68 7F660440 PUSH 4004667F ; |cmd = FIONREAD
00462C00 |. 51 PUSH ECX ; |socket => [ARG.ECX+4]
00462C01 |. FF15 60934700 CALL DWORD PTR DS:[<&WS2_32.#10>] ; \WS2_32.ioctlsocket
00462C07 |. 83F8 FF CMP EAX,-1 ; CONST FFFFFFFF => SOCKET_ERROR
00462C0A |. 74 23 JE SHORT 00462C2F
00462C0C |. 8B4424 08 MOV EAX,DWORD PTR SS:[LOCAL.0]
00462C10 |. 83F8 01 CMP EAX,1
00462C13 |. 7F 11 JG SHORT 00462C26
00462C15 |. 56 PUSH ESI ; /pfromlen
00462C16 |. 56 PUSH ESI ; |pfrom
00462C17 |. 56 PUSH ESI ; |flags
00462C18 |. 56 PUSH ESI ; |bufsize
00462C19 |. 56 PUSH ESI ; |buf
00462C1A |. FF74E4 14 PUSH DWORD PTR SS:[LOCAL.2] ; |socket => ARG.EDI
00462C1E |. FF15 14934700 CALL DWORD PTR DS:[<&WS2_32.#17>] ; \WS2_32.recvfrom
00462C24 |. EB 09 JMP SHORT 00462C2F
00462C26 |> 8B17 MOV EDX,DWORD PTR DS:[EDI]
00462C28 |. 50 PUSH EAX
00462C29 |. 56 PUSH ESI
00462C2A |. 8BCF MOV ECX,EDI
00462C2C |. FF52 50 CALL DWORD PTR DS:[EDX+50]
00462C2F |> 5F POP EDI
00462C30 |. B8 01000000 MOV EAX,1
00462C35 |. 5E POP ESI
00462C36 |. 59 POP ECX
00462C37 \. C2 0400 RETN 4

Saved some space because the WSAGetLastError call wasn't actually needed. 😁

Anyway, I think the term injection only applies if you're modifying/inserting the code during runtime. Patches in this thread are simply modified binaries, so nothing new is written when they're being used, just existing code flowing differently due to extra checks.

Back to my Drakan AiO Patch, the simpler fixes (which is actually a majority) are implemented as regular assembly code fixes and they work by just having the patched executables in place, same as patches in this thread, more complicated fixes are implemented in proxy dinput.dll, powered by this code. One of the strangest pieces of code, I use the DetourFunction to insert the jump right in the middle the function (it's usually used to redirect the flow right at the beginning of the function), which makes a jump to the code in my DLL, the relevant lines are 538 and 309-325. It's a wonder that mess of the code works as good as it does! 😁

One thing about working with disassembled code, you have to be careful with register usage. AiO Patch had a bug at one point in time which would mysteriously cause the game server to crash on older Windows versions after player connected. It was because I made a call to one of the API functions, overlooking the details regarding calling conventions and associated register preservation rules. XP version of the API overwrote one of the registers which content was needed by game code after the call. It worked on later versions simply because the API function was optimized better and didn't utilize register in question, but it could if it wanted to. The API in question was ntohl/htonl, which just changes endianness of the integer.

spiroyster wrote:

The patch you do is specifically for the dgVoodoo? or this patch so that it can run at all with later api's?

Nope, it allows the games to run at all out-of-the box. Max Payne is very famous for not starting at all with ATI cards without bizarre workarounds like setting process affinity to one core (which is not needed anymore with this patch), maybe Intel too, but I haven't tried. Nothing is shown on the screen, just the process hangs in the Task Manager. Note that this isn't driver issue at all, although people love to blame everything from drivers to Windows. Soulbringer now also doesn't hang anymore on AMD hardware, although there is a separate compatibility issue preventing it from working with native DirectX, but dgVoodoo helps with that.

spiroyster wrote:

Why was this once considered safe (enough for the original authors to do stuff in DllMain), but now causing problems?

Under certain circumstances, it just works, but it's pure luck. MSDN says DllMain shouldn't be used like that, on the other hand, default project template in Visual Studio 2015 for DLL puts DllMain in code without any warnings, so it may look tempting to use it. Another thing worth mentioning, DllMain is Windows-specific, there is no exact equivalent Linux, closest things are __attribute__((constructor)) and __attribute__((destructor)), but why make life difficult when writing special initialization function is so simple?

I've read somewhere when with this type of problem occured, one thing people with AMD cards also did is placing older atiumdag.dll in the game folder. So, mixing older driver DLLs with newer ones, that's also the recipe for disaster.

Arthur Schopenhauer wrote:

A man can be himself only so long as he is alone; and if he does not love solitude, he will not love freedom; for it is only when he is alone that he is really free.

Reply 7 of 39, by Dege

User metadata
Rank l33t
Rank
l33t
UCyborg wrote:
GOG version patches, which take care of aspect ratio and cinematic issues, don't work when we put dgVoodoo in game folder, but d […]
Show full quote

GOG version patches, which take care of aspect ratio and cinematic issues, don't work when we put dgVoodoo in game folder, but dgVoodoo can take care of aspect ratio (select 4:3 resolution in game, set Scaling mode in dgVoodoo to Stretched, keep Aspect Ratio). They also don't work with DX7 modes (stretched image). EAX enabled executable also doesn't work, just crashes, seems like they haven't patched it like the non-EAX one. I do have working Creative ALchemy installation, including EAX Unified, which other games are able to use just fine for querying EAX capabilities. Parts of the game also stutter a little.

So I went on and patched the retail version files, put dgVoodoo in the folder and things look mostly fine at first glance. 256-color software mode works as well as DX6, which allows 32-bit color mode (though game textures are only 16-bit I think). DX7 modes result in black screen, music plays, the game runs, just nothing is shown. Seems to run smoothly. EAX .exe works as good as the other one.

The game really doesn't like AMD cards out-of-the-box, there is another issue in addition to DllMain. Impossible to even reach main menu as long as it's running via native Direct3D, no matter what. Get dgVoodoo in its place and it works!

One oddity that remains when ran through dgVoodoo is the intro cinematic, it runs in a small borderless window on the top-left and the taskbar flickers like crazy when said window appears when scaling mode is set to Unspecified. If dgVoodoo is allowed to control fullscreen/windowed state, borders appears around it and there is no taskbar flickering. If I set Stretched, keep Aspect ratio, it appears fullscreen, but there is just blackness (just like with native DirectX). GOG version has a patch for that that only works with native DirectX. Similar problem exists with a brief screen with game logo that displays after the intro cinematic, it appears in a small borderless window in the top-left corner.

Launcher can be buggy, probably the best to use it to set resolution then launch the game via one of the other executables. Can run via native DirectX 6 or 7 with some fiddling on NVIDIA graphics card, best not to try though.

TL;DR This was all over the place. It's a buggy game out-of-the box, less buggy when you get it running via dgVoodoo. You can download patched binaries and try it out with dgVoodoo if you'd like.

Soulbringer.png

Thanks! Cool!! 😎
I've just tried it and indeed, cinematics doesn't work in fullscreen (probably Intel Indeo which is not supported through dgVoodoo, can only be displayed in windowed mode, old issue) and DX7 gives black screen (that one should be fixed).

Reply 8 of 39, by Dege

User metadata
Rank l33t
Rank
l33t
Dege wrote:

Thanks! Cool!! 😎
I've just tried it and indeed, cinematics doesn't work in fullscreen (probably Intel Indeo which is not supported through dgVoodoo, can only be displayed in windowed mode, old issue) and DX7 gives black screen (that one should be fixed).

Didn't have much time so far but:
- I was wrong, cinematics are not Intel Indeo. This game gets switched back to windowed mode at movie presentations (must do sg similar like SC1) and the wrapper couldn't handle that unexpected situation, movies fixed
- Rendering through DX7 is also fixed, the game performs its texture uploadig in a valid, but for dgVoodoo an unexpected way, fixed (at least it got revealed)

Reply 9 of 39, by UCyborg

User metadata
Rank Member
Rank
Member

Updated all patches to handle the case when DllMain returns FALSE when called with DLL_PROCESS_ATTACH, in which scenario the library must be unloaded. For correctness sake.

I've also upgraded the Soulbringer patch to get it working a little better under native DirectX. DX7 renderer doesn't enable VSYNC on its own so it runs too fast which makes the game glitchy. BTW, I noticed that whatever guys at GOG did with their version, their tweaks also influence the game flow itself in subtle ways and introduce stutter. Like in the first cutscene, the ferryman goes around the house to reach the door like he's drunk, then noclips through it, funny. Original version doesn't have that issue.

I'm glad you found the problem with DX7. I wonder though, does Soulbringer's DX7 mode have VSYNC under dgVoodoo without forcing it? Because it seem to get activated only with DX6. It must run at 60 FPS, otherwise it gets glitchy. Interestingly, it runs at correct speed on its own under native DX7 if you leave maximized windowed mode enabled. But contrary to what the ReadMe says, at least under native DX7, it runs slower than DX6.

About Indeo Video, it works with Mafia just fine, though I remember you said one time things don't work when DirectShow is involved. It just takes running regsvr32 ir50_32.dll from SysWOW64/System32 directory. Windows XP compatibility mode might work too. No issues with dgVoodoo in that regard, though parts of the game are still flickery, at least with Water Shader Mod installed, which is actually D3D8 proxy which injects shaders to make water have reflections. The proxy is named d3df.dll so therefore it modifies import table in a game engine binary to point to that. I haven't tried without it yet.

Last edited by UCyborg on 2017-01-27, 23:34. Edited 2 times in total.
Arthur Schopenhauer wrote:

A man can be himself only so long as he is alone; and if he does not love solitude, he will not love freedom; for it is only when he is alone that he is really free.

Reply 10 of 39, by UCyborg

User metadata
Rank Member
Rank
Member

One more thing regarding Soulbringer, in software mode, it crashes or hangs in one of graphics driver DLLs when you use quit menu option to close it.

Arthur Schopenhauer wrote:

A man can be himself only so long as he is alone; and if he does not love solitude, he will not love freedom; for it is only when he is alone that he is really free.

Reply 11 of 39, by Dege

User metadata
Rank l33t
Rank
l33t
UCyborg wrote:

I'm glad you found the problem with DX7. I wonder though, does Soulbringer's DX7 mode have VSYNC under dgVoodoo without forcing it? Because it seem to get activated only with DX6. It must run at 60 FPS, otherwise it gets glitchy. Interestingly, it runs at correct speed on its own under native DX7 if you leave maximized windowed mode enabled. But contrary to what the ReadMe says, at least under native DX7, it runs slower than DX6.

No, it runs at ~1200 FPS through DX7, so there is no vsync.

UCyborg wrote:

About Indeo Video, it works with Mafia just fine, though I remember you said one time things don't work when DirectShow is involved. It just takes running regsvr32 ir50_32.dll from SysWOW64/System32 directory. Windows XP compatibility mode might work too.

Well, the problem is not with the codec used for movies but the fact that the movie player renders the movie through DDraw/DX8 or legacy GDI.
GDI is not compatible with DXGI in fullscreen that's why no movies in those cases. Intel Indeo always comes paired with a videoplayer that renders through GDI (ok, maybe I'm wrong, I didn't checked the cases when there were no problems with the playback 😀 ).
On the other hand, DirectShow should be OK because that renders through DirectDraw. The latest WIP contains a fix for Win10 though, DDraw+DX8 scenario didn't always work (some mysterious dll path-thing when DShow loads DDraw).

UCyborg wrote:

No issues with dgVoodoo in that regard, though parts of the game are still flickery, at least with Water Shader Mod installed, which is actually D3D8 proxy which injects shaders to make water have reflections. The proxy is named d3df.dll so therefore it modifies import table in a game engine binary to point to that. I haven't tried without it yet.

Hmm, never heard about that mod, up to now. 😀

Reply 12 of 39, by Dege

User metadata
Rank l33t
Rank
l33t
UCyborg wrote:

One more thing regarding Soulbringer, in software mode, it crashes or hangs in one of graphics driver DLLs when you use quit menu option to close it.

It does for me too, with my nVidia. It gets stuck gratuitously in a swapchain call (WaitForSingleObject in the deepness of the driver). 😕
Works OK with Intel HD.

Reply 13 of 39, by UCyborg

User metadata
Rank Member
Rank
Member
Dege wrote:

Hmm, never heard about that mod, up to now. 😀

Well, now you have and can test dgVoodoo with it! 😁 It also alters some data files to work properly. Either way, the game is still rendered flawlessly on Linux under Wine, with the mod or not. No shadow flickering neither. Just runs slower.

Dege wrote:

It does for me too, with my nVidia. It gets stuck gratuitously in a swapchain call (WaitForSingleObject in the deepness of the driver). 😕
Works OK with Intel HD.

Interesting, I got the same result as you with NVIDIA (GeForce GTX 750 Ti - 372.90 drivers) and a crash on AMD (Radeon R2 - 16.11 driver). NVIDIA went downhill with drivers these days generally.

Last edited by UCyborg on 2017-01-25, 01:44. Edited 1 time in total.
Arthur Schopenhauer wrote:

A man can be himself only so long as he is alone; and if he does not love solitude, he will not love freedom; for it is only when he is alone that he is really free.

Reply 14 of 39, by UCyborg

User metadata
Rank Member
Rank
Member

One more thing when you have the time, can you try Soulbringer with forcing VSYNC off through drivers and check whether it runs slower or faster in DX6 mode on your end?

Arthur Schopenhauer wrote:

A man can be himself only so long as he is alone; and if he does not love solitude, he will not love freedom; for it is only when he is alone that he is really free.

Reply 15 of 39, by CoolGamer

User metadata
Rank Member
Rank
Member

UCyborg,

I tried the patch you made for 3DMark99 Max, but I am unable to get the application to start on my Windows 7 Ultimate 64bit with GT230M laptop. I just installed the program in admin mode and applied your patch. I did not apply any compatibility settings. When I run the application I get an error that says "3DMark needs Directx 6.1" and it shuts down. I get the same error with and without dgVoodoo.

Are you able to run 3DMark99 Max on your computer? Can you give me some tips to run it?

Thanks.

Reply 16 of 39, by teleguy

User metadata
Rank Member
Rank
Member
CoolGamer wrote:
UCyborg, […]
Show full quote

UCyborg,

I tried the patch you made for 3DMark99 Max, but I am unable to get the application to start on my Windows 7 Ultimate 64bit with GT230M laptop. I just installed the program in admin mode and applied your patch. I did not apply any compatibility settings. When I run the application I get an error that says "3DMark needs Directx 6.1" and it shuts down. I get the same error with and without dgVoodoo.

Are you able to run 3DMark99 Max on your computer? Can you give me some tips to run it?

Thanks.

Works for me. Either using Win 98 mode or this:

Attachments

Reply 17 of 39, by UCyborg

User metadata
Rank Member
Rank
Member

I personally use compatibility database with the following fixes:

  • DXPrimaryEmulation -DisableMaxWindowedMode
  • FileVersionInfo
  • EmulateHeap

The first one is needed on Windows 8+ to get real fullscreen mode under native Direct3D, that is, without dgVoodoo, but there's no harm if it's enabled on Windows 7, the second one makes sure the game doesn't complain about DirectX because it checks version of some DirectX DLL and bails out when it sees it's not the version it excepts, the 3rd one compensates for buggy memory management in the program which makes it randomly crash either during transitions between tests or before showing the final scores when it tries to release block of memory. The crash point is inside ntdll.dll.

I'm attaching updated .sdb file. If you want, you can search for Compatibility Toolkit 5.6, which includes Compatibility Administrator, which is the program for working with these .sdb files and lets you see what's inside and change them. For Windows 8+, there's Windows 8 and Windows 10 ADK respectively, which includes Compatibility Administrator.

The thing about the database from teleguy, it lacks EmulateHeap, which is critical for preventing random crashes and includes bunch of redundant fixes. Applying Windows 98 compatibility mode from .exe file's Properties also gives you a lot of unneeded fixes as well. It's a good practice to use only minimum amount of fixes required by certain application/game.

Attachments

Arthur Schopenhauer wrote:

A man can be himself only so long as he is alone; and if he does not love solitude, he will not love freedom; for it is only when he is alone that he is really free.

Reply 18 of 39, by CoolGamer

User metadata
Rank Member
Rank
Member

Teleguy,

Thanks for the compatibility fix. It worked for me.

UCyborg,

Thanks for the updated/enhanced fix and the detailed explanations. I am using your fix now. When combined with your DLL patch, I was able to run 3DMark99 Max both with and without dgVoodoo. I also downloaded the Compatibility Toolkit and will use it.

Reply 19 of 39, by UCyborg

User metadata
Rank Member
Rank
Member

There is another advantage of getting familiar with Compatibility Administrator, you can put fixes for many applications in one database. Windows comes with a huge database with fixes for tons of different applications. It's common for people to distribute databases with a fix for a single application, which is understandable, as it's the easy way to get the fixes to end users who are unfamiliar with technical details involved. By having all of the fixes in one database, it's easy to transfer all of them to a different machine.

As a point of interest, Compatibility Administrator 5.6 has 2 annoying bugs, I seem to remember uninstalling database from the system, making modifications to the copy you have elsewhere (you can't modify installed version) and installing it again seems to fail, unless you close and re-open program when you want to re-install database.

Sometimes you need to disable entries in system database because some things aren't needed anymore. For example, alt-tab combination is needlessly blocked for Drakan: Order of the Flame. There is a bug in 5.6 version of the program that writes this information in the wrong part of the registry on 64-bit systems, so it's impossible to disable certain entry without manually inspecting registry and putting the information in correct place. It writes to HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\AppCompatFlags instead of HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags This was solved with later versions, but they don't work on Windows 7.

Oh. and technically correct term for individual fix is shim.

Arthur Schopenhauer wrote:

A man can be himself only so long as he is alone; and if he does not love solitude, he will not love freedom; for it is only when he is alone that he is really free.