/*==========================================================================
 *
 *  Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved.
 *  Copyright (C) 1994-1995 ATI Technologies Inc. All Rights Reserved.
 *
 *  File:	ddraw.c
 *  Content:	Misc. Direct Draw access routines
 *
 ***************************************************************************/
#include "foxbear.h"

typedef struct {
    GUID *guid;
    GUID DriverGUID;
    CHAR DriverDesc[128];
    CHAR DriverName[128];
} Devices;

Devices aDDDevs[15];
int     MaxDevIndex = 0;
int     DevIndex = 0;

BOOL    bUseEmulation;
BOOL    bUseSysMem;
int     nBufferCount;
int     CmdLineBufferCount;
BOOL	bTransDest;
BOOL    bColorFill;

HRESULT CALLBACK EnumDisplayModesCallback(LPDDSURFACEDESC pddsd, LPVOID Context);

HRESULT CALLBACK DDEnumCallback(GUID * lpGUID, LPSTR DRiverDesc, LPSTR DriverName, LPVOID lpContext);

/*
 * DDInit
 */
BOOL DDInit( void )
{
    DirectDrawEnumerate(&DDEnumCallback,NULL);
    DDEnumCallback((GUID *)DDCREATE_EMULATIONONLY, "Hardware Emulation Layer", "", NULL);
    return TRUE;
}
/*
 * DDEnable
 */
