DirectDraw output adaptation

519 Views Asked by At

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?

2

There are 2 best solutions below

0
Carlos Cortez On BEST ANSWER

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

3
Adrian McCarthy On

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.