//
// Project UnReality: The First Nintendo 64 Emulator
// Copyright (C) 1997-1998, Cold Fusion Entertainment
// All rights reserved
//
// This source code is unpublished propietary information of Cold Fusion Entertainment.  No portion of this software
// may be disclosed to any third party or copied or duplicated in any form, in whole or in part, without the prior
// written consent of Cold Fusion Entertainment.
// 

//
// $Log: GFX.CPP $
// Revision 1.3  1998/03/08 19:12:22  BREAKPOINT
// Version 0.3b.3 checkin... I need to really check this stuff in more often..
// Revision 1.2  1998/01/31 19:50:22  BREAKPOINT
// Added Convert5551to555() to change from N64's framebuffer to Windows'.
// Revision 1.1  1998/01/10 01:26:54  BREAKPOINT
// Initial revision
//

static char *rcsid = "$Id: GFX.CPP 1.3 1998/03/08 19:12:22 BREAKPOINT Exp $";

//---------------------------------------------------------------------------------------------------------------------

#include <stdio.h>
#include <ddraw.h>
#define EXTERN
#include <types.h>
#include "gfx.h"
#include "unreal.h"

#include <windows.h>
#include <windowsx.h>

void (*Flip)(void);
void (*Clear)(void);
uchar *(*Lock)(void);
void (*Unlock)(void);

#define ABS(x)              ( ((x) >= 0 ) ? (x) : (-(x)) )

LPDIRECTDRAW lpDD;
LPDIRECTDRAWSURFACE lpDDSPrimary, lpDDSBack, lpDDSOff;
LPDIRECTDRAWSURFACE lpDDSDrawable;
LPDIRECTDRAWPALETTE lpDDPal;

uint   DD_Xsize, DD_Ysize, DD_FullXsize;
uchar  DD_Depth, *DD_Surfptr, vga_pal[768];
ulong *DD_RowOffsets;
HWND   DD_hWnd;
bool   Using_BackBuffer = FALSE;

// In-a-window stuff...
bool   WinInUse = FALSE;
HDC    hdc;
BITMAPINFO *bi;
uchar *FrameBuffer;

bool UsingWindow(void)
{
	return WinInUse;
}

void ResetMode(void)
{
	HRESULT res;

	// Up cooperative level to exclusive fullscreen.
    res = lpDD->SetCooperativeLevel(DD_hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWREBOOT);
    if (res != DD_OK) return;

	// Set mode.
    lpDD->SetDisplayMode(DD_Xsize, DD_Ysize, DD_Depth);
}

bool RestoreSurfaces(void)
{
	HRESULT res;

	// Don't attempt to restore surfaces if we're using a window.
	if (WinInUse) return TRUE;

	// Restore primary surface.
	if (lpDDSPrimary->IsLost() == DDERR_SURFACELOST)
	{
		res = lpDDSPrimary->Restore();
		if (res != DD_OK)
		{
			ResetMode();
			if (lpDDSPrimary->Restore() != DD_OK) return FALSE;
		}
	}

	// If not using a backbuffer, restore offscreen surface.
	if (!Using_BackBuffer)
	{
		if (lpDDSOff->IsLost() == DDERR_SURFACELOST)
		{
			res = lpDDSOff->Restore();
			if (res != DD_OK) return FALSE;
		}
	}

	return TRUE;
}

bool Mode_Found;

HRESULT WINAPI EnumModesCallback(LPDDSURFACEDESC lpDDSurfaceDesc, LPVOID lpContext)
{
	Mode_Found = TRUE;
	return DDENUMRET_CANCEL;
}

bool ModeExist(uint xsize, uint ysize, uchar bpp)
{
	DDSURFACEDESC ddsd;

	Mode_Found = FALSE;

	// Create comparatative descriptor.
    ZeroMemory(&ddsd, sizeof(ddsd));
    ddsd.dwSize = sizeof(ddsd);
    ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
    ddsd.dwWidth = xsize;
    ddsd.dwHeight = ysize;

	ZeroMemory(&ddsd.ddpfPixelFormat, sizeof(ddsd.ddpfPixelFormat));
	ddsd.ddpfPixelFormat.dwSize = sizeof(ddsd.ddpfPixelFormat);
	ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB;
	ddsd.ddpfPixelFormat.dwRGBBitCount = bpp;

	lpDD->EnumDisplayModes(0, &ddsd, NULL, EnumModesCallback);
	return Mode_Found;
}

bool InitDirectDraw(HWND hwnd, uint xsize, uint ysize, uchar bpp, bool use_backbuffer, bool use_window)
{
    uint t;
    HRESULT res;
    DWORD dwStyle, pitch;
    DDSCAPS ddscaps;
    DDSURFACEDESC ddsd;
    PALETTEENTRY temp_pal[256];
    RECT rcWork, rc;

    // Set some things to NULL...
    lpDD          = NULL;
    lpDDSPrimary  = NULL;
    lpDDSBack     = NULL;
    lpDDSOff      = NULL;
    lpDDPal       = NULL;
    DD_Surfptr    = NULL;
    DD_RowOffsets = NULL;

    // Set some other things...
    DD_Xsize = xsize;
    DD_Ysize = ysize;
    DD_Depth = bpp;
	DD_hWnd  = hwnd;
	Using_BackBuffer = use_backbuffer;

    // Create DD Object (ALWAYS).
    res = DirectDrawCreate(NULL, &lpDD, NULL);
    if (res != DD_OK) return FALSE;

    if (use_window)
    {
		// We can get fullscreen access, but we don't need it... so release it.
		res = lpDD->SetCooperativeLevel(DD_hWnd, DDSCL_NORMAL);
		if (res != DD_OK) return FALSE;

        // Note we're NOT using DirectDraw here... :)  It's too yucky, and is dependent on the bit depth of the display.
        WinInUse = TRUE;

        // Get the DC of the window.
        hdc = GetDC(DD_hWnd);

        // Convert window to a normal looking thing...
        dwStyle  = GetWindowStyle(DD_hWnd);
        dwStyle &= ~WS_POPUP;
        dwStyle |= WS_OVERLAPPED | WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX;
        SetWindowLong(DD_hWnd, GWL_STYLE, dwStyle);
        SetRect(&rc, 0, 0, xsize, ysize);
        AdjustWindowRectEx(&rc, GetWindowStyle(DD_hWnd), GetMenu(DD_hWnd) != NULL, GetWindowExStyle(DD_hWnd));
        SetWindowPos(DD_hWnd, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
        SetWindowPos(DD_hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);

        // Make sure our window does not hang outside of the work area.  This will make people who have the tray on the 
        // top or left happy.
        SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
        GetWindowRect(DD_hWnd, &rc);

        if (rc.left < rcWork.left) rc.left = rcWork.left;
        if (rc.top  < rcWork.top)  rc.top  = rcWork.top;
        SetWindowPos(DD_hWnd, NULL, rc.left, rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);

        // Set function pointers...
        Flip   = BlitToWindow;
        Clear  = ClearWindowBuffer;
        Lock   = LockWindowBuffer;
        Unlock = UnlockWindowBuffer;

        // Allocate the frame buffer.
        FrameBuffer = new uchar[DD_Xsize * DD_Ysize * (DD_Depth >> 3)];
        if (FrameBuffer == NULL) return FALSE;

        // Allocate DIB stuff..
        if (DD_Depth == 8)
        {
            bi = (BITMAPINFO *)malloc(sizeof(BITMAPINFOHEADER) + (256 * sizeof(RGBQUAD)));
        
            bi->bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
            bi->bmiHeader.biWidth         = DD_Xsize;
            bi->bmiHeader.biHeight        = -((LONG)DD_Ysize);
            bi->bmiHeader.biPlanes        = 1;
            bi->bmiHeader.biBitCount      = 8;
            bi->bmiHeader.biCompression   = BI_RGB;
            bi->bmiHeader.biSizeImage     = 0;
            bi->bmiHeader.biXPelsPerMeter = 0;
            bi->bmiHeader.biYPelsPerMeter = 0;
            bi->bmiHeader.biClrUsed       = 0;
            bi->bmiHeader.biClrImportant  = 0;
        }
        else
        {
            bi = (BITMAPINFO *)malloc(sizeof(BITMAPINFOHEADER));
        
            bi->bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
            bi->bmiHeader.biWidth         = DD_Xsize;
            bi->bmiHeader.biHeight        = -((LONG)DD_Ysize);
            bi->bmiHeader.biPlanes        = 1;
            bi->bmiHeader.biBitCount      = DD_Depth;
            bi->bmiHeader.biCompression   = BI_RGB;
            bi->bmiHeader.biSizeImage     = 0;
            bi->bmiHeader.biXPelsPerMeter = 0;
            bi->bmiHeader.biYPelsPerMeter = 0;
            bi->bmiHeader.biClrUsed       = 0;
            bi->bmiHeader.biClrImportant  = 0;
        }

        DD_FullXsize = DD_Xsize * (DD_Depth >> 3);

        DD_RowOffsets = new ulong[DD_Ysize];
        for (t = 0; t < DD_Ysize; t++)
            DD_RowOffsets[t] = (DD_Xsize * t * (DD_Depth >> 3));

        return TRUE;
    }

	// Up cooperative level to exclusive fullscreen.
    res = lpDD->SetCooperativeLevel(DD_hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWREBOOT);
    if (res != DD_OK) return FALSE;

    dwStyle  = GetWindowStyle(DD_hWnd);
    dwStyle |= WS_POPUP;
    dwStyle &= ~(WS_OVERLAPPED | WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX);
    SetWindowLong(DD_hWnd, GWL_STYLE, dwStyle);
    SetRect(&rc, 0, 0, xsize, ysize);
    AdjustWindowRectEx(&rc, GetWindowStyle(DD_hWnd), GetMenu(DD_hWnd) != NULL, GetWindowExStyle(DD_hWnd));

	// Set mode.
    res = lpDD->SetDisplayMode(xsize, ysize, bpp);
    if (res != DD_OK) return FALSE;

    if (Using_BackBuffer)
    {
        // Set function pointers...
        Flip   = SwapPages;
        Clear  = ClearPage;
        Lock   = LockPage;
        Unlock = UnlockPage;

        // Create the primary surface with a back buffer.
        ZeroMemory(&ddsd, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);
        ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
        ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
        ddsd.dwBackBufferCount = 1;
        res = lpDD->CreateSurface(&ddsd, &lpDDSPrimary, NULL);
        if (res != DD_OK) return FALSE;

        ZeroMemory(&ddscaps, sizeof(ddscaps));
        ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
        res = lpDDSPrimary->GetAttachedSurface(&ddscaps, &lpDDSBack);
        if (res != DD_OK) return FALSE;

        // Set screen pointer...
        lpDDSDrawable = lpDDSBack;

        // Get pitch of surface...
        ZeroMemory(&ddsd, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);
        res = lpDDSBack->GetSurfaceDesc(&ddsd);
        if (res != DD_OK) return FALSE;

        pitch = ddsd.lPitch;
    }
    else
    {
        // Set function pointers...
        Flip   = BlitOffBuffer;
        Clear  = ClearOffBuffer;
        Lock   = LockBuffer;
        Unlock = UnlockBuffer;

        // Create a primary surface by itself.
        ZeroMemory(&ddsd, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);
        ddsd.dwFlags = DDSD_CAPS;
        ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
        res = lpDD->CreateSurface(&ddsd, &lpDDSPrimary, NULL);
        if (res != DD_OK) return FALSE;

        // Create an offscreen surface.
        ZeroMemory(&ddsd, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);
        ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
        ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
        ddsd.dwWidth = DD_Xsize;
        ddsd.dwHeight = DD_Ysize;
        res = lpDD->CreateSurface(&ddsd, &lpDDSOff, NULL);
        if (res != DD_OK) return FALSE;

        // Set screen pointer...
        lpDDSDrawable = lpDDSOff;

        // Get pitch of surface...
        ZeroMemory(&ddsd, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);
        res = lpDDSOff->GetSurfaceDesc(&ddsd);
        if (res != DD_OK) return FALSE;

        pitch = ddsd.lPitch;
    }

    // If in an 8bpp mode, create a palette.
    if (bpp == 8)
    {
        // Attach the palette to the primary surface.
        res = lpDD->CreatePalette(DDPCAPS_8BIT, temp_pal, &lpDDPal, NULL);
        if (res != DD_OK) return FALSE;

        res = lpDDSPrimary->SetPalette(lpDDPal);
        if (res != DD_OK) return FALSE;
    }

    // Finally, create row offsets (since we can't get a pointer until we lock the surface).
    DD_RowOffsets = new ulong[DD_Ysize];
    for (t = 0; t < DD_Ysize; t++)
        DD_RowOffsets[t] = pitch * t;

    DD_FullXsize = pitch;
    return TRUE;
}

void DeinitDirectDraw(void)
{
    if (lpDD != NULL)
    {
        lpDD->RestoreDisplayMode();
        lpDD->FlipToGDISurface();

        if (lpDDSPrimary != NULL)
        {
            lpDDSPrimary->Release();
            lpDDSPrimary = NULL;
        }

        if (lpDDSOff != NULL)
        {
            lpDDSOff->Release();
            lpDDSOff = NULL;
        }

        if (lpDDPal != NULL)
        {
            lpDDPal->Release();
            lpDDPal = NULL;
        }

        lpDD->Release();
        lpDD = NULL;
    }

    if (DD_RowOffsets != NULL)
    {
        delete []DD_RowOffsets;
        DD_RowOffsets = NULL;
    }

    if (WinInUse && (FrameBuffer != NULL))
    {
        delete []FrameBuffer;
        FrameBuffer = NULL;
        WinInUse = FALSE;

        free(bi);
    }
}

void ChangeResolution(uint xsize, uint ysize, uchar bpp)
{
    uint t;
    HRESULT res;
    DWORD dwStyle, pitch;
    DDSCAPS ddscaps;
    DDSURFACEDESC ddsd;
    PALETTEENTRY temp_pal[256];
    RECT rcWork, rc;

	// See if we can skip all this.
	if ((xsize == DD_Xsize) && (ysize == DD_Ysize) && (bpp == DD_Depth))
		return;

    // Set stuff up...
    DD_Xsize = xsize;
    DD_Ysize = ysize;
    DD_Depth = bpp;

    if (WinInUse)
    {
        // Get the DC of the window.
        hdc = GetDC(DD_hWnd);

        // Convert window to a normal looking thing...
        dwStyle  = GetWindowStyle(DD_hWnd);
        dwStyle &= ~WS_POPUP;
        dwStyle |= WS_OVERLAPPED | WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX;
        SetWindowLong(DD_hWnd, GWL_STYLE, dwStyle);
        SetRect(&rc, 0, 0, xsize, ysize);
        AdjustWindowRectEx(&rc, GetWindowStyle(DD_hWnd), GetMenu(DD_hWnd) != NULL, GetWindowExStyle(DD_hWnd));
        SetWindowPos(DD_hWnd, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
        SetWindowPos(DD_hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);

        // Make sure our window does not hang outside of the work area.  This will make people who have the tray on the 
        // top or left happy.
        SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
        GetWindowRect(DD_hWnd, &rc);

        if (rc.left < rcWork.left) rc.left = rcWork.left;
        if (rc.top  < rcWork.top)  rc.top  = rcWork.top;
        SetWindowPos(DD_hWnd, NULL, rc.left, rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);

        // Allocate a new frame buffer.
		delete []FrameBuffer;
		FrameBuffer = new uchar[DD_Xsize * DD_Ysize * (DD_Depth >> 3)];
        if (FrameBuffer == NULL) return;

        // Allocate new DIB stuff..
		free(bi);
        if (DD_Depth == 8)
        {
            bi = (BITMAPINFO *)malloc(sizeof(BITMAPINFOHEADER) + (256 * sizeof(RGBQUAD)));
        
            bi->bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
            bi->bmiHeader.biWidth         = DD_Xsize;
            bi->bmiHeader.biHeight        = -((LONG)DD_Ysize);
            bi->bmiHeader.biPlanes        = 1;
            bi->bmiHeader.biBitCount      = 8;
            bi->bmiHeader.biCompression   = BI_RGB;
            bi->bmiHeader.biSizeImage     = 0;
            bi->bmiHeader.biXPelsPerMeter = 0;
            bi->bmiHeader.biYPelsPerMeter = 0;
            bi->bmiHeader.biClrUsed       = 0;
            bi->bmiHeader.biClrImportant  = 0;
        }
        else
        {
            bi = (BITMAPINFO *)malloc(sizeof(BITMAPINFOHEADER));
        
            bi->bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
            bi->bmiHeader.biWidth         = DD_Xsize;
            bi->bmiHeader.biHeight        = -((LONG)DD_Ysize);
            bi->bmiHeader.biPlanes        = 1;
            bi->bmiHeader.biBitCount      = DD_Depth;
            bi->bmiHeader.biCompression   = BI_RGB;
            bi->bmiHeader.biSizeImage     = 0;
            bi->bmiHeader.biXPelsPerMeter = 0;
            bi->bmiHeader.biYPelsPerMeter = 0;
            bi->bmiHeader.biClrUsed       = 0;
            bi->bmiHeader.biClrImportant  = 0;
        }

        DD_FullXsize = DD_Xsize * (DD_Depth >> 3);

		// Create new row offsets.
		delete []DD_RowOffsets;
        DD_RowOffsets = new ulong[DD_Ysize];
        for (t = 0; t < DD_Ysize; t++)
            DD_RowOffsets[t] = (DD_Xsize * t * (DD_Depth >> 3));

        return;
    }

    dwStyle  = GetWindowStyle(DD_hWnd);
    dwStyle |= WS_POPUP;
    dwStyle &= ~(WS_OVERLAPPED | WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX);
    SetWindowLong(DD_hWnd, GWL_STYLE, dwStyle);
    SetRect(&rc, 0, 0, xsize, ysize);
    AdjustWindowRectEx(&rc, GetWindowStyle(DD_hWnd), GetMenu(DD_hWnd) != NULL, GetWindowExStyle(DD_hWnd));

    // Set new display mode.
    res = lpDD->SetDisplayMode(xsize, ysize, bpp);
    if (res != DD_OK) return;

    if (Using_BackBuffer)
    {
		// Zap old primary surface and back buffer.
        if (lpDDSPrimary != NULL) lpDDSPrimary->Release();

        // Create the primary surface with a back buffer.
        ZeroMemory(&ddsd, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);
        ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
        ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
        ddsd.dwBackBufferCount = 1;
        res = lpDD->CreateSurface(&ddsd, &lpDDSPrimary, NULL);
        if (res != DD_OK) return;

        ZeroMemory(&ddscaps, sizeof(ddscaps));
        ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
        res = lpDDSPrimary->GetAttachedSurface(&ddscaps, &lpDDSBack);
        if (res != DD_OK) return;

        // Set screen pointer...
        lpDDSDrawable = lpDDSBack;

        // Get pitch of surface...
        ZeroMemory(&ddsd, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);
        res = lpDDSBack->GetSurfaceDesc(&ddsd);
        if (res != DD_OK) return;

        pitch = ddsd.lPitch;
    }
    else
    {
		// Zap old surfaces.
        if (lpDDSPrimary != NULL) lpDDSPrimary->Release();
        if (lpDDSOff     != NULL) lpDDSOff->Release();

        // Create a new primary surface by itself.
        ZeroMemory(&ddsd, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);
        ddsd.dwFlags = DDSD_CAPS;
        ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
        res = lpDD->CreateSurface(&ddsd, &lpDDSPrimary, NULL);
        if (res != DD_OK) return;

        // Create a new offscreen surface.
        ZeroMemory(&ddsd, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);
        ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
        ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
        ddsd.dwWidth = DD_Xsize;
        ddsd.dwHeight = DD_Ysize;
        res = lpDD->CreateSurface(&ddsd, &lpDDSOff, NULL);
        if (res != DD_OK) return;

        // Set screen pointer...
        lpDDSDrawable = lpDDSOff;

        // Get pitch of surface...
        ZeroMemory(&ddsd, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);
        res = lpDDSOff->GetSurfaceDesc(&ddsd);
        if (res != DD_OK) return;

        pitch = ddsd.lPitch;
    }

    // If in an 8bpp mode, create a new palette.
    if (bpp == 8)
    {
		// Zap old palette.
		if (lpDDPal != NULL) lpDDPal->Release();

        // Attach the palette to the primary surface.
        res = lpDD->CreatePalette(DDPCAPS_8BIT, temp_pal, &lpDDPal, NULL);
        if (res != DD_OK) return;

        res = lpDDSPrimary->SetPalette(lpDDPal);
        if (res != DD_OK) return;
    }

    // Finally, create new row offsets.
	delete []DD_RowOffsets;
    DD_RowOffsets = new ulong[DD_Ysize];
    for (t = 0; t < DD_Ysize; t++)
        DD_RowOffsets[t] = pitch * t;

    DD_FullXsize = pitch;
}

void SwitchToWindow(void)
{
	int t;
    DWORD dwStyle;
    RECT rcWork, rc;

	// See if we can skip this.
	if (WinInUse) return;

    // Set stuff up...
	WinInUse = TRUE;

	// Free memory.
    if (lpDD != NULL)
    {
        lpDD->RestoreDisplayMode();
        lpDD->FlipToGDISurface();

        if (lpDDSPrimary != NULL)
        {
            lpDDSPrimary->Release();
            lpDDSPrimary = NULL;
        }

        if (lpDDSOff != NULL)
        {
            lpDDSOff->Release();
            lpDDSOff = NULL;
        }

        if (lpDDPal != NULL)
        {
            lpDDPal->Release();
            lpDDPal = NULL;
        }

		lpDD->SetCooperativeLevel(DD_hWnd, DDSCL_NORMAL);
    }

	delete []DD_RowOffsets;

    // Get the DC of the window.
    hdc = GetDC(DD_hWnd);

    // Convert window to a normal looking thing...
    dwStyle  = GetWindowStyle(DD_hWnd);
    dwStyle &= ~WS_POPUP;
    dwStyle |= WS_OVERLAPPED | WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX;
    SetWindowLong(DD_hWnd, GWL_STYLE, dwStyle);
    SetRect(&rc, 0, 0, DD_Xsize, DD_Ysize);
    AdjustWindowRectEx(&rc, GetWindowStyle(DD_hWnd), GetMenu(DD_hWnd) != NULL, GetWindowExStyle(DD_hWnd));
    SetWindowPos(DD_hWnd, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
    SetWindowPos(DD_hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);

    // Make sure our window does not hang outside of the work area.  This will make people who have the tray on the 
    // top or left happy.
    SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
    GetWindowRect(DD_hWnd, &rc);

    if (rc.left < rcWork.left) rc.left = rcWork.left;
    if (rc.top  < rcWork.top)  rc.top  = rcWork.top;
    SetWindowPos(DD_hWnd, NULL, rc.left, rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);

    // Set function pointers...
    Flip   = BlitToWindow;
    Clear  = ClearWindowBuffer;
    Lock   = LockWindowBuffer;
    Unlock = UnlockWindowBuffer;

    // Allocate the frame buffer.
    FrameBuffer = new uchar[DD_Xsize * DD_Ysize * (DD_Depth >> 3)];
    if (FrameBuffer == NULL) return;

    // Allocate DIB stuff..
    if (DD_Depth == 8)
    {
        bi = (BITMAPINFO *)malloc(sizeof(BITMAPINFOHEADER) + (256 * sizeof(RGBQUAD)));
    
        bi->bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
        bi->bmiHeader.biWidth         = DD_Xsize;
        bi->bmiHeader.biHeight        = -((LONG)DD_Ysize);
        bi->bmiHeader.biPlanes        = 1;
        bi->bmiHeader.biBitCount      = 8;
        bi->bmiHeader.biCompression   = BI_RGB;
        bi->bmiHeader.biSizeImage     = 0;
        bi->bmiHeader.biXPelsPerMeter = 0;
        bi->bmiHeader.biYPelsPerMeter = 0;
        bi->bmiHeader.biClrUsed       = 0;
        bi->bmiHeader.biClrImportant  = 0;
    }
    else
    {
        bi = (BITMAPINFO *)malloc(sizeof(BITMAPINFOHEADER));
    
        bi->bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
        bi->bmiHeader.biWidth         = DD_Xsize;
        bi->bmiHeader.biHeight        = -((LONG)DD_Ysize);
        bi->bmiHeader.biPlanes        = 1;
        bi->bmiHeader.biBitCount      = DD_Depth;
        bi->bmiHeader.biCompression   = BI_RGB;
        bi->bmiHeader.biSizeImage     = 0;
        bi->bmiHeader.biXPelsPerMeter = 0;
        bi->bmiHeader.biYPelsPerMeter = 0;
        bi->bmiHeader.biClrUsed       = 0;
        bi->bmiHeader.biClrImportant  = 0;
    }

    DD_FullXsize = DD_Xsize * (DD_Depth >> 3);

    DD_RowOffsets = new ulong[DD_Ysize];
    for (t = 0; t < DD_Ysize; t++)
        DD_RowOffsets[t] = (DD_Xsize * t * (DD_Depth >> 3));
}

void SwitchToFullscreen(void)
{
    uint t;
    HRESULT res;
    DWORD dwStyle, pitch;
    DDSCAPS ddscaps;
    DDSURFACEDESC ddsd;
    PALETTEENTRY temp_pal[256];
    RECT rc;

	// See if we can skip this.
	if (!WinInUse) return;

    // Set stuff up...
	WinInUse = FALSE;

	// Free memory.
	delete []FrameBuffer;
	delete []DD_RowOffsets;
	free(bi);

	// Fixup window.
    dwStyle  = GetWindowStyle(DD_hWnd);
    dwStyle |= WS_POPUP;
    dwStyle &= ~(WS_OVERLAPPED | WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX);
    SetWindowLong(DD_hWnd, GWL_STYLE, dwStyle);
    SetRect(&rc, 0, 0, DD_Xsize, DD_Ysize);
    AdjustWindowRectEx(&rc, GetWindowStyle(DD_hWnd), GetMenu(DD_hWnd) != NULL, GetWindowExStyle(DD_hWnd));

	// Go for fullscreen mode.
    res = lpDD->SetCooperativeLevel(DD_hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWREBOOT);
    if (res != DD_OK) return;

    res = lpDD->SetDisplayMode(DD_Xsize, DD_Ysize, DD_Depth);
    if (res != DD_OK) return;

    if (Using_BackBuffer)
    {
        // Set new function pointers...
        Flip   = SwapPages;
        Clear  = ClearPage;
        Lock   = LockPage;
        Unlock = UnlockPage;

        // Create the primary surface with a back buffer.
        ZeroMemory(&ddsd, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);
        ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
        ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
        ddsd.dwBackBufferCount = 1;
        res = lpDD->CreateSurface(&ddsd, &lpDDSPrimary, NULL);
        if (res != DD_OK) return;

        ZeroMemory(&ddscaps, sizeof(ddscaps));
        ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
        res = lpDDSPrimary->GetAttachedSurface(&ddscaps, &lpDDSBack);
        if (res != DD_OK) return;

        // Set screen pointer...
        lpDDSDrawable = lpDDSBack;

        // Get pitch of surface...
        ZeroMemory(&ddsd, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);
        res = lpDDSBack->GetSurfaceDesc(&ddsd);
        if (res != DD_OK) return;

        pitch = ddsd.lPitch;
    }
    else
    {
        // Set new function pointers...
        Flip   = BlitOffBuffer;
        Clear  = ClearOffBuffer;
        Lock   = LockBuffer;
        Unlock = UnlockBuffer;

        // Create a new primary surface by itself.
        ZeroMemory(&ddsd, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);
        ddsd.dwFlags = DDSD_CAPS;
        ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
        res = lpDD->CreateSurface(&ddsd, &lpDDSPrimary, NULL);
        if (res != DD_OK) return;

        // Create a new offscreen surface.
        ZeroMemory(&ddsd, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);
        ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
        ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
        ddsd.dwWidth = DD_Xsize;
        ddsd.dwHeight = DD_Ysize;
        res = lpDD->CreateSurface(&ddsd, &lpDDSOff, NULL);
        if (res != DD_OK) return;

        // Set screen pointer...
        lpDDSDrawable = lpDDSOff;

        // Get pitch of surface...
        ZeroMemory(&ddsd, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);
        res = lpDDSOff->GetSurfaceDesc(&ddsd);
        if (res != DD_OK) return;

        pitch = ddsd.lPitch;
    }

    // If in an 8bpp mode, create a palette.
    if (DD_Depth == 8)
    {
        // Attach the palette to the primary surface.
        res = lpDD->CreatePalette(DDPCAPS_8BIT, temp_pal, &lpDDPal, NULL);
        if (res != DD_OK) return;

        res = lpDDSPrimary->SetPalette(lpDDPal);
        if (res != DD_OK) return;
    }

    // Finally, create new row offsets.
    DD_RowOffsets = new ulong[DD_Ysize];
    for (t = 0; t < DD_Ysize; t++)
        DD_RowOffsets[t] = pitch * t;

    DD_FullXsize = pitch;
}

int CountBits(ulong val)
{
	int count, loop;

	count = 0;
	loop = 32;
	while (loop--)
	{
		if (val & 0x00000001) count++;
		val >>= 1;
	}
	return count;
}

ushort GetSurfaceRGB(void)
{
	DDSURFACEDESC ddsd;
	int r, g, b, res;

	if (WinInUse) return 0x0000;

    ZeroMemory(&ddsd, sizeof(ddsd));
    ddsd.dwSize = sizeof(ddsd);
    res = lpDDSPrimary->GetSurfaceDesc(&ddsd);
	if (res != DD_OK) return 0x0000;

	r = CountBits(ddsd.ddpfPixelFormat.dwRBitMask);
	g = CountBits(ddsd.ddpfPixelFormat.dwGBitMask);
	b = CountBits(ddsd.ddpfPixelFormat.dwBBitMask);

	return ((r << 8) | (g << 4) | b);
}

void SwapPages(void)
{
    HRESULT res;

    do
    {
        res = lpDDSPrimary->Flip(NULL, DDFLIP_WAIT);
        if (res == DDERR_SURFACELOST) return;
    } while (res == DDERR_WASSTILLDRAWING);
}

void BlitOffBuffer(void)
{
    RECT r = { 0, 0, DD_Xsize - 1, DD_Ysize - 1 };

    lpDDSPrimary->BltFast(0, 0, lpDDSOff, &r, DDBLTFAST_NOCOLORKEY | DDBLTFAST_WAIT);
}

void ClearPage(void)
{
    bool do_unlock;
    HRESULT res;
    DDSURFACEDESC ddsd;

    do_unlock = FALSE;

    if (DD_Surfptr == NULL)
    {
        do_unlock   = TRUE;
        ddsd.dwSize = sizeof(ddsd);

        do
        {
            res = lpDDSBack->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR, NULL);
            if (res == DDERR_SURFACELOST) return;
        } while (res == DDERR_WASSTILLDRAWING);

        DD_Surfptr = (uchar *)ddsd.lpSurface;
    }

    memset(DD_Surfptr, 0, DD_FullXsize * DD_Ysize);

    if (do_unlock)
    {
        lpDDSBack->Unlock(NULL);
        DD_Surfptr = NULL;
    }
}

void ClearOffBuffer(void)
{
    bool do_unlock;
    HRESULT res;
    DDSURFACEDESC ddsd;

    do_unlock = FALSE;

    if (DD_Surfptr == NULL)
    {
        do_unlock   = TRUE;
        ddsd.dwSize = sizeof(ddsd);

        do
        {
            res = lpDDSOff->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR, NULL);
            if (res == DDERR_SURFACELOST) return;
        } while (res == DDERR_WASSTILLDRAWING);

        DD_Surfptr = (uchar *)ddsd.lpSurface;
    }

    memset(DD_Surfptr, 0, DD_FullXsize * DD_Ysize);

    if (do_unlock)
    {
        lpDDSOff->Unlock(NULL);
        DD_Surfptr = NULL;
    }
}

uchar *LockPage(void)
{
    HRESULT res;
    DDSURFACEDESC ddsd;

    ddsd.dwSize = sizeof(ddsd);

    do
    {
        res = lpDDSBack->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR, NULL);
        if (res == DDERR_SURFACELOST) return NULL;
    } while (res == DDERR_WASSTILLDRAWING);

    DD_Surfptr = (uchar *)ddsd.lpSurface;
    return DD_Surfptr;
}

void UnlockPage(void)
{
    lpDDSBack->Unlock(NULL);
    DD_Surfptr = NULL;
}

uchar *LockBuffer(void)
{
    HRESULT res;
    DDSURFACEDESC ddsd;

    ddsd.dwSize = sizeof(ddsd);

    do
    {
        res = lpDDSOff->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR, NULL);
        if (res == DDERR_SURFACELOST) return NULL;
    } while (res == DDERR_WASSTILLDRAWING);

    DD_Surfptr = (uchar *)ddsd.lpSurface;
    return DD_Surfptr;
}

void UnlockBuffer(void)
{
    lpDDSOff->Unlock(NULL);
    DD_Surfptr = NULL;
}

void BlitToWindow(void)
{
    StretchDIBits(hdc, 0, 0, win_Xsize + 1, win_Ysize + 1, 0, 0, DD_Xsize, DD_Ysize, FrameBuffer, bi, DIB_RGB_COLORS, SRCCOPY);
}

void ClearWindowBuffer(void)
{
    memset(FrameBuffer, 0, DD_Xsize * DD_Ysize * (DD_Depth >> 3));
}

uchar *LockWindowBuffer(void)
{
    DD_Surfptr = FrameBuffer;
    return DD_Surfptr;
}

void UnlockWindowBuffer(void)
{
    DD_Surfptr = NULL;
}

void BlitBuf(uint xpos, uint ypos, blitbuf *buf)
{
    int t;
    uchar *sptr, *dptr;
    
    sptr = buf->image;
    dptr = FrameBuffer + (DD_RowOffsets[ypos] + xpos);
    
    for (t = 0; t < buf->ysize; t++)
    {
        memcpy(dptr, sptr, buf->xsize);

        sptr += buf->xsize;
        dptr += DD_Xsize;
    }
}

void UnpackPCX(blitbuf *buf, int xsize, int ysize, uchar *image)
{
    int size;
    uchar PCX_byte, RLE_byte;
    uchar *buf_ptr, *end_of_buf, *rptr;

    buf->xsize = xsize;
    buf->ysize = ysize;

    size = (buf->xsize * buf->ysize);

    buf->image = new uchar[size];
    buf_ptr = buf->image;
    end_of_buf = buf_ptr + size;

    rptr = image;

    while (buf_ptr < end_of_buf)
    {
        // Read next packet
        PCX_byte = *rptr++;

        if (PCX_byte < 192)
            *buf_ptr++ = PCX_byte;                  // Raw Pixel
        else
        {
            // RLE Pixels
            PCX_byte = PCX_byte & 0x3F;
            RLE_byte = *rptr++;
            memset(buf_ptr, RLE_byte, PCX_byte);
            buf_ptr += PCX_byte;
        }
    }
}

void SaveFrame(char *fname)
{
    FILE *fp;
    ushort temp_int;
    uint i, yrun;
    uchar *buf_ptr;
    uchar temp_char, match, count;

    // If the surface is in use, don't try to access it (partly-rendered frame)...
    if (DD_Surfptr != NULL) return;

    fp = fopen(fname, "wb");
    if (fp == NULL) return;

    Lock();

    // Write manufacturer's byte
    temp_char = 10;
    fwrite(&temp_char, 1, 1, fp);

    // Write version of PCX.  5 = 256 color (PCX Version 5.0)
    temp_char = 5;
    fwrite(&temp_char, 1, 1, fp);

    // Write encoding type, always 1 for RLE.
    temp_char = 1;
    fwrite(&temp_char, 1, 1, fp);

    // Write bits_per_pixel = 8.
    temp_char = 8;
    fwrite(&temp_char, 1, 1, fp);

    // Write starting X and Y coords
    temp_int = 0;
    fwrite(&temp_int, 2, 1, fp);
    fwrite(&temp_int, 2, 1, fp);

    // Write X size
    temp_int = (DD_Xsize - 1);
    fwrite(&temp_int, 2, 1, fp);

    // Write Y size
    temp_int = (DD_Ysize - 1);
    fwrite(&temp_int, 2, 1, fp);

    // Do HRES and VRES **
    temp_int = DD_Xsize;
    fwrite(&temp_int, 2, 1, fp);
    temp_int = DD_Ysize;
    fwrite(&temp_int, 2, 1, fp);

    // Write 16 color palette, not used.
    temp_int = 0;
    i = 24;
    while (i--)
        fwrite(&temp_int, 2, 1, fp);

    // Write vmode byte.
    temp_char = 0;
    fwrite(&temp_char, 1, 1, fp);

    // Write bit_planes
    temp_char = 1;
    fwrite(&temp_char, 1, 1, fp);

    // Write bytes_per_line
    temp_int = DD_Xsize;
    fwrite(&temp_int, 2, 1, fp);

    // Write palette type
    temp_int = 1;
    fwrite(&temp_int, 2, 1, fp);

    // Write junk filler
    temp_int = 0;
    i = 29;
    while (i--)
        fwrite(&temp_int, 2, 1, fp);

    // Write the actual image
    for (yrun = 0; yrun < DD_Ysize; yrun++)
    {
        buf_ptr = DD_Surfptr + DD_RowOffsets[yrun];
        count   = 0;
        match   = *buf_ptr;

        i = DD_Xsize;

        while (i--)
        {
            temp_char = *buf_ptr++;

            if ((temp_char == match) && (count < 63))
                count++;
            else
            {
                if ((count == 1) && (match < 192))
                    fwrite(&match, 1, 1, fp);            // Write single byte
                else
                {
                    // Write run of pixels
                    if (count != 0)
                    {
                        count += 192;
                        fwrite(&count, 1, 1, fp);
                        fwrite(&match, 1, 1, fp);
                    }
                }

                count = 1;
                match = temp_char;
            }
        }

        if ((count == 1) && (match < 192))
            fwrite(&match, 1, 1, fp);                    // Write single byte
        else
        {
            // Write run of pixels
            if (count != 0)
            {
                count += 192;
                fwrite(&count, 1, 1, fp);
                fwrite(&match, 1, 1, fp);
            }
        }
    }

    // Write palette verification byte
    temp_char = 12;
    fwrite(&temp_char, 1, 1, fp);

    // Write 256 color palette
    fwrite(vga_pal, 1, 768, fp);
    fclose(fp);

    Unlock();
    return;
}

//
// ------------------------------------------ Palette functions --------------------------------------------
//

void LoadPal(uchar *pal)
{
    int i;
    PALETTEENTRY pe[256];

    memcpy(vga_pal, pal, 768);

    if (WinInUse && (DD_Depth == 8))
    {
        for (i = 0; i < 256; i++)
        {
            bi->bmiColors[i].rgbRed      = *pal++;
            bi->bmiColors[i].rgbGreen    = *pal++;
            bi->bmiColors[i].rgbBlue     = *pal++;
            bi->bmiColors[i].rgbReserved = 0;
        }

        return;
    }

    if (lpDDPal == NULL) return;

    for (i = 0; i < 256; i++)
    {
        pe[i].peRed   = *pal++;
        pe[i].peGreen = *pal++;
        pe[i].peBlue  = *pal++;
    }

    lpDDPal->SetEntries(0, 0, 256, pe);
}

void CreateShadePal(void)
{
    int i;
    PALETTEENTRY pe[256];

    if (WinInUse && (DD_Depth == 8))
    {
        for (i = 0; i < 256; i++)
        {
            bi->bmiColors[i].rgbRed      = i;
            bi->bmiColors[i].rgbGreen    = i;
            bi->bmiColors[i].rgbBlue     = i;
            bi->bmiColors[i].rgbReserved = 0;
        }

        return;
    }

    if (lpDDPal == NULL) return;

    for (i = 0; i < 256; i++)
    {
        pe[i].peRed   = i;
        pe[i].peGreen = i;
        pe[i].peBlue  = i;
    }

    lpDDPal->SetEntries(0, 0, 256, pe);
}

//
// ------------------------------------- Primitive drawing functions --------------------------------------------
//

void PutPixel(uint x, uint y, uchar color)
{
    *(DD_Surfptr + DD_RowOffsets[y] + x) = color;
}

void Line(uint x1, uint y1, uint x2, uint y2, uchar color)
{
    int d, x, y, dx, dy, xinc1, xinc2, yinc1, yinc2, inc1, inc2, limit;

    dx = ABS((sint)x2 - (sint)x1);
    dy = ABS((sint)y2 - (sint)y1);
    x = x1;
    y = y1;

    xinc2 = 1;
    yinc2 = 1;

    if (dx >= dy)
    {
        // X major line
        inc1 = dy << 1;
        inc2 = (dy - dx) << 1;

        d = inc1 - dx;

        xinc1 = 1;
        yinc1 = 0;
        limit = dx;
    }
    else
    {
        // Y major line
        inc1 = dx << 1;
        inc2 = (dx - dy) << 1;

        d = inc1 - dy;

        xinc1 = 0;
        yinc1 = 1;
        limit = dy;
    }

    if (x1 > x2)
    {
        xinc1 = -xinc1;
        xinc2 = -1;
    }

    if (y1 > y2)
    {
        yinc1 = -yinc1;
        yinc2 = -1;
    }

    *(DD_Surfptr + DD_RowOffsets[y] + x) = color;

    while (limit--)
    {
        if (d < 0)
        {
            d += inc1;
            x += xinc1;
            y += yinc1;
        }
        else
        {
            d += inc2;
            x += xinc2;
            y += yinc2;
        }

        *(DD_Surfptr + DD_RowOffsets[y] + x) = color;
    }
}

//
// ------------------------------------- Colordepth conversion functions --------------------------------------------
//

void Convert5551to555_Dswap(void *sptr, void *dptr, ulong count)
{
	__asm {
		mov  esi, sptr
		mov  edi, dptr
		mov  ecx, count
		shr  ecx, 3

	Cnv5551to555:
		mov  ax, WORD PTR [esi + 2]
		mov  bx, WORD PTR [esi]
		shr  ax, 1
		shr  bx, 1
		mov  WORD PTR [edi], ax
		mov  WORD PTR [edi + 2], bx
		add  esi, 4
		add  edi, 4

		mov  ax, WORD PTR [esi + 2]
		mov  bx, WORD PTR [esi]
		shr  ax, 1
		shr  bx, 1
		mov  WORD PTR [edi], ax
		mov  WORD PTR [edi + 2], bx
		add  esi, 4
		add  edi, 4

		mov  ax, WORD PTR [esi + 2]
		mov  bx, WORD PTR [esi]
		shr  ax, 1
		shr  bx, 1
		mov  WORD PTR [edi], ax
		mov  WORD PTR [edi + 2], bx
		add  esi, 4
		add  edi, 4

		mov  ax, WORD PTR [esi + 2]
		mov  bx, WORD PTR [esi]
		shr  ax, 1
		shr  bx, 1
		mov  WORD PTR [edi], ax
		mov  WORD PTR [edi + 2], bx
		add  esi, 4
		add  edi, 4

		dec  ecx
		jnz  Cnv5551to555
	};
}

void Convert5551to565_Dswap(void *sptr, void *dptr, ulong count)
{
	__asm {
		mov  esi, sptr
		mov  edi, dptr
		mov  ecx, count
		shr  ecx, 3

	Cnv5551to565:
		mov  ax, WORD PTR [esi + 2]
		mov  dx, ax
		shr  ax, 1
		and  ax, 0000000000011111b
		and  dx, 1111111111000000b
		or   ax, dx
		mov  bx, WORD PTR [esi]
		mov  dx, bx
		shr  bx, 1
		and  bx, 0000000000011111b
		and  dx, 1111111111000000b
		or   bx, dx		
		mov  WORD PTR [edi], ax
		mov  WORD PTR [edi + 2], bx
		add  esi, 4
		add  edi, 4

		mov  ax, WORD PTR [esi + 2]
		mov  dx, ax
		shr  ax, 1
		and  ax, 0000000000011111b
		and  dx, 1111111111000000b
		or   ax, dx
		mov  bx, WORD PTR [esi]
		mov  dx, bx
		shr  bx, 1
		and  bx, 0000000000011111b
		and  dx, 1111111111000000b
		or   bx, dx		
		mov  WORD PTR [edi], ax
		mov  WORD PTR [edi + 2], bx
		add  esi, 4
		add  edi, 4

		mov  ax, WORD PTR [esi + 2]
		mov  dx, ax
		shr  ax, 1
		and  ax, 0000000000011111b
		and  dx, 1111111111000000b
		or   ax, dx
		mov  bx, WORD PTR [esi]
		mov  dx, bx
		shr  bx, 1
		and  bx, 0000000000011111b
		and  dx, 1111111111000000b
		or   bx, dx		
		mov  WORD PTR [edi], ax
		mov  WORD PTR [edi + 2], bx
		add  esi, 4
		add  edi, 4

		mov  ax, WORD PTR [esi + 2]
		mov  dx, ax
		shr  ax, 1
		and  ax, 0000000000011111b
		and  dx, 1111111111000000b
		or   ax, dx
		mov  bx, WORD PTR [esi]
		mov  dx, bx
		shr  bx, 1
		and  bx, 0000000000011111b
		and  dx, 1111111111000000b
		or   bx, dx		
		mov  WORD PTR [edi], ax
		mov  WORD PTR [edi + 2], bx
		add  esi, 4
		add  edi, 4

		dec  ecx
		jnz  Cnv5551to565
	};
}

void Convert8888to8888_Dswap(void *sptr, void *dptr, ulong count)
{
}

void Convert5551to555_Dswap_DoubleUp(void *sptr, void *dptr, ulong count)
{
	__asm {
		push ebp

		xor  eax, eax
		mov  esi, sptr
		mov  edi, dptr

		mov  ebp, 240

	Cnv5551to555_Double_Yloop:
		mov  ecx, 80

	Cnv5551to555_Double:
		mov  ax, WORD PTR [esi + 2]
		mov  bx, WORD PTR [esi]
		shr  ax, 1
		shr  bx, 1
		mov  WORD PTR [edi], ax
		mov  WORD PTR [edi + 2], ax
		mov  WORD PTR [edi + 4], bx
		mov  WORD PTR [edi + 6], bx
		add  esi, 4
		add  edi, 4

		mov  ax, WORD PTR [esi + 2]
		mov  bx, WORD PTR [esi]
		shr  ax, 1
		shr  bx, 1
		mov  WORD PTR [edi], ax
		mov  WORD PTR [edi + 2], ax
		mov  WORD PTR [edi + 4], bx
		mov  WORD PTR [edi + 6], bx
		add  esi, 4
		add  edi, 4

		mov  ax, WORD PTR [esi + 2]
		mov  bx, WORD PTR [esi]
		shr  ax, 1
		shr  bx, 1
		mov  WORD PTR [edi], ax
		mov  WORD PTR [edi + 2], ax
		mov  WORD PTR [edi + 4], bx
		mov  WORD PTR [edi + 6], bx
		add  esi, 4
		add  edi, 4

		mov  ax, WORD PTR [esi + 2]
		mov  bx, WORD PTR [esi]
		shr  ax, 1
		shr  bx, 1
		mov  WORD PTR [edi], ax
		mov  WORD PTR [edi + 2], ax
		mov  WORD PTR [edi + 4], bx
		mov  WORD PTR [edi + 6], bx
		add  esi, 4
		add  edi, 4

		dec  ecx
		jnz  Cnv5551to555_Double

		xor  eax, 80000000h
		sub  esi, 640

		cmp  eax, 80000000h
		jae  Cnv5551to555_Double_Yloop

		dec  ebp
		jnz  Cnv5551to555_Double_Yloop

		pop  ebp
	};
}

void Convert5551to565_Dswap_DoubleUp(void *sptr, void *dptr, ulong count)
{
	__asm {
		push ebp

		xor  eax, eax
		mov  esi, sptr
		mov  edi, dptr

		mov  ebp, 240

	Cnv5551to565_Double_Yloop:
		mov  ecx, 80

	Cnv5551to565_Double:
		mov  ax, WORD PTR [esi + 2]
		mov  dx, ax
		shr  ax, 1
		and  ax, 0000000000011111b
		and  dx, 1111111111000000b
		or   ax, dx
		mov  bx, WORD PTR [esi]
		mov  dx, bx
		shr  bx, 1
		and  bx, 0000000000011111b
		and  dx, 1111111111000000b
		or   bx, dx		
		mov  WORD PTR [edi], ax
		mov  WORD PTR [edi + 2], ax
		mov  WORD PTR [edi + 4], bx
		mov  WORD PTR [edi + 6], bx
		add  esi, 4
		add  edi, 8

		mov  ax, WORD PTR [esi + 2]
		mov  dx, ax
		shr  ax, 1
		and  ax, 0000000000011111b
		and  dx, 1111111111000000b
		or   ax, dx
		mov  bx, WORD PTR [esi]
		mov  dx, bx
		shr  bx, 1
		and  bx, 0000000000011111b
		and  dx, 1111111111000000b
		or   bx, dx		
		mov  WORD PTR [edi], ax
		mov  WORD PTR [edi + 2], ax
		mov  WORD PTR [edi + 4], bx
		mov  WORD PTR [edi + 6], bx
		add  esi, 4
		add  edi, 8

		mov  ax, WORD PTR [esi + 2]
		mov  dx, ax
		shr  ax, 1
		and  ax, 0000000000011111b
		and  dx, 1111111111000000b
		or   ax, dx
		mov  bx, WORD PTR [esi]
		mov  dx, bx
		shr  bx, 1
		and  bx, 0000000000011111b
		and  dx, 1111111111000000b
		or   bx, dx		
		mov  WORD PTR [edi], ax
		mov  WORD PTR [edi + 2], ax
		mov  WORD PTR [edi + 4], bx
		mov  WORD PTR [edi + 6], bx
		add  esi, 4
		add  edi, 8

		mov  ax, WORD PTR [esi + 2]
		mov  dx, ax
		shr  ax, 1
		and  ax, 0000000000011111b
		and  dx, 1111111111000000b
		or   ax, dx
		mov  bx, WORD PTR [esi]
		mov  dx, bx
		shr  bx, 1
		and  bx, 0000000000011111b
		and  dx, 1111111111000000b
		or   bx, dx		
		mov  WORD PTR [edi], ax
		mov  WORD PTR [edi + 2], ax
		mov  WORD PTR [edi + 4], bx
		mov  WORD PTR [edi + 6], bx
		add  esi, 4
		add  edi, 8

		dec  ecx
		jnz  Cnv5551to565_Double

		xor  eax, 80000000h
		sub  esi, 640

		cmp  eax, 80000000h
		jae  Cnv5551to565_Double_Yloop

		dec  ebp
		jnz  Cnv5551to565_Double_Yloop

		pop  ebp
	};
}

void Convert8888to8888_Dswap_DoubleUp(void *sptr, void *dptr, ulong count)
{
}