BOOL DDEnable( void )
{
    LPDIRECTDRAW	lpdd;
    LPDIRECTDRAW2   lpdd2;
    DDCAPS              ddcaps;
    HRESULT		ddrval;
    BOOL		use_dest;
	OSVERSIONINFO osv;

	osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
	//
	//What are we running on ?
	//
	GetVersionEx(&osv);


    nBufferCount = GetProfileInt( "FoxBear", "buffers", CmdLineBufferCount);
    bUseEmulation = GetProfileInt( "FoxBear", "use_emulation", bUseEmulation);
    bUseSysMem = GetProfileInt( "FoxBear", "sysmem", 0);
    use_dest = GetProfileInt( "FoxBear", "use_dest", 0 );

    if ((lpDD == NULL) || (lpDD2 == NULL))
    {
        if( bUseEmulation)
            DevIndex = MaxDevIndex-1;
        ddrval = DirectDrawCreate(aDDDevs[DevIndex].guid,&lpdd,NULL);
        if(ddrval == DD_OK)
        {
            ddrval = IDirectDraw_QueryInterface(lpdd, &IID_IDirectDraw2, &lpdd2);
        }
    }
    else
    {
        lpdd = lpDD;
        lpdd2 = lpDD2;
        ddrval = DD_OK;
    }

    if( ddrval != DD_OK )
    {
        Msg("DirectDrawCreate failed err=%d", ddrval);
        goto error;
    }

    /*
     * grab exclusive mode if we are going to run as fullscreen
     * otherwise grab normal mode.
     */
    if (lpDD == NULL)
    {
	NumModes = 0;

        if (bFullscreen)
        {
            ddrval = IDirectDraw_SetCooperativeLevel( lpdd, hWndMain,
                            DDSCL_ALLOWMODEX |
                            DDSCL_EXCLUSIVE |
						    DDSCL_FULLSCREEN );
		
            ddrval = IDirectDraw_EnumDisplayModes(lpdd, DDEDM_STANDARDVGAMODES, NULL, 0, EnumDisplayModesCallback);
            if(ddrval != DD_OK)
            {
			    ddrval = IDirectDraw_EnumDisplayModes(lpdd, 0, NULL, 0, EnumDisplayModesCallback);
            };
		}


        else
        {
            ddrval = IDirectDraw_SetCooperativeLevel( lpdd, hWndMain,
			    DDSCL_NORMAL );

	    // in normal windowed mode, just add some "stock" window
	    // sizes

	    ModeList[NumModes].w = 320;
	    ModeList[NumModes].h = 200;
	    NumModes++;

	    ModeList[NumModes].w = 320;
	    ModeList[NumModes].h = 240;
	    NumModes++;

	    ModeList[NumModes].w = 512;
	    ModeList[NumModes].h = 384;
	    NumModes++;

	    ModeList[NumModes].w = 640;
	    ModeList[NumModes].h = 400;
	    NumModes++;

	    ModeList[NumModes].w = 640;
	    ModeList[NumModes].h = 480;
	    NumModes++;
        }

        if( ddrval != DD_OK )
        {
            Msg("SetCooperativeLevel failed err=%d", ddrval);
            goto error;
        }
    }

    if (bFullscreen)
    {
        Msg("SetDisplayMode %d %d %d",GameMode.cx,GameMode.cy, GameBPP);
        if((GameMode.cx == 320) && (GameMode.cy == 200) && (GameBPP == 8))
        {
            ddrval = IDirectDraw2_SetDisplayMode(lpdd2, GameMode.cx, GameMode.cy, GameBPP,0,DDSDM_STANDARDVGAMODE);
        } else {
            ddrval = IDirectDraw_SetDisplayMode( lpdd, GameMode.cx, GameMode.cy, GameBPP);
        }

        if (ddrval != DD_OK && (GameMode.cx != 640 || GameMode.cy != 480))
        {
            Msg( "cant set mode trying 640x480" );

            GameMode.cx = 640;
            GameMode.cy = 480;
	    GameSize = GameMode;

            ddrval = IDirectDraw_SetDisplayMode( lpdd,
                GameMode.cx, GameMode.cy, GameBPP);
        }

        if (ddrval != DD_OK && GameBPP != 8)
        {
            Msg( "cant set mode trying 640x480x8" );

            GameBPP = 8;

            ddrval = IDirectDraw_SetDisplayMode( lpdd,
                GameMode.cx, GameMode.cy, GameBPP);
        }

        if (ddrval != DD_OK && GameBPP != 16)
        {
            Msg( "cant set mode trying 640x480x16" );

            GameBPP = 16;

            ddrval = IDirectDraw_SetDisplayMode( lpdd,
                GameMode.cx, GameMode.cy, GameBPP);
        }

        if( ddrval != DD_OK )
        {
            Msg("SetMode failed err=%d", ddrval);
            goto error;
        }
    }
    else
    {
        RECT rcWork;
        RECT rc;
        HDC hdc;
        DWORD dwStyle;

        //
        //  when in rome (I mean when in windows) we should use the
        //  current mode
        //
        hdc = GetDC(NULL);
        GameBPP = GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL);
        ReleaseDC(NULL, hdc);

        //
        // if we are still a WS_POPUP window we should convert to a
        // normal app window so we look like a windows app.
        //
        dwStyle = GetWindowStyle(hWndMain);
        dwStyle &= ~WS_POPUP;
        dwStyle |= WS_OVERLAPPED | WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX;
        SetWindowLong(hWndMain, GWL_STYLE, dwStyle);

        if (bStretch)
            SetRect(&rc, 0, 0, GameMode.cx*2, GameMode.cy*2);
        else
            SetRect(&rc, 0, 0, GameMode.cx, GameMode.cy);

        AdjustWindowRectEx(&rc,
            GetWindowStyle(hWndMain),
            GetMenu(hWndMain) != NULL,
            GetWindowExStyle(hWndMain));

        SetWindowPos(hWndMain, NULL, 0, 0, rc.right-rc.left, rc.bottom-rc.top,
            SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);

        SetWindowPos(hWndMain, 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(hWndMain, &rc);
        if (rc.left < rcWork.left) rc.left = rcWork.left;
        if (rc.top  < rcWork.top)  rc.top  = rcWork.top;
        SetWindowPos(hWndMain, NULL, rc.left, rc.top, 0, 0,
            SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
    }

    /*
     * check capabilites
     */
    ddcaps.dwSize = sizeof( ddcaps );
    ddrval = IDirectDraw_GetCaps( lpdd, &ddcaps, NULL );

    if( ddrval != DD_OK )
    {
        Msg("GetCaps failed err=%d", ddrval);
        goto error;
    }

    if( ddcaps.dwCaps & DDCAPS_NOHARDWARE )
    {
        Msg( "No hardware support at all" );
    }

    if( ddcaps.dwCaps & DDCAPS_BLTCOLORFILL )
    {
        bColorFill = TRUE;
        Msg( "Device supports color fill" );
    }
    else
    {
        bColorFill = FALSE;
        Msg( "Device does not support color fill" );
    }

    /*
     * default to double buffered on 1mb, triple buffered
     * on > 1mb
     */
    if (nBufferCount == 0)
    {
        if( ddcaps.dwVidMemTotal <= 1024L*1024L*(GameBPP/8) ||
            GameMode.cx > 640 )
        {
            Msg("double buffering (not enough memory)");
            nBufferCount = 2;
        }
        else
        {
            Msg("triple buffering");
            nBufferCount = 3;
        }
    }

    if( ddcaps.dwCaps & DDCAPS_COLORKEY )
    {
        if( ddcaps.dwCKeyCaps & DDCKEYCAPS_SRCBLT )
        {
            Msg( "Can do Src colorkey in hardware" );
        }

        if( ddcaps.dwCKeyCaps & DDCKEYCAPS_DESTBLT )
        {
            Msg( "Can do Dest colorkey in hardware" );
            if( use_dest || !(ddcaps.dwCKeyCaps & DDCKEYCAPS_SRCBLT) )
            {
                /*
                 * since direct draw doesn't support
                 * destination color key in emulation, only
                 * use it if there is enough vram ...
                 */
                if( ddcaps.dwVidMemTotal >= 2 * 1024L*1024L*(GameBPP/8) )
                {
                    Msg( "Using destination color key" );
                    bTransDest = TRUE;
                }
            }
        }
    }
    else
    {
        Msg( "Can't do color key in hardware!" );
    }

    lpDD = lpdd;
    lpDD2 = lpdd2;
    return TRUE;

error:
    return FALSE;

} /* DDEnable */

/*
 * DDDisable
 */
BOOL DDDisable( BOOL fFinal )
{
    if( lpClipper )
    {
        IDirectDrawClipper_Release(lpClipper);
        lpClipper = NULL;
    }

    if( lpBackBuffer )
    {
        IDirectDrawSurface_Release(lpBackBuffer);
        lpBackBuffer = NULL;
    }

    if( lpFrontBuffer )
    {
        IDirectDrawSurface_Release(lpFrontBuffer);
        lpFrontBuffer = NULL;
    }

    if( lpStretchBuffer )
    {
        IDirectDrawSurface_Release(lpStretchBuffer);
        lpStretchBuffer = NULL;
    }

    //
    // fFinal is TRUE when the app is exiting, FALSE if we are
    // just seting a new game size..
    //
    if ( fFinal )
    {
        if( lpDD != NULL )
        {
            IDirectDraw_Release( lpDD );
            IDirectDraw_Release( lpDD2 );
            lpDD = NULL;
        }
    }

    return TRUE;
}

/*
 * DDClear
 *
 * clear the front buffer and all backbuffers.
 */
BOOL DDClear( void )
{
    DDBLTFX     ddbltfx;
    int		i;
    HRESULT     ddrval;

    UpdateWindow(hWndMain);

    ddbltfx.dwSize = sizeof( ddbltfx );
    ddbltfx.dwFillColor = DDColorMatch(lpBackBuffer, RGB(0, 0, 200));

    if (bFullscreen)
    {
        /*
         * do it for all buffers, we either have 1 or 2 back buffers
         * make sure we get them all, 4 is plenty!
         */
        for( i=0; i<4; i++ )
        {
            ddrval = IDirectDrawSurface_Blt(
                            lpBackBuffer,           // dest surface
                            NULL,                   // dest rect
                            NULL,                   // src surface
                            NULL,                   // src rect
                            DDBLT_COLORFILL | DDBLT_WAIT,
                            &ddbltfx);

            if( ddrval != DD_OK )
            {
                Msg("Fill failed ddrval =0x%08lX", ddrval);
                return FALSE;
            }

            ddrval = IDirectDrawSurface_Flip(lpFrontBuffer, NULL, DDFLIP_WAIT);

            if( ddrval != DD_OK )
            {
                Msg("Flip failed ddrval =0x%08lX", ddrval );
                return FALSE;
            }
        }
    }
    else
    {
        ddrval = IDirectDrawSurface_Blt(
                        lpFrontBuffer,          // dest surface
                        &rcWindow,              // dest rect
                        NULL,                   // src surface
                        NULL,                   // src rect
                        DDBLT_COLORFILL | DDBLT_WAIT,
                        &ddbltfx);

        if( ddrval != DD_OK )
        {
            Msg("Fill failed ddrval =0x%08lX", ddrval);
            return FALSE;
        }
    }

    return TRUE;

} /* DDClear */

/*
 * DDCreateFlippingSurface
 *
 * create a FrontBuffer and a BackBuffer(s)
 *
 */
BOOL DDCreateFlippingSurface( void )
{
    DDPIXELFORMAT       ddpf;
    DDSURFACEDESC	ddsd;
    HRESULT		ddrval;
    DDSCAPS		ddscaps;
    DDCAPS              ddcaps;

    ddcaps.dwSize = sizeof( ddcaps );

    if( IDirectDraw_GetCaps( lpDD, &ddcaps, NULL ) != DD_OK )
        return FALSE;

    /*
     * fill in surface desc:
     * want a primary surface with 2 back buffers
     */
    ZeroMemory( &ddsd, sizeof( ddsd ) );
    ddsd.dwSize = sizeof( ddsd );

    if (bFullscreen && nBufferCount > 1)
    {
        //
        //  fullscreen case, create a primary (ie front) and
        //  either 1  or 2 back buffers
        //
        ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
        ddsd.dwBackBufferCount = nBufferCount-1;
        ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
                DDSCAPS_FLIP | DDSCAPS_COMPLEX;

        OutputDebugString("Creating multiple backbuffer primary\n\r");
        ddrval = IDirectDraw_CreateSurface( lpDD, &ddsd, &lpFrontBuffer, NULL );

        if( ddrval != DD_OK )
        {
            Msg( "CreateSurface FAILED! %08lx", ddrval );
            return FALSE;
        }

        /*
         * go find the back buffer
         */
        ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
        ddrval = IDirectDrawSurface_GetAttachedSurface(
                    lpFrontBuffer,
                    &ddscaps,
                    &lpBackBuffer );

        if( ddrval != DD_OK )
        {
            Msg( "GetAttachedSurface failed! err=%d",ddrval );
            return FALSE;
        }

        /*
         *  if we are stretching create a buffer to stretch into
         *
         *  NOTE we always make this buffer in system memory because
         *  we render to the backbuffer (in VRAM) at half the size
         *  now we need to stretch into the backbuffer.  we could just
         *  do a VRAM->VRAM stretch, but this is REAL REAL REAL slow on
         *  some cards (banked cards..)
         */
        if( bStretch && (ddcaps.dwCaps & DDCAPS_BANKSWITCHED) )
        {
            Msg( "On bank switched hardware, creating stretch buffer" );
            lpStretchBuffer = DDCreateSurface( GameSize.cx, GameSize.cy,
	    					TRUE, FALSE );
        }
    }
    else if (bFullscreen && nBufferCount == 1)
    {
        ddsd.dwFlags = DDSD_CAPS;
        ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

        OutputDebugString("Creating no backbuffer primary\n\r");
        ddrval = IDirectDraw_CreateSurface( lpDD, &ddsd, &lpFrontBuffer, NULL );

        if( ddrval != DD_OK )
        {
            Msg( "CreateSurface FAILED! %08lx", ddrval );
            return FALSE;
        }

        IDirectDrawSurface_AddRef(lpFrontBuffer);
        lpBackBuffer = lpFrontBuffer;
    }
    else
    {
        //
        //  window case, create the primary surface
        //  and create a backbuffer in offscreen memory.
        //
        ddsd.dwFlags = DDSD_CAPS;
        ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

        ddrval = IDirectDraw_CreateSurface( lpDD, &ddsd, &lpFrontBuffer, NULL );

        if( ddrval != DD_OK )
        {
            Msg( "CreateSurface FAILED! %08lx", ddrval );
            return FALSE;
        }

        lpBackBuffer = DDCreateSurface( GameSize.cx, GameSize.cy, FALSE, FALSE );

        if( lpBackBuffer == NULL )
        {
            Msg( "Cant create the backbuffer" );
            return FALSE;
        }

        //
        // now create a DirectDrawClipper object.
        //
        ddrval = IDirectDraw_CreateClipper(lpDD, 0, &lpClipper, NULL);

        if( ddrval != DD_OK )
        {
            Msg("Cant create clipper");
            return FALSE;
        }

        ddrval = IDirectDrawClipper_SetHWnd(lpClipper, 0, hWndMain);

        if( ddrval != DD_OK )
        {
            Msg("Cant set clipper window handle");
            return FALSE;
        }

        ddrval = IDirectDrawSurface_SetClipper(lpFrontBuffer, lpClipper);

        if( ddrval != DD_OK )
        {
            Msg("Cant attach clipper to front buffer");
            return FALSE;
        }
    }

    /*
     * init the color key
     */
    ddpf.dwSize = sizeof(ddpf);
    IDirectDrawSurface_GetPixelFormat(lpFrontBuffer, &ddpf);

    /*
     * we use white as the color key, if we are in a 8bpp mode, we know
     * what white is (because we use a 332 palette) if we are not in a
     * a 8bpp mode we dont know what white is and we need to figure it
     * out from the device (remember 16bpp comes in two common flavors
     * 555 and 565).  if we wanted to any random color as the color key
     * we would call DDColorMatch (see below) to convert a RGB into a
     * physical color.
     */
    if (ddpf.dwRGBBitCount == 8)
        dwColorKey = 0xff;
    else
        dwColorKey = ddpf.dwRBitMask | ddpf.dwGBitMask | ddpf.dwBBitMask;

    Msg("dwColorKey = 0x%08lX", dwColorKey);

    if( bTransDest )
    {
	DDCOLORKEY		ddck;
        ddck.dwColorSpaceLowValue = dwColorKey;
        ddck.dwColorSpaceHighValue = dwColorKey;
        IDirectDrawSurface_SetColorKey( lpBackBuffer, DDCKEY_DESTBLT, &ddck);
    }

    return TRUE;

} /* DDCreateFlippingSurface */

/*
 * DDCreateSurface
 */
LPDIRECTDRAWSURFACE DDCreateSurface(
		DWORD width,
		DWORD height,
		BOOL sysmem,
		BOOL trans )
{
    DDSURFACEDESC	ddsd;
    HRESULT		ddrval;
    DDCOLORKEY		ddck;
    LPDIRECTDRAWSURFACE	psurf;


    /*
     * fill in surface desc
     */
    memset( &ddsd, 0, sizeof( ddsd ) );
    ddsd.dwSize = sizeof( ddsd );
    ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT |DDSD_WIDTH;

    ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
    if( sysmem || bUseSysMem )
    {
	ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
    }

    ddsd.dwHeight = height;
    ddsd.dwWidth = width;

    ddrval = IDirectDraw_CreateSurface( lpDD, &ddsd, &psurf, NULL );

    /*
     * set the color key for this bitmap
     */
    if( ddrval == DD_OK )
    {
	if( trans && !bTransDest )
	{
            ddck.dwColorSpaceLowValue = dwColorKey;
            ddck.dwColorSpaceHighValue = dwColorKey;
            IDirectDrawSurface_SetColorKey( psurf, DDCKEY_SRCBLT, &ddck);
	}
    }
    else
    {
	Msg( "CreateSurface FAILED, rc = %ld", (DWORD) LOWORD( ddrval ) );
	psurf = NULL;
    }

     return psurf;

} /* DDCreateSurface */

DWORD DDColorMatch(IDirectDrawSurface *pdds, COLORREF rgb)
{
    COLORREF rgbT;
    HDC hdc;
    DWORD dw = CLR_INVALID;
    DDSURFACEDESC ddsd;
    HRESULT hres;

    if (IDirectDrawSurface_GetDC(pdds, &hdc) == DD_OK)
    {
        rgbT = GetPixel(hdc, 0, 0);
        SetPixel(hdc, 0, 0, rgb);
        IDirectDrawSurface_ReleaseDC(pdds, hdc);
    }

    ddsd.dwSize = sizeof(ddsd);
    hres = IDirectDrawSurface_Lock(
        pdds, NULL, &ddsd, DDLOCK_WAIT, NULL);

    if (hres == DD_OK)
    {
        dw  = *(DWORD *)ddsd.lpSurface;
	if(ddsd.ddpfPixelFormat.dwRGBBitCount != 32)
	    dw &= (1 << ddsd.ddpfPixelFormat.dwRGBBitCount)-1;
        IDirectDrawSurface_Unlock(pdds, NULL);
    }
    else
    {
	IDirectDrawSurface_GetSurfaceDesc(pdds,&ddsd);
	if(ddsd.ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
	    Msg("Failed to lock Primary Surface!");
	else
	    Msg("Failed to lock NON-PRIMARY Surface!");
    }

    if (IDirectDrawSurface_GetDC(pdds, &hdc) == DD_OK)
    {
        SetPixel(hdc, 0, 0, rgbT);
        IDirectDrawSurface_ReleaseDC(pdds, hdc);
    }

    return dw;
}

/*
 * ReadPalFile
 *
 * Create a DirectDrawPalette from a palette file
 *
 * if the palette files cant be found, make a default 332 palette
 */
LPDIRECTDRAWPALETTE ReadPalFile( char *fname )
{
    int                 i;
    int			fh;
    HRESULT		ddrval;
    IDirectDrawPalette *ppal;

    struct  {
        DWORD           dwRiff;
        DWORD           dwFileSize;
        DWORD           dwPal;
        DWORD           dwData;
        DWORD           dwDataSize;
        WORD            palVersion;
        WORD            palNumEntries;
        PALETTEENTRY    ape[256];
    }   pal;

    pal.dwRiff = 0;

    if (fname)
    {
        fh = _lopen( fname, OF_READ);

        if (fh != -1)
        {
            _lread(fh, &pal, sizeof(pal));
            _lclose(fh);
        }
    }

    /*
     * if the file is not a palette file, or does not exist
     * default to a 332 palette
     */
    if (pal.dwRiff != 0x46464952 || // 'RIFF'
        pal.dwPal  != 0x204C4150 || // 'PAL '
        pal.dwData != 0x61746164 || // 'data'
        pal.palVersion != 0x0300 ||
        pal.palNumEntries > 256  ||
        pal.palNumEntries < 1)
    {
        Msg("Can't open palette file, using default 332.");

        for( i=0; i<256; i++ )
        {
            pal.ape[i].peRed   = (BYTE)(((i >> 5) & 0x07) * 255 / 7);
            pal.ape[i].peGreen = (BYTE)(((i >> 2) & 0x07) * 255 / 7);
            pal.ape[i].peBlue  = (BYTE)(((i >> 0) & 0x03) * 255 / 3);
            pal.ape[i].peFlags = (BYTE)0;
        }
    }

    ddrval = IDirectDraw_CreatePalette(
			    lpDD,
			    DDPCAPS_8BIT,
                            pal.ape,
			    &ppal,
			    NULL );
    return ppal;

} /* ReadPalFile */


/*
 * Splash
 *
 * Draw a splash screen during startup
 * NOTE the screen has been cleared in DDCreateFlippingSurface
 */
void Splash( void )
{
    HDC hdc;
    HRESULT err = 0;

    DDClear();

//    if ((err = IDirectDrawSurface_GetDC(lpFrontBuffer, &hdc)) == DD_OK)
    if ((hdc = GetDC(hWndMain)) )
    {
        char ach[256];
        int len;

        len = wsprintf(ach,
            "FoxBear is loading......   Device:%s %s",
            aDDDevs[DevIndex].DriverDesc, aDDDevs[DevIndex].DriverName);

        SetTextColor(hdc, RGB(255,255,255));
        SetBkMode(hdc, TRANSPARENT);
        TextOut(hdc, rcWindow.left, rcWindow.top, ach, len);

        //IDirectDrawSurface_ReleaseDC(lpFrontBuffer, hdc);
        ReleaseDC(hWndMain,hdc);
    }
    else
    {
	Msg("GetDC failed! 0x%x",err);
    }

} /* Splash */

/*
 * MEMORY ALLOCATION ROUTINES...
 */

/*
 * MemAlloc
 */
LPVOID MemAlloc( UINT size )
{
    LPVOID	ptr;

    ptr = LocalAlloc( LPTR, size );
    return ptr;

} /* MemAlloc */

/*
 * CMemAlloc
 */
LPVOID CMemAlloc( UINT cnt, UINT isize )
{
    DWORD	size;
    LPVOID	ptr;

    size = cnt * isize;
    ptr = LocalAlloc( LPTR, size );
    return ptr;

} /* CMemAlloc */

/*
 * MemFree
 */
void MemFree( LPVOID ptr )
{
    if( ptr != NULL )
    {
	LocalFree( ptr );
    }

} /* MemFree */

HRESULT CALLBACK EnumDisplayModesCallback(LPDDSURFACEDESC pddsd, LPVOID Context)
{
    int ChkMode;

    Msg("Mode: %dx%dx%d", pddsd->dwWidth, pddsd->dwHeight,pddsd->ddpfPixelFormat.dwRGBBitCount);

    for(ChkMode = 0;ChkMode < NumModes;ChkMode++)
    {
        if(
            (ModeList[ChkMode].w == (int)pddsd->dwWidth)&&
            (ModeList[ChkMode].h == (int)pddsd->dwHeight)&&
            (ModeList[ChkMode].bpp == (int)pddsd->ddpfPixelFormat.dwRGBBitCount)&& 
            (ModeList[ChkMode].dwFlags == (int)pddsd->ddsCaps.dwCaps)
          )
            return DDENUMRET_OK;
    }

    ModeList[NumModes].w   = pddsd->dwWidth;
    ModeList[NumModes].h   = pddsd->dwHeight;
    ModeList[NumModes].bpp = pddsd->ddpfPixelFormat.dwRGBBitCount;
    ModeList[NumModes].dwFlags = pddsd->ddsCaps.dwCaps;

    NumModes++;

    return DDENUMRET_OK;
}

HRESULT CALLBACK DDEnumCallback(GUID * lpGUID, LPSTR DriverDesc, LPSTR DriverName, LPVOID lpContext)
{
    if(MaxDevIndex >= sizeof(aDDDevs)/sizeof(aDDDevs[0]))
        return E_FAIL;

    Msg("Device: %s (%s)", DriverDesc, DriverName);

    if(lpGUID == NULL || lpGUID == (GUID*)DDCREATE_EMULATIONONLY)
    {
        aDDDevs[MaxDevIndex].guid = lpGUID;
    }
    else
    {
        aDDDevs[MaxDevIndex].DriverGUID = *lpGUID;
        aDDDevs[MaxDevIndex].guid = &aDDDevs[MaxDevIndex].DriverGUID;
    }

    lstrcpyn(aDDDevs[MaxDevIndex].DriverDesc, DriverDesc, 128);
    lstrcpyn(aDDDevs[MaxDevIndex].DriverName, DriverName, 128);

    MaxDevIndex++;
    return DDENUMRET_OK;
}
