0

I'm working on a project to add functionality to an old game; the idea is to add the option to run it windowed (originally it only supports 800x600 fullscreen).

So far, i modified directDraw's initialization to remove fullscreen exclusive mode and to enable it to work with GDI, created a clipper and set everything properly the point is, the game sets the fullscreen mode to 8bit color depth, running it windowed causes it to output garbage like this image:

Game output running windowed

So far i tried to do some tricks using GetDIBits and SetDIBits to work around the problem but i had no success.

The game works with a very old directDraw version (there is no documentation at all about this version, most of my work is based on guesses and tests).

The game uses BltFast to get information to the screen.

Here is a piece of the code that was written to try to fix the issue using DIBits

PrimarySurface = Game primary surface itself

patch_1:
  ; Blt interface
  CALL DWORD [EDX+1Ch] ;<--- Surface->BltFast() Outputs game screen, params pushed to stack elsewhere

  pushad

  ; Get diBits and transform it properly to display
  push surfaceDC
  mov eax, [PrimarySurface]
  mov edx, [eax]
  push eax
  call dword [edx+44h] ; Surface->GetDC(&surfaceDC)
  test eax, eax
  je patch_1_abort
    invoke FindWindowA, 0, 'Rising Lands'
    mov [windowHandle], eax
    invoke GetDC, eax
    mov [windowDC], eax
    invoke CreateCompatibleDC, [surfaceDC]
    mov [compatibleDC], eax
    invoke CreateCompatibleDC, [windowDC]
    mov [compatibleWindowDC], eax
    invoke CreateCompatibleBitmap, [windowDC], 800, 600
    mov [zbitmap], eax

    ; Get screen header
    invoke GetDIBits, [compatibleWindowDC], [zbitmap], 0, 0, 0, bitmapHeader, 0

    ; Get game screen data
    invoke GetDIBits, [compatibleDC], [zbitmap], 0, 600, bbuffer, bitmapHeader, 0

    ; Copy content back to screen
    invoke SetDIBits, [compatibleWindowDC], [zbitmap], 0, 600, bbuffer, bitmapHeader, 0

    ; Release
    push [surfaceDC]
    mov eax, [PrimarySurface]
    mov edx, [eax]
    push eax
    call dword [edx+68h]

  patch_1_abort:
  popad

  ; Original code finalization
  MOV EDX,EAX
  TEST EAX, EAX
  jmp [p1r]

  surfaceDC dd 0
  windowHandle dd 0
  windowDC dd 0
  compatibleDC dd 0
  compatibleWindowDC dd 0
  zbitmap dd 0


  bitmapInfo dd bitmapHeader
  dd 0

  bitmapHeader:
  hSize dd endHeader - bitmapHeader
  hWidth dd 0;800
  hHeight dd 0;600
  hPlanes dw 0;1
  hBitCount dw 0;32
  hCompression dd 0
  hSizeImage dd 0
  hxPPm dd 0
  hyPPm dd 0
  hClrUsed dd 0
  hClrImp dd 0
  endHeader:

  bbuffer rb 800*600*10 

Is there any way i can convert the 8bit color format directly from the buffer by changing the call to BltFast to output correctly to the screen?

Is it a better solution to reroute the output to another DC to then use GetDIBits/SetDIBits to fix the image?

  • 3
    It looks either your source format isn't what you expect it to be or your destination isn't. Are you sure the game isn't using 800x600x4 (16 colours)? In any case, I think you're going about this wrong way, I wouldn't use GDI at all. I'd just write a DirectDraw wrapper that emulated exclusive mode using DirectDraw windowed mode. No need to patch the executable or use assembly. just dump your own DDRAW.DLL in the same directory. There's already at least one program, who's name escapes me, that does this already. – Ross Ridge Aug 05 '16 at 19:34
  • I'm kinda new to DirectDraw so i really have no idea how i would emulate windowed mode based on the fullscreen, do you have any reference on that? Also, the game uses a extremelly old version of ddraw which i guess windows switches to some compatibility version of (if you use the ddraw distributed with the game, nothing will work at all) – Carlos Cortez Aug 05 '16 at 19:41
  • DirectDraw 7 is still documented online: https://msdn.microsoft.com/en-us/library/windows/desktop/gg426124.aspx – Adrian McCarthy Aug 05 '16 at 19:53
  • I found "DirectDraw hack" which seems to be the fix you mentioned; It doesn't work for the game i'm working with but i will try to adapt it to my situation. Still, i guess converting a 8bit image to 32bit should be easier, i just have no idea how to get this correctly so far – Carlos Cortez Aug 05 '16 at 20:38

2 Answers2

2

After a lot of research, i found a wrapper (as suggested by Ross Ridge) that does the trick and forces the game to work with openGL and also enables a lot of neat features as well and is compatible even with the first versions of ddraw

https://sourceforge.net/p/dxwnd

0

The original game is 8-bit, so it relies on a palette. GetDIBits will use the current palette associated with its DC in order to translate the 8-bit pixel values into the 32-bit values you're requesting in with the bitmapHeader.

I think this is the first part of the problem, as the game's DC probably doesn't have the palette selected and realized. Since it was using DirectDraw in exclusive mode, it was probably setting the palette directly in the video card, and GDI had no notion of what that palette was.

To solve this, I think you're going to need to find the code that sets up and modifies the palette, and then explicitly select and realize that palette into the game's DC before you do the GetDIBits.

After that, I'm unclear how you're getting the pixels to the window DC. You seem to create a memory DC that's compatible with the window and then use SetDIBits to get the data to it, but I don't see where you then blit from the memory DC to the actual window DC.

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
  • The screenshot shows the game running without the actual fix using the Dibits approach (as i mentioned, it didnt really work); My idea was to get the DC from the surface, then get the destination window BitmapInfo to specify the DIB format to then finally copy data. Would you have a code sample about the approach you mentioned? The game does create a palette and i do have access to it. – Carlos Cortez Aug 05 '16 at 19:31
  • I don't have a code sample, but the basic pattern is to call CreatePalette, then select it into the DC with SelectPalette. After selecting it in, you might also need to call RealizePalette, but I'm not sure if that last step necessary for the type of DC you have. – Adrian McCarthy Aug 05 '16 at 21:44
  • 1
    To get the data to the window, you may be able to skip the memory DC and use SetDIBitsToDevice to get the pixel data to the window DC directly. – Adrian McCarthy Aug 05 '16 at 21:46