elishacloud / dxwrapper

Fixes compatibility issues with older games running on Windows 10/11 by wrapping DirectX dlls. Also allows loading custom libraries with the file extension .asi into game processes.
zlib License
1.15k stars 82 forks source link

DDS Textures are scrambled #190

Closed dkollmann closed 1 year ago

dkollmann commented 1 year ago

There is something wrong with the surface created for DDS textures.

This is the original texture extracted from the game folder: image

This is the surface, which I saved as a DDS file, afteer the surface is unlocked. image

The texture is a DXT3 BC2_UNORM one: 18.zip

My guess is that the surface is already created with incorrect settings, so that the data layout of the memory does not align with the one from the surface.

elishacloud commented 1 year ago

Yes, I have seen this also. It looks like only the top part of the file is preserved. This could be for a couple of reasons:

  1. The texture is created early in the and not properly backed up when the surface is recreated after getting released.
  2. It could be that the texture is not loaded right into memory, if it is loaded by Blt() (which uses CopySurface()) or by locking the surface.
  3. Or it is not creating the right surface to begin with.

Do you know how/when the surface is loaded into memory?

elishacloud commented 1 year ago

I just checked and the textures are not getting released and reloaded. Also, the requested type of texture is being created correctly. i.e. When the game requests D3DFMT_DXT3 then a D3DFMT_DXT3 texture is getting created.

I also checked the texture size (width & height) and the requested size is getting created.

Somehow it seems there is an issue with populating the texture. Maybe the way DirectDraw populates the texture does not work correctly with Direct3D9 textures?

Textures are done in blocks. It looks like the first row of blocks are getting populated but none of the other rows.

Here is a good post I found: https://www.g-truc.net/post-0335.html

dkollmann commented 1 year ago

So I did a test, where I instead of letting the game write into the surface buffer, I let it write into my own memory and saved that data as a DDS file. The result is the same. And the memory matches what we see, so most of it remains zero.

So it seems to me that the Unlock function is called too early.

elishacloud commented 1 year ago

That's weird. I am seeing the call to Unlock, but I don't see the call to Lock the DXT textures.

dkollmann commented 1 year ago

I hooked into

m_IDirectDrawSurfaceX::LockD39Surface(D3DLOCKED_RECT* pLockedRect, RECT* pRect, DWORD Flags)

elishacloud commented 1 year ago

I hooked into m_IDirectDrawSurfaceX::LockD39Surface()

The weird thing is that Lock() and Unlock() are supposed to be a pair and LockD39Surface() and UnlockD39Surface() are supposed to be a pair. However, I am seeing Unlock() being called without Lock() ever getting called. Something weird is happening here and I am not sure yet if it is something weird with the game or with dxwrapper.

PatrickvL commented 1 year ago

Perhaps completely unrelated, but on some compiles/platforms (like original Xbox) Lock got inlined, meaning any interface (re-)implementation wouldn't ever get called (instead, the code itself was compiled into the caller location)

dkollmann commented 1 year ago

Another idea to fix this could be to just call the other function if it is a DXT surface type.

elishacloud commented 1 year ago

It appears that the issue is an issue with lPitch. DirectDraw returns 65535 whereas Direct3D9 returns 1024 for some of the DXT formats.

dkollmann commented 1 year ago

1024 is definitely correct. The format BC2 has 16 bytes per block, which is four pixels. So each pixel has four bytes. As the texture is 256x256, that is a pitch of 1024.

elishacloud commented 1 year ago

If you change the following line then it will be able to draw all the textures correctly:

From:

lpDDSurfaceDesc2->lPitch = LockedRect.Pitch;

to:

lpDDSurfaceDesc2->lPitch = LockedRect.Pitch * (ISDXTEX(surfaceFormat) ? 64 : 1);
dkollmann commented 1 year ago

OMG you are a genius!! Yes, that fixed it :)

elishacloud commented 1 year ago

Fixed this issue in the following check in: 6e2db420cd8dc80cab13f41cbecdba48d0442c4b

elishacloud commented 1 year ago

Closing issue.