//=================================================================
//
// Copyright (c) Microsoft Corporation 1999 - All Rights Reserved.
//
//=================================================================

//========================================================================
// include files
//========================================================================
#include <windows.h>
#include <ddraw.h>
#include <d3d.h>
#include <stdio.h>
#include <stdarg.h>
#include "globals.h"
#include "softm.h"
#include "smddraw.h"

//========================================================================
// types
//========================================================================
typedef struct tagDDENUMSTUB
{
	LPDDENUMCALLBACKEXA		m_lpCallback;
	LPVOID					m_lpContext;
	DWORD					m_dwFlags;
} DDENUMSTUB;


//========================================================================
// macros
//========================================================================
#define FREELIBRARY(hinst) if (hinst) { FreeLibrary(hinst); (hinst)=NULL; }
#ifndef RELEASE
#define RELEASE if(x) { (x)->Release(); (x)=NULL; }
#endif
//#define USE_DX

//========================================================================
// global variables
//========================================================================
HINSTANCE		g_hInstDll = NULL;
HINSTANCE		g_hInstDDraw = NULL;

#ifdef _DEBUG
const char		g_szDeviceName[] = "Microsoft Blade Software Rasterizer - DEBUG";
const char		g_szDeviceDesc[] = "Blade - DEBUG";
#else
const char		g_szDeviceName[] = "Microsoft Blade Software Rasterizer";
const char		g_szDeviceDesc[] = "Blade";
#endif

//========================================================================
// global function pointers
//========================================================================
HRESULT (WINAPI *g_pfnDirectDrawCreate)( GUID FAR *lpGUID, LPDIRECTDRAW FAR *lplpDD, IUnknown FAR *pUnkOuter ) = NULL;
HRESULT (WINAPI *g_pfnDirectDrawCreateClipper)( DWORD dwFlags, LPDIRECTDRAWCLIPPER FAR *lplpDDClipper, IUnknown FAR *pUnkOuter ) = NULL;
HRESULT (WINAPI *g_pfnDirectDrawEnumerateW)( LPDDENUMCALLBACKW lpCallback, LPVOID lpContext ) = NULL;
HRESULT (WINAPI *g_pfnDirectDrawEnumerateA)( LPDDENUMCALLBACKA lpCallback, LPVOID lpContext ) = NULL;
HRESULT (WINAPI *g_pfnDirectDrawEnumerateExW)( LPDDENUMCALLBACKEXW lpCallback, LPVOID lpContext, DWORD dwFlags) = NULL;
HRESULT (WINAPI *g_pfnDirectDrawEnumerateExA)( LPDDENUMCALLBACKEXA lpCallback, LPVOID lpContext, DWORD dwFlags) = NULL;
#ifdef USE_DX7_INTERFACE
HRESULT (WINAPI *g_pfnDirectDrawCreateEx)( GUID FAR *lpGUID, LPVOID *lplpDD, REFIID iid, IUnknown FAR *pUnkOuter ) = NULL;
#endif

//========================================================================
// static function prototypes1
//========================================================================
static BOOL InitDirectDrawLibrary(void);

//========================================================================
// dll main
//========================================================================
#ifdef SOFTM_EXPORTS
BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpReserved)
{
	switch (fdwReason)
	{
	case DLL_PROCESS_ATTACH:
		g_hInstDll = hInstDll;
		//return InitDirectDrawLibrary();

	case DLL_THREAD_ATTACH:
		break;

	case DLL_PROCESS_DETACH:
		FreeLibrary(g_hInstDDraw);

	case DLL_THREAD_DETACH:
		break;

	}


	return TRUE;
}
#endif



//========================================================================
//========================================================================
HRESULT WINAPI DirectDrawCreate( GUID FAR *lpGUID, LPDIRECTDRAW FAR *lplpDD, IUnknown FAR *pUnkOuter )
{
	InitDirectDrawLibrary();

#ifdef USE_DX
	return g_pfnDirectDrawCreate(lpGUID, lplpDD, pUnkOuter);
#endif

	if (lpGUID)
	{
		GUID nullguid={0};
		if (*lpGUID == nullguid)
			lpGUID=NULL;
	}

	CDirectDraw *pDD = NULL;
	*lplpDD = NULL;

	pDD = new CDirectDraw;
	if (pDD)
	{
		HRESULT hr;
		if (SUCCEEDED(hr = g_pfnDirectDrawCreate(lpGUID, &pDD->m_pDirectDraw, pUnkOuter)))
		{
			pDD->m_pDirectDraw->QueryInterface(IID_IDirectDraw4, (void **)&pDD->m_pDirectDraw4);
			pDD->m_pDirectDraw->QueryInterface(IID_IDirect3D, (void **)&pDD->m_pDirect3D);
			pDD->m_pDirectDraw->QueryInterface(IID_IDirect3D3, (void **)&pDD->m_pDirect3D3);
			pDD->AddRef();

			*lplpDD = (IDirectDraw *)pDD;
		}
		else
			delete pDD;

		return hr;
	}
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
HRESULT WINAPI DirectDrawCreateEx(GUID FAR *lpGUID, LPVOID *lplpDD, REFIID iid, IUnknown FAR *pUnkOuter)
{
#ifdef USE_DX7_INTERFACE
	if (iid != IID_IDirectDraw7)
		return -1;

	InitDirectDrawLibrary();

#ifdef USE_DX
	return g_pfnDirectDrawCreateEx(lpGUID, lplpDD, iid, pUnkOuter);
#endif

	if (lpGUID)
	{
		GUID nullguid={0};
		if (*lpGUID == nullguid)
			lpGUID=NULL;
	}

	CDirectDraw *pDD = NULL;
	*lplpDD = NULL;

	pDD = new CDirectDraw;
	if (pDD)
	{
		HRESULT hr;
		if (g_pfnDirectDrawCreateEx)
		{
			if (SUCCEEDED(hr = g_pfnDirectDrawCreateEx(lpGUID, (void **)&pDD->m_pDirectDraw4, IID_IDirectDraw7, pUnkOuter)))
			{
				pDD->m_pDirectDraw4->QueryInterface(IID_IDirect3D7, (void **)&pDD->m_pDirect3D3);
				pDD->m_pDirectDraw4->QueryInterface(IID_IDirectDraw, (void **)&pDD->m_pDirectDraw);
				pDD->AddRef();

				*lplpDD = (IDirectDraw7 *)pDD;
			}
			else
				delete pDD;
		}
		else
		{
			if (SUCCEEDED(hr = g_pfnDirectDrawCreate(lpGUID, &pDD->m_pDirectDraw, pUnkOuter)))
			{
				pDD->m_pDirectDraw->QueryInterface(IID_IDirectDraw4, (void **)&pDD->m_pDirectDraw4);
				pDD->m_pDirectDraw->QueryInterface(IID_IDirect3D, (void **)&pDD->m_pDirect3D);
				pDD->m_pDirectDraw->QueryInterface(IID_IDirect3D3, (void **)&pDD->m_pDirect3D3);
				pDD->AddRef();

				*lplpDD = (IDirectDraw7 *)pDD;
			}
			else
				delete pDD;
		}

		return hr;
	}
	else
		return E_FAIL;


	return S_OK;
#else
	return -2;
#endif
}

//========================================================================
//========================================================================
HRESULT WINAPI DirectDrawCreateClipper( DWORD dwFlags, LPDIRECTDRAWCLIPPER FAR *lplpDDClipper, IUnknown FAR *pUnkOuter )
{
	InitDirectDrawLibrary();

	if (g_pfnDirectDrawCreateClipper)
		return g_pfnDirectDrawCreateClipper(dwFlags, lplpDDClipper, pUnkOuter);
	else
		return -1;
}

//========================================================================
//========================================================================
HRESULT WINAPI DirectDrawEnumerateW( LPDDENUMCALLBACKW lpCallback, LPVOID lpContext )
{
	InitDirectDrawLibrary();

	if (g_pfnDirectDrawEnumerateW)
		return g_pfnDirectDrawEnumerateW(lpCallback, lpContext);
	else
		return -1;
}

//========================================================================
//========================================================================
HRESULT WINAPI DirectDrawEnumerateA( LPDDENUMCALLBACKA lpCallback, LPVOID lpContext )
{
	InitDirectDrawLibrary();

	if (g_pfnDirectDrawEnumerateA)
		return g_pfnDirectDrawEnumerateA(lpCallback, lpContext);
	else
		return -1;
}

//========================================================================
//========================================================================
HRESULT WINAPI DirectDrawEnumerateExW( LPDDENUMCALLBACKEXW lpCallback, LPVOID lpContext, DWORD dwFlags)
{
	InitDirectDrawLibrary();

	if (g_pfnDirectDrawEnumerateExW)
		return g_pfnDirectDrawEnumerateExW(lpCallback, lpContext, dwFlags);
	else
		return -1;
}

//========================================================================
//========================================================================
static BOOL WINAPI DDEnumCallbackStub(GUID FAR *lpGUID, LPSTR lpDriverDescription, LPSTR lpDriverName, DDENUMSTUB *lpContext)
{
	return lpContext->m_lpCallback(lpGUID, lpDriverDescription, lpDriverName, lpContext->m_lpContext, NULL);

}

//========================================================================
//========================================================================
HRESULT WINAPI DirectDrawEnumerateExA( LPDDENUMCALLBACKEXA lpCallback, LPVOID lpContext, DWORD dwFlags)
{
	InitDirectDrawLibrary();

	if (g_pfnDirectDrawEnumerateExA)
		return g_pfnDirectDrawEnumerateExA(lpCallback, lpContext, dwFlags);
	else
	{
		DDENUMSTUB ddes;

		ddes.m_lpCallback	= lpCallback;
		ddes.m_lpContext	= lpContext;
		ddes.m_dwFlags		= dwFlags;

		if (g_pfnDirectDrawEnumerateA)
			return g_pfnDirectDrawEnumerateA((LPDDENUMCALLBACKA)DDEnumCallbackStub, &ddes);
		else
			return -1;
	}
}



//========================================================================
//========================================================================
BOOL InitDirectDrawLibrary(void)
{
	if (g_hInstDDraw)
		return FALSE;

	g_hInstDDraw = LoadLibrary("ddraw");

	if (!g_hInstDDraw)
		return FALSE;

	*(FARPROC *)&g_pfnDirectDrawCreate			= GetProcAddress(g_hInstDDraw, "DirectDrawCreate");
	*(FARPROC *)&g_pfnDirectDrawCreateClipper	= GetProcAddress(g_hInstDDraw, "DirectDrawCreateClipper");
	*(FARPROC *)&g_pfnDirectDrawEnumerateW		= GetProcAddress(g_hInstDDraw, "DirectDrawEnumerateW");
	*(FARPROC *)&g_pfnDirectDrawEnumerateA		= GetProcAddress(g_hInstDDraw, "DirectDrawEnumerateA");
	*(FARPROC *)&g_pfnDirectDrawEnumerateExW	= GetProcAddress(g_hInstDDraw, "DirectDrawEnumerateExW");
	*(FARPROC *)&g_pfnDirectDrawEnumerateExA	= GetProcAddress(g_hInstDDraw, "DirectDrawEnumerateExA");
#ifdef USE_DX7_INTERFACE
	*(FARPROC *)&g_pfnDirectDrawCreateEx		= GetProcAddress(g_hInstDDraw, "DirectDrawCreateEx");
#endif

	if (
		(g_pfnDirectDrawCreate == NULL) ||
		(g_pfnDirectDrawCreateClipper == NULL) ||
		(g_pfnDirectDrawEnumerateW == NULL) ||
		(g_pfnDirectDrawEnumerateA == NULL))
	{
		FREELIBRARY(g_hInstDDraw);
		return FALSE;
	}

	return TRUE;
}


//========================================================================
//========================================================================
#ifdef _DEBUG
int __cdecl dprintf(char *format,...)
{
	va_list arglist;
	char buffer[1024];
	int retval;

	va_start(arglist,format);
	retval=vsprintf(buffer,format,arglist);
	va_end(arglist);

	OutputDebugString(buffer);
	return retval;
}
#endif
