This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

| dx8Diagnostics and dx8Dynamic Submitted by

I've put together a library with two components: dx8Dynamic and dx8Diagnostics. dx8Dynamic lets you load DirectX 8 by hand at runtime, instead of using the import library, so that a game can run and fail gracefully on machines without DirectX 8 instead of Windows printing out that offensive error message. dx8Diagnostics is a diagnostic dumper, which prints out all sorts of information to an HTML file (or to memory).
You can read all about it, and download a copy, here:
http://www.midwinter.com/~lch/programming/dx8diagnostics/ I would be entirely happy if this was used as a flipcode COTD; that's what inspired me to clean it up and release it. (And to make it so nice in the first place.) As for licensing, I put it under a "do what you like, just leave my name on the source"-type license. larry

Currently browsing [dx8diagnostics.zip] (84,112 bytes) - [dx8diagnostics.cpp] - (84,038 bytes)

    /*
    ** Copyright (C) 2003 Larry Hastings, larry@hastings.org
    **
    ** This software is provided 'as-is', without any express or implied warranty.  In no event will the authors be held liable for any damages arising from the use of this software.
    **
    ** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
    **
    ** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
    ** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
    ** 3. This notice may not be removed or altered from any source distribution.
    **
    ** The dx8Diagnostics / dx8Dynamic homepage is here:
    **		http://www.midwinter.com/~lch/programming/dx8diagnostics/
    */

    #include <ctype.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>

    #include "dx8Diagnostics.h"

    /*
    ** all of these macros assume the following
    **  *  there is a variable "returnValue" of some sort of integer/enum type
    **  *  there a goto label in the current function called "EXIT"
    **  *  zero is success, nonzero is failure
    */

    #define RETURN(expr)		\
    	{						\
    	returnValue = expr;		\
    	goto EXIT;				\
    	}						\

    /* if the expression "expr" is nonzero, return whatever "expr" was */
    #define ASSERT_SUCCESS(expr)\
    	{						\
    	returnValue = expr;		\
    	if (returnValue)		\
    		goto EXIT;			\
    	}						\

    /* if the expression "expr" is false, return rv */
    #define ASSERT_RETURN(expr, rv)	\
    	{						\
    	if (!(expr))			\
    		{					\
    		returnValue = rv;	\
    		goto EXIT;			\
    		}					\
    	}						\

    ///////////////////////////////////////////////////////////////////////////
    //
    //
    // random string helper functions
    //
    //

    static char *collapse(char *s)
    	{
    	char *source = s;
    	char *destination = s;
    	do
    		{
    		if (isspace(*source))
    			source++;
    		else
    			*destination++ = *source++;
    		} while (*source);
    		*destination = 0;
    		return s;
    	};

    ///////////////////////////////////////////////////////////////////////////
    //
    //
    // dx8DiagnosticsNameValue
    //
    //

    dx8DiagnosticsNameValue *dx8DiagnosticsNameValueFind(dx8DiagnosticsNameValue *array, DWORD value)
    	{
    	while (array->name != NULL)
    		{
    		if (array->value == value)
    			return array;
    		array++;
    		}
    	return NULL;
    	}

    ///////////////////////////////////////////////////////////////////////////
    //
    //
    // dx8DiagnosticsOutput
    //
    //

    static char outputBuffer[2048];

    static colorScheme colorSchemes[] =
    	{
    		{
    		{ "#0000b0", "white" }, // header
    		{ "#a0a0e0", "white" }, // subheader
    		{ "#e0e0ff", "black" } // body
    		},

    		{
    		{ "#d0d0d0", "white" }, // header
    		{ "#a0a0a0", "white" }, // subheader
    		{ "white", "black" } // body
    		},

    		{
    		{ "#0000b0", "white" }, // header
    		{ "#a0a0e0", "white" }, // subheader
    		{ "#ececff", "black" } // body
    		},

    		{
    		{ "#606060", "white" }, // header
    		{ "#a0a0a0", "white" }, // subheader
    		{ "#f0f0f0", "black" } // body
    		},

    		{
    		{ NULL, NULL }, // header
    		{ NULL, NULL }, // subheader
    		{ NULL, NULL } // body
    		},
    	};

    dx8DiagnosticsOutput::dx8DiagnosticsOutput(void)
    	{
    	eos = false;

    	schemes = colorSchemes + 2;
    	scheme = colorSchemes;
    	}

    void dx8DiagnosticsOutput::nextScheme(void)
    	{
    	scheme++;
    	if (scheme->header.background == NULL)
    		scheme = schemes;
    	}

    HRESULT dx8DiagnosticsOutput::printEnum(char *name, DWORD value, dx8DiagnosticsNameValue *enumValues)
    	{
    	char *stringValue = "unknown?";
    	dx8DiagnosticsNameValue *namevalue = dx8DiagnosticsNameValueFind(enumValues, value);
    	if (namevalue != NULL)
    		stringValue = namevalue->name;

    	return print("<dt><b>%s</b>\n<dd><code>%s</code> (%u, <code>0x%x</code>)\n", name, stringValue, value, value);
    	}

    HRESULT dx8DiagnosticsOutput::printBitfield(char *name, DWORD value, dx8DiagnosticsNameValue *bitValues)
    	{
    	HRESULT returnValue = S_OK;

    	ASSERT_SUCCESS(print("<dt><b>%s</b>\n<dd><code>0x%x</code> (%u)\n<blockquote>", name, value, value));

    	int i;
    	DWORD bitfield;
    	bool addBreak;

    	addBreak = false;
    	for (i = 0; i < 32; i++)
    		{
    		bitfield = 1 << i;
    		if ((value & bitfield) == bitfield)
    			{
    			dx8DiagnosticsNameValue *namevalue = dx8DiagnosticsNameValueFind(bitValues, bitfield);
    			char *stringValue = "unknown?";
    			if (namevalue != NULL)
    				stringValue = namevalue->name;
    			if (addBreak)
    				{
    				ASSERT_SUCCESS(print("<br>"));
    				}
    			addBreak = true;
    			ASSERT_SUCCESS(print("<code>%s</code> (<code>0x%x</code>, %u)\n", stringValue, bitfield, bitfield));
    			}
    		}

    	RETURN(print("</blockquote>\n"));

    EXIT:
    	return returnValue;
    	}

    HRESULT dx8DiagnosticsOutput::printInteger(char *name, DWORD value)
    	{
    	return print("<dt><b>%s</b>\n<dd>%u, <code>0x%x</code>\n", name, value, value);
    	}

    HRESULT dx8DiagnosticsOutput::printFloat(char *name, double value)
    	{
    	return print("<dt><b>%s</b>\n<dd>%f\n", name, value);
    	}

    HRESULT dx8DiagnosticsOutput::printString(char *name, const char *value)
    	{
    	return print("<dt><b>%s</b>\n<dd><code>%s</code>\n", name, value);
    	}

    HRESULT dx8DiagnosticsOutput::printVersion(char *name, LARGE_INTEGER v)
    	{
    	int product = HIWORD(v.HighPart);
    	int version = LOWORD(v.HighPart);
    	int subVersion = HIWORD(v.LowPart);
    	int build = LOWORD(v.LowPart);
    	return print("<dt><b>%s</b>\n<dd>%d.%d.%d.%d\n", name, product, version, subVersion, build);
    	}

    HRESULT dx8DiagnosticsOutput::printGUID(char *name, const GUID *value)
    	{
    	HRESULT returnValue = S_OK;

    	ASSERT_SUCCESS(print("<dt><b>%s</b>\n<dd><code>{%08x-%04x-%04x-", name, (DWORD)value->Data1, (DWORD)value->Data2, (DWORD)value->Data3));

    	int i;
    	for (i = 0; i < 8; i++)
    		{
    		char buffer[32];
    		// if you don't do this little dance, a char with its high bit set
    		// will result in a sign extension, and you'll wind up with FFFFFFA3
    		// or some such nonsense.
    		sprintf(buffer, "%08x", (DWORD)value->Data4[i]);
    		ASSERT_SUCCESS(print("%s", buffer + 6));

    		// canonical GUID form adds a dash here.  don't ask me why.
    		if (i == 1)
    			ASSERT_SUCCESS(print("-"));
    		}
    	RETURN(print("}</code>\n"));

    EXIT:
    	return returnValue;
    	}

    HRESULT dx8DiagnosticsOutput::printWHQLLevel(char *name, DWORD level)
    	{
    	HRESULT returnValue = S_OK;

    	ASSERT_SUCCESS(print("<dt><b>%s</b>\n<dd>", name));

    	switch (level)
    		{
    		case 0:
    			RETURN(print("<i>Not digitally signed.</i>"));
    		case 1:
    			RETURN(print("WHQL digitally-signed, but no date information is available."));
    		default:
    			{
    			DWORD year = HIWORD(level);
    			DWORD month = HIBYTE(LOWORD(level));
    			DWORD day = LOBYTE(LOWORD(level));
    			RETURN(print("WHQL digitally-signed on <code>%d/%d/%d</code>.", year, month, day));
    			}
    		}

    EXIT:
    	return returnValue;
    	}

    HRESULT dx8DiagnosticsOutput::printWednesdaySpaced(char *string)
    	{
    	char buffer[256];
    	char *trace = buffer;
    	bool addSpace = false;
    	bool keepGoing = true;
    	char charBuffer[2];
    	charBuffer[1] = 0;
    	while (keepGoing)
    		{
    		char *append;
    		switch (*string)
    			{
    			case 0:
    				keepGoing = false;
    				continue;
    			case ' ':
    				append = " ";
    				break;
    			default:
    				append = charBuffer;
    				*charBuffer = *string;
    				break;
    			}
    		if (addSpace)
    			*trace++ = ' ';
    		addSpace = true;
    		lstrcpy(trace, append);
    		trace += lstrlen(trace);
    		string++;
    		}
    	return print(buffer);
    	}

    HRESULT dx8DiagnosticsOutput::printHeading(char *title, int size)
    	{
    	HRESULT returnValue = S_OK;

    	char collapsedName[64];
    	strcpy(collapsedName, title);
    	collapse(collapsedName);

    	ASSERT_SUCCESS(print("<a name=%s><table width=100%% bgcolor=%s><tr><td><br><font size=%d color=%s><code><b>", collapsedName, scheme->header.background, size, scheme->header.foreground));
    	ASSERT_SUCCESS(printWednesdaySpaced(title));
    	RETURN(print("</b></code></font><p></td></tr></table></a>\n"));

    EXIT:
    	return returnValue;
    	}

    HRESULT dx8DiagnosticsOutput::printSubheading(char *title, int size)
    	{
    	HRESULT returnValue = S_OK;

    	ASSERT_SUCCESS(print("</dl><p><table width=100%% bgcolor=%s><tr><td><font size=%d color=%s><code><b>", scheme->subheader.background, size, scheme->subheader.foreground));
    	ASSERT_SUCCESS(printWednesdaySpaced(title));
    	RETURN(print("</b></code></font></td></tr></table></a><dl>\n"));

    EXIT:
    	return returnValue;
    	}

    HRESULT dx8DiagnosticsOutput::startBody(const char *subtitle, int size)
    	{
    	HRESULT returnValue = S_OK;

    	ASSERT_SUCCESS(print("<table width=100%% bgcolor=%s><tr><td><font color=%s>\n", scheme->body.background, scheme->body.foreground));

    	if (subtitle != NULL)
    		ASSERT_SUCCESS(print("<font size=5><b>%s</b><p></font>\n", subtitle));

    	RETURN(print("<dl>"));

    EXIT:
    	return returnValue;
    	}

    HRESULT dx8DiagnosticsOutput::endBody(void)
    	{
    	HRESULT returnValue = print("</dl></font></td></tr></table><p>\n");
    	nextScheme();
    	return returnValue;
    	}

    ///////////////////////////////////////////////////////////////////////////
    //
    //
    // dx8DiagnosticsOutputMemory
    //
    //

    void dx8DiagnosticsOutputMemory::initialize(char *buffer, DWORD bufferSize)
    	{
    	this->buffer = buffer;
    	this->bufferSize = bufferSize;
    	this->trace = buffer;
    	this->end = buffer + bufferSize;
    	}

    dx8DiagnosticsOutputMemory::dx8DiagnosticsOutputMemory()
    	{
    	buffer = NULL;
    	}

    HRESULT dx8DiagnosticsOutputMemory::print(char *format, ...)
    	{
    	if ((buffer == NULL) || (trace >= end))
    		return E_FAIL;

    	va_list list;
    	va_start(list, format);
    	// argh!  would have used wvsprintf() here, except it doesn't support %f !
    	DWORD length = vsprintf(outputBuffer, format, list);
    	va_end(list);

    #define MIN(a, b) ((a) < (b) ? (a) : (b))
    	length = MIN(length, (DWORD)((end - trace) - 1));
    	memcpy(trace, outputBuffer, length);
    	trace[length] = 0;
    	trace += length;
    	eos = (trace >= end);
    	return eos ? E_FAIL : S_OK;
    	}

    ///////////////////////////////////////////////////////////////////////////
    //
    //
    // dx8DiagnosticsOutputFile
    //
    //
    dx8DiagnosticsOutputFile::dx8DiagnosticsOutputFile()
    	{
    	hFile = INVALID_HANDLE_VALUE;
    	}

    void dx8DiagnosticsOutputFile::initialize(char *filename)
    	{
    	hFile = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
    	}

    dx8DiagnosticsOutputFile::~dx8DiagnosticsOutputFile()
    	{
    	if (hFile != INVALID_HANDLE_VALUE)
    		CloseHandle(hFile);
    	}

    HRESULT dx8DiagnosticsOutputFile::print(char *format, ...)
    	{
    	if (hFile == INVALID_HANDLE_VALUE)
    		return E_FAIL;

    	va_list list;
    	va_start(list, format);
    	// argh!  would have used wvsprintf() here, except it doesn't support %f !
    	DWORD length = vsprintf(outputBuffer, format, list);
    	va_end(list);
    	DWORD written;
    	WriteFile(hFile, outputBuffer, length, &written, NULL);
    	eos = (length != written);
    	return eos ? E_FAIL : S_OK;
    	}

    class printerTitle : public dx8DiagnosticsPrinter
    	{
    	public:

    	virtual HRESULT printTOC(dx8Diagnostics *diagnostics)
    		{
    		return S_OK;
    		}

    	virtual HRESULT printBody(dx8Diagnostics *diagnostics)
    		{
    		HRESULT returnValue = S_OK;

    		dx8DiagnosticsOutput *output = diagnostics->output;

    		ASSERT_SUCCESS(output->print("<html>\n\n<head>\n<title>%s Diagnostics</title>\n</head>\n\n<body>\n\n", diagnostics->applicationName));

    		ASSERT_SUCCESS(output->printHeading(diagnostics->applicationName, 7));
    		ASSERT_SUCCESS(output->printHeading("diagnostics"));
    		ASSERT_SUCCESS(output->startBody());

    		time_t now;
    		time(&now);

    		struct tm *local;
    		local = localtime(&now);
    		char buffer[64];
    		strftime(buffer, sizeof(buffer), "generated %Y/%m/%d %H:%M:%S", local);
    		ASSERT_SUCCESS(output->print("<code>"));
    		ASSERT_SUCCESS(output->printWednesdaySpaced(buffer));
    		ASSERT_SUCCESS(output->print("</code>\n"));

    		RETURN(output->endBody());

    EXIT:
    		return returnValue;
    		}
    	};

    class printerTOC : public dx8DiagnosticsPrinter
    	{
    	public:

    	virtual HRESULT printTOC(dx8Diagnostics *diagnostics)
    		{
    		return S_OK;
    		}

    	virtual HRESULT printBody(dx8Diagnostics *diagnostics)
    		{
    		HRESULT returnValue = S_OK;

    		dx8DiagnosticsOutput *output = diagnostics->output;

    		ASSERT_SUCCESS(output->printHeading("table of contents"));
    		ASSERT_SUCCESS(output->startBody());

    		dx8DiagnosticsPrinter *trace;
    		trace = diagnostics->head;
    		while (trace != NULL)
    			{
    			ASSERT_SUCCESS(trace->printTOC(diagnostics));
    			trace = trace->next;
    			}

    		RETURN(output->endBody());
    EXIT:
    		return returnValue;
    		}
    	};

    class printerFooter : public dx8DiagnosticsPrinter
    	{
    	public:

    		virtual HRESULT printTOC(dx8Diagnostics *diagnostics)
    			{
    			return S_OK;
    			}

    		virtual HRESULT printBody(dx8Diagnostics *diagnostics)
    			{
    			HRESULT returnValue = S_OK;
    			diagnostics->endTime = GetTickCount();

    			char buffer[64];
    			sprintf(buffer, "calculation took %dms", diagnostics->endTime - diagnostics->startTime);
    			dx8DiagnosticsOutput *output = diagnostics->output;
    			ASSERT_SUCCESS(output->printHeading(buffer, 4));
    			ASSERT_SUCCESS(output->print("<table width=100%% bgcolor=%s><tr><td><p><font color=%s size=-1 face=arial,helvetica><b>Generated by <a href=http://www.midwinter.com/~lch/programming/dx8diagnostics/><font color=%s>dx8Diagnostics version " DX8DIAGNOSTICS_VERSION "</font></a></b></font></td></tr></table>", output->scheme->header.background, output->scheme->header.foreground, output->scheme->header.foreground));
    			RETURN(diagnostics->output->print("\n\n</body>\n</html>"));
    EXIT:
    			return returnValue;
    			}
    	};

    ///////////////////////////////////////////////////////////////////////////
    //
    //
    // hardware
    //
    //

    ///////////////////////////////////////////////////////////////////////////
    //
    //
    // IShellDispatch2 interface
    // (typed in here so this compiles with MSVC6 even without the platform SDK)
    //
    //
    #ifndef __IShellDispatch2_FWD_DEFINED__
    #define __IShellDispatch2_FWD_DEFINED__
    typedef interface IShellDispatch2 IShellDispatch2;
    #endif 	/* __IShellDispatch2_FWD_DEFINED__ */

    /* interface IShellDispatch2 */
    /* [object][dual][hidden][oleautomation][helpstring][uuid] */

    IID IID_IShellDispatch2 =
    	{
    	2764474668,
    	15273,
    	4562,
    		{
    		157,
    		234,
    		0,
    		192,
    		79,
    		177,
    		97,
    		98,
    		}
    	};

    MIDL_INTERFACE("A4C6892C-3BA9-11d2-9DEA-00C04FB16162")
    IShellDispatch2 : public IShellDispatch
        {
        public:
            virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE IsRestricted(
                /* [in] */ BSTR Group,
                /* [in] */ BSTR Restriction,
                /* [retval][out] */ long *plRestrictValue) = 0;

    			virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE ShellExecute(
                /* [in] */ BSTR File,
                /* [optional][in] */ VARIANT vArgs,
                /* [optional][in] */ VARIANT vDir,
                /* [optional][in] */ VARIANT vOperation,
                /* [optional][in] */ VARIANT vShow) = 0;

    			virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE FindPrinter(
                /* [optional][in] */ BSTR name,
                /* [optional][in] */ BSTR location,
                /* [optional][in] */ BSTR model) = 0;

    			virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetSystemInformation(
                /* [in] */ BSTR name,
                /* [retval][out] */ VARIANT *pv) = 0;

    			virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE ServiceStart(
                /* [in] */ BSTR ServiceName,
                /* [in] */ VARIANT Persistent,
                /* [retval][out] */ VARIANT *pSuccess) = 0;

    			virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE ServiceStop(
                /* [in] */ BSTR ServiceName,
                /* [in] */ VARIANT Persistent,
                /* [retval][out] */ VARIANT *pSuccess) = 0;

    			virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE IsServiceRunning(
                /* [in] */ BSTR ServiceName,
                /* [retval][out] */ VARIANT *pRunning) = 0;

    			virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE CanStartStopService(
                /* [in] */ BSTR ServiceName,
                /* [retval][out] */ VARIANT *pCanStartStop) = 0;

    			virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE ShowBrowserBar(
                /* [in] */ BSTR bstrClsid,
                /* [in] */ VARIANT bShow,
                /* [retval][out] */ VARIANT *pSuccess) = 0;

        };

    class printerHardware : public dx8DiagnosticsPrinter
    	{
    	public:

    	virtual HRESULT printTOC(dx8Diagnostics *diagnostics)
    		{
    		dx8DiagnosticsOutput *output = diagnostics->output;
    		return output->print("<dd><a href=#hardware><code><b>hardware</b></code></a>\n");
    		}

    	virtual HRESULT printBody(dx8Diagnostics *diagnostics)
    		{
    		HRESULT returnValue = S_OK;

    		dx8DiagnosticsOutput *output = diagnostics->output;

    		IShellDispatch2 *shell2 = NULL;
    		BSTR bstr = NULL;

    		ASSERT_SUCCESS(output->printHeading("hardware"));
    		ASSERT_SUCCESS(output->startBody());

    		SYSTEM_INFO systemInfo;
    		memset(&systemInfo, 0, sizeof(systemInfo));
    		GetSystemInfo(&systemInfo);

    	#define PRINT_SYSTEM_INFO_FIELD(name, type) ASSERT_SUCCESS(PRINT_FIELD_ ## type(name, systemInfo))
    		PRINT_SYSTEM_INFO_FIELD(dwNumberOfProcessors, Integer);
    		PRINT_SYSTEM_INFO_FIELD(dwProcessorType, Integer);
    		PRINT_SYSTEM_INFO_FIELD(wProcessorLevel, Integer);
    		ASSERT_SUCCESS(output->printInteger("Model", (systemInfo.wProcessorRevision & 0xFFff) >> 8));
    		ASSERT_SUCCESS(output->printInteger("Stepping", systemInfo.wProcessorRevision & 0xFF));

    		if (CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch2, (void **)&shell2) == 0)
    			{
    			VARIANT value;

    			bstr = SysAllocString(L"PhysicalMemoryInstalled");
    			ASSERT_RETURN(bstr != NULL, E_FAIL);
    			memset(&value, 0, sizeof(value));
    			if (SUCCEEDED(shell2->GetSystemInformation(bstr, &value)))
    				ASSERT_SUCCESS(output->printInteger("Physical Memory Installed (MB)", (DWORD)(value.dblVal / (1024 * 1024))));
    			SysFreeString(bstr);

    			bstr = SysAllocString(L"ProcessorSpeed");
    			ASSERT_RETURN(bstr != NULL, E_FAIL);
    			memset(&value, 0, sizeof(value));
    			if (SUCCEEDED(shell2->GetSystemInformation(bstr, &value)))
    				ASSERT_SUCCESS(output->printInteger("Processor Speed", value.intVal));
    			}

    		RETURN(output->endBody());

    EXIT:
    		if (shell2 != NULL)
    			shell2->Release();
    		if (bstr != NULL)
    			SysFreeString(bstr);

    		return returnValue;
    		}
    	};

    ///////////////////////////////////////////////////////////////////////////
    //
    //
    // operating system
    //
    //

    class printerOS : public dx8DiagnosticsPrinter
    	{
    	public:

    	virtual HRESULT printTOC(dx8Diagnostics *diagnostics)
    		{
    		dx8DiagnosticsOutput *output = diagnostics->output;
    		return output->print("<dd><a href=#operatingsystem><code><b>operating system</b></code></a>\n");
    		}

    	virtual HRESULT printBody(dx8Diagnostics *diagnostics)
    		{
    		HRESULT returnValue = S_OK;

    		dx8DiagnosticsOutput *output = diagnostics->output;

    		OSVERSIONINFO osvi;
    		memset(&osvi, 0, sizeof(osvi));
    		osvi.dwOSVersionInfoSize = sizeof(osvi);
    		bool gotOsvi = (GetVersionEx(&osvi) != 0);

    		char *osName;
    		if (!gotOsvi)
    			osName = "<i>could not be determined!</i>";
    		else
    			{
    			osName = "<i>unknown!</i>";
    			switch (osvi.dwMajorVersion)
    				{
    				case 3:
    					{
    					osName = "Windows NT 3.51";
    					break;
    					}
    				case 4:
    					{
    					switch (osvi.dwMinorVersion)
    						{
    						case 0:
    							if (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
    								osName = "Windows 95";
    							else
    								osName = "Windows NT 4.0";
    							break;
    						case 10:
    							osName = "Windows 98";
    							break;
    						case 90:
    							osName = "Windows ME";
    							break;
    						}
    					break;
    					}
    				case 5:
    					{
    					switch (osvi.dwMinorVersion)
    						{
    						case 0:
    							osName = "Windows 2000";
    							break;
    						case 1:
    							osName = "Windows XP or .NET Server";
    							break;
    						}
    					}
    				}
    			}

    		char buffer[128];
    		DWORD length;

    		ASSERT_SUCCESS(output->printHeading("operating system"));
    		ASSERT_SUCCESS(output->startBody(osName));

    		length = sizeof(buffer);
    		if (!GetComputerName(buffer, &length))
    			strcpy(buffer, "<i>could not be determined!</i>");
    		ASSERT_SUCCESS(output->printString("Computer Name", buffer));

    		length = sizeof(buffer);
    		if (!GetUserName(buffer, &length))
    			strcpy(buffer, "<i>could not be determined!</i>");
    		ASSERT_SUCCESS(output->printString("User Name", buffer));

    		#define PRINT_OSVERSIONINFO_FIELD(name, type) ASSERT_SUCCESS(PRINT_FIELD_ ## type(name, osvi))

    		if (gotOsvi)
    			{
    			sprintf(buffer, "%d.%d.%d", osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber);
    			ASSERT_SUCCESS(output->printString("OS Version", buffer));

    			ASSERT_SUCCESS(PRINT_FIELD_String(szCSDVersion, osvi));

    			OSVERSIONINFOEX osviex;
    			osviex.dwOSVersionInfoSize = sizeof(osviex);
    			if (GetVersionEx((OSVERSIONINFO *)&osviex) != 0)
    				{
    				sprintf(buffer, "%d.%d", osviex.wServicePackMajor, osviex.wServicePackMinor);
    				ASSERT_SUCCESS(output->printString("Service Pack Version", buffer));
    				}
    			}

    		RETURN(output->endBody());

    EXIT:
    		return returnValue;
    		}
    	};

    ///////////////////////////////////////////////////////////////////////////
    //
    //
    // direct 3d 8
    //
    //

    static dx8DiagnosticsNameValue enumdwPlatformId[] =
    	{
    	dx8DiagnosticsNameValue(VER_PLATFORM_WIN32s)
    	dx8DiagnosticsNameValue(VER_PLATFORM_WIN32_WINDOWS)
    	dx8DiagnosticsNameValue(VER_PLATFORM_WIN32_NT)
    	{ NULL, 0}
    	};

    static dx8DiagnosticsNameValue enumDeviceType[] =
    	{
        dx8DiagnosticsNameValue(D3DDEVTYPE_HAL)
        dx8DiagnosticsNameValue(D3DDEVTYPE_REF)
        dx8DiagnosticsNameValue(D3DDEVTYPE_SW)
    	{ NULL, 0}
    	};

    static dx8DiagnosticsNameValue bitfieldCaps[] =
    	{
    	dx8DiagnosticsNameValue(D3DCAPS_READ_SCANLINE)
    	{ NULL, 0}
    	};

    #define D3DCAPS2_CANAUTOGENMIPMAP	0x40000000L	/* DX9: The driver is capable of automatically generating mipmaps. For more information, see Automatic Generation of Mipmaps. */

    static dx8DiagnosticsNameValue bitfieldCaps2[] =
    	{
    	dx8DiagnosticsNameValue(D3DCAPS2_NO2DDURING3DSCENE)
    	dx8DiagnosticsNameValue(D3DCAPS2_FULLSCREENGAMMA)
    	dx8DiagnosticsNameValue(D3DCAPS2_CANRENDERWINDOWED)
    	dx8DiagnosticsNameValue(D3DCAPS2_CANCALIBRATEGAMMA)
    	dx8DiagnosticsNameValue(D3DCAPS2_RESERVED)
    	dx8DiagnosticsNameValue(D3DCAPS2_CANMANAGERESOURCE)
    	dx8DiagnosticsNameValue(D3DCAPS2_DYNAMICTEXTURES)
    	dx8DiagnosticsNameValue_DX9(D3DCAPS2_CANAUTOGENMIPMAP)
    	{ NULL, 0}
    	};

    //
    // Caps3
    //
    #define D3DCAPS3_COPY_TO_VIDMEM					0x00000100L	/* DX9: Device can accelerate a memory copy from system memory to local video memory. This cap guarantees that IDirect3DDevice9::UpdateSurface and IDirect3DDevice9::UpdateTexture calls will be hardware accelerated. If this cap is absent, these calls will succeed but will be slower. */
    #define D3DCAPS3_COPY_TO_SYSTEMMEM				0x00000200L	/* DX9: Device can accelerate a memory copy from local video memory to system memory. This cap guarantees that IDirect3DDevice9::GetRenderTargetData calls will be hardware accelerated. If this cap is absent, this call will succeed but will be slower. */
    #define D3DCAPS3_LINEAR_TO_SRGB_PRESENTATION	0x00000080L /* DX9 */

    static dx8DiagnosticsNameValue bitfieldCaps3[] =
    	{
    	dx8DiagnosticsNameValue(D3DCAPS3_ALPHA_FULLSCREEN_FLIP_OR_DISCARD)
    	dx8DiagnosticsNameValue_DX9(D3DCAPS3_LINEAR_TO_SRGB_PRESENTATION)
    	dx8DiagnosticsNameValue_DX9(D3DCAPS3_COPY_TO_VIDMEM)
    	dx8DiagnosticsNameValue_DX9(D3DCAPS3_COPY_TO_SYSTEMMEM)
    	{ NULL, 0}
    	};

    //
    // PresentationIntervals
    //
    static dx8DiagnosticsNameValue bitfieldPresentationIntervals[] =
    	{
    	dx8DiagnosticsNameValue(D3DPRESENT_INTERVAL_DEFAULT)
    	dx8DiagnosticsNameValue(D3DPRESENT_INTERVAL_ONE)
    	dx8DiagnosticsNameValue(D3DPRESENT_INTERVAL_TWO)
    	dx8DiagnosticsNameValue(D3DPRESENT_INTERVAL_THREE)
    	dx8DiagnosticsNameValue(D3DPRESENT_INTERVAL_FOUR)
    	dx8DiagnosticsNameValue(D3DPRESENT_INTERVAL_IMMEDIATE)
    	{ NULL, 0}
    	};

    //
    // CursorCaps
    //
    // Driver supports HW color cursor in at least hi-res modes(height >=400)
    static dx8DiagnosticsNameValue bitfieldCursorCaps[] =
    	{
    	dx8DiagnosticsNameValue(D3DCURSORCAPS_COLOR)
    	dx8DiagnosticsNameValue(D3DCURSORCAPS_LOWRES)
    	{ NULL, 0}
    	};

    //
    // DevCaps
    static dx8DiagnosticsNameValue bitfieldDevCaps[] =
    	{
    	dx8DiagnosticsNameValue(D3DDEVCAPS_EXECUTESYSTEMMEMORY)
    	dx8DiagnosticsNameValue(D3DDEVCAPS_EXECUTEVIDEOMEMORY)
    	dx8DiagnosticsNameValue(D3DDEVCAPS_TLVERTEXSYSTEMMEMORY)
    	dx8DiagnosticsNameValue(D3DDEVCAPS_TLVERTEXVIDEOMEMORY)
    	dx8DiagnosticsNameValue(D3DDEVCAPS_TEXTURESYSTEMMEMORY)
    	dx8DiagnosticsNameValue(D3DDEVCAPS_TEXTUREVIDEOMEMORY)
    	dx8DiagnosticsNameValue(D3DDEVCAPS_DRAWPRIMTLVERTEX)
    	dx8DiagnosticsNameValue(D3DDEVCAPS_CANRENDERAFTERFLIP)
    	dx8DiagnosticsNameValue(D3DDEVCAPS_TEXTURENONLOCALVIDMEM)
    	dx8DiagnosticsNameValue(D3DDEVCAPS_DRAWPRIMITIVES2)
    	dx8DiagnosticsNameValue(D3DDEVCAPS_SEPARATETEXTUREMEMORIES)
    	dx8DiagnosticsNameValue(D3DDEVCAPS_DRAWPRIMITIVES2EX)
    	dx8DiagnosticsNameValue(D3DDEVCAPS_HWTRANSFORMANDLIGHT)
    	dx8DiagnosticsNameValue(D3DDEVCAPS_CANBLTSYSTONONLOCAL)
    	dx8DiagnosticsNameValue(D3DDEVCAPS_HWRASTERIZATION)
    	dx8DiagnosticsNameValue(D3DDEVCAPS_PUREDEVICE)
    	dx8DiagnosticsNameValue(D3DDEVCAPS_QUINTICRTPATCHES)
    	dx8DiagnosticsNameValue(D3DDEVCAPS_RTPATCHES)
    	dx8DiagnosticsNameValue(D3DDEVCAPS_RTPATCHHANDLEZERO)
    	dx8DiagnosticsNameValue(D3DDEVCAPS_NPATCHES)
    	{ NULL, 0}
    	};

    //
    // PrimitiveMiscCaps
    //

    // old
    #define D3DPMISCCAPS_MASKPLANES					0x00000001L
    #define D3DPMISCCAPS_CONFORMANT					0x00000008L

    // dx9
    #define D3DPMISCCAPS_INDEPENDENTWRITEMASKS		0x00004000L	/* DX9: Device supports independent write masks for multiple element textures or multiple render targets. */
    #define D3DPMISCCAPS_PERSTAGECONSTANT			0x00008000L	/* DX9: Device supports per-stage constants. See D3DTSS_CONSTANT in D3DTEXTURESTAGESTATETYPE. */
    #define D3DPMISCCAPS_FOGANDSPECULARALPHA		0x00010000L	/* DX9: Device supports separate fog and specular alpha. Many devices use the specular alpha channel to store the fog factor. */
    #define D3DPMISCCAPS_SEPARATEALPHABLEND			0x00020000L	/* DX9: Device supports separate blend settings for the alpha channel. */
    #define D3DPMISCCAPS_MRTINDEPENDENTBITDEPTHS	0x00040000L	/* DX9: Device supports different bit depths for multiple render targets. */
    #define D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING	0x00080000L	/* DX9: Device supports post-pixel shader operations for multiple render targets. */
    #define D3DPMISCCAPS_FOGVERTEXCLAMPED			0x00100000L

    static dx8DiagnosticsNameValue bitfieldPrimitiveMiscCaps[] =
    	{
    	dx8DiagnosticsNameValue_OLD(D3DPMISCCAPS_MASKPLANES)
    	dx8DiagnosticsNameValue(D3DPMISCCAPS_MASKZ)
    	dx8DiagnosticsNameValue(D3DPMISCCAPS_LINEPATTERNREP)
    	dx8DiagnosticsNameValue_OLD(D3DPMISCCAPS_CONFORMANT)
    	dx8DiagnosticsNameValue(D3DPMISCCAPS_CULLNONE)
    	dx8DiagnosticsNameValue(D3DPMISCCAPS_CULLCW)
    	dx8DiagnosticsNameValue(D3DPMISCCAPS_CULLCCW)
    	dx8DiagnosticsNameValue(D3DPMISCCAPS_COLORWRITEENABLE)
    	dx8DiagnosticsNameValue(D3DPMISCCAPS_CLIPPLANESCALEDPOINTS)
    	dx8DiagnosticsNameValue(D3DPMISCCAPS_CLIPTLVERTS)
    	dx8DiagnosticsNameValue(D3DPMISCCAPS_TSSARGTEMP)
    	dx8DiagnosticsNameValue(D3DPMISCCAPS_BLENDOP)
    	dx8DiagnosticsNameValue(D3DPMISCCAPS_NULLREFERENCE)
    	dx8DiagnosticsNameValue_DX9(D3DPMISCCAPS_INDEPENDENTWRITEMASKS)
    	dx8DiagnosticsNameValue_DX9(D3DPMISCCAPS_PERSTAGECONSTANT)
    	dx8DiagnosticsNameValue_DX9(D3DPMISCCAPS_FOGANDSPECULARALPHA)
    	dx8DiagnosticsNameValue_DX9(D3DPMISCCAPS_SEPARATEALPHABLEND)
    	dx8DiagnosticsNameValue_DX9(D3DPMISCCAPS_MRTINDEPENDENTBITDEPTHS)
    	dx8DiagnosticsNameValue_DX9(D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING)
    	dx8DiagnosticsNameValue_DX9(D3DPMISCCAPS_FOGVERTEXCLAMPED)
    	{ NULL, 0}
    	};

    //
    // LineCaps
    //
    static dx8DiagnosticsNameValue bitfieldLineCaps[] =
    	{
    	dx8DiagnosticsNameValue(D3DLINECAPS_TEXTURE)
    	dx8DiagnosticsNameValue(D3DLINECAPS_ZTEST)
    	dx8DiagnosticsNameValue(D3DLINECAPS_BLEND)
    	dx8DiagnosticsNameValue(D3DLINECAPS_ALPHACMP)
    	dx8DiagnosticsNameValue(D3DLINECAPS_FOG)
    	{ NULL, 0}
    	};

    //
    // RasterCaps
    //

    // old
    #define D3DPRASTERCAPS_ROP2                     0x00000002L
    #define D3DPRASTERCAPS_XOR                      0x00000004L
    #define D3DPRASTERCAPS_SUBPIXEL                 0x00000020L
    #define D3DPRASTERCAPS_SUBPIXELX                0x00000040L
    #define D3DPRASTERCAPS_STIPPLE                  0x00000200L
    #define D3DPRASTERCAPS_ANTIALIASSORTDEPENDENT   0x00000400L
    #define D3DPRASTERCAPS_ANTIALIASSORTINDEPENDENT 0x00000800L

    // dx9
    #define D3DPRASTERCAPS_TRANSLUCENTSORTINDEPENDENT   0x00080000L
    #define D3DPRASTERCAPS_SCISSORTEST            0x01000000L
    #define D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS    0x02000000L
    #define D3DPRASTERCAPS_DEPTHBIAS              0x04000000L
    #define D3DPRASTERCAPS_MULTISAMPLE_TOGGLE     0x08000000L

    static dx8DiagnosticsNameValue bitfieldRasterCaps[] =
    	{
    	dx8DiagnosticsNameValue(D3DPRASTERCAPS_DITHER)
    	dx8DiagnosticsNameValue_OLD(D3DPRASTERCAPS_ROP2)
    	dx8DiagnosticsNameValue_OLD(D3DPRASTERCAPS_XOR)
    	dx8DiagnosticsNameValue(D3DPRASTERCAPS_PAT)
    	dx8DiagnosticsNameValue(D3DPRASTERCAPS_ZTEST)
    	dx8DiagnosticsNameValue_OLD(D3DPRASTERCAPS_SUBPIXEL)
    	dx8DiagnosticsNameValue_OLD(D3DPRASTERCAPS_SUBPIXELX)
    	dx8DiagnosticsNameValue(D3DPRASTERCAPS_FOGVERTEX)
    	dx8DiagnosticsNameValue(D3DPRASTERCAPS_FOGTABLE)
    	dx8DiagnosticsNameValue_OLD(D3DPRASTERCAPS_STIPPLE)
    	dx8DiagnosticsNameValue_OLD(D3DPRASTERCAPS_ANTIALIASSORTDEPENDENT)
    	dx8DiagnosticsNameValue_OLD(D3DPRASTERCAPS_ANTIALIASSORTINDEPENDENT)
    	dx8DiagnosticsNameValue(D3DPRASTERCAPS_ANTIALIASEDGES)
    	dx8DiagnosticsNameValue(D3DPRASTERCAPS_MIPMAPLODBIAS)
    	dx8DiagnosticsNameValue(D3DPRASTERCAPS_ZBIAS)
    	dx8DiagnosticsNameValue(D3DPRASTERCAPS_ZBUFFERLESSHSR)
    	dx8DiagnosticsNameValue(D3DPRASTERCAPS_FOGRANGE)
    	dx8DiagnosticsNameValue(D3DPRASTERCAPS_ANISOTROPY)
    	dx8DiagnosticsNameValue(D3DPRASTERCAPS_WBUFFER)
    	dx8DiagnosticsNameValue_DX9(D3DPRASTERCAPS_TRANSLUCENTSORTINDEPENDENT)
    	dx8DiagnosticsNameValue(D3DPRASTERCAPS_WFOG)
    	dx8DiagnosticsNameValue(D3DPRASTERCAPS_ZFOG)
    	dx8DiagnosticsNameValue(D3DPRASTERCAPS_COLORPERSPECTIVE)
    	dx8DiagnosticsNameValue(D3DPRASTERCAPS_STRETCHBLTMULTISAMPLE)
    	dx8DiagnosticsNameValue_DX9(D3DPRASTERCAPS_SCISSORTEST)
    	dx8DiagnosticsNameValue_DX9(D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS)
    	dx8DiagnosticsNameValue_DX9(D3DPRASTERCAPS_DEPTHBIAS)
    	dx8DiagnosticsNameValue_DX9(D3DPRASTERCAPS_MULTISAMPLE_TOGGLE)
    	{ NULL, 0}
    	};

    //
    // ZCmpCaps, AlphaCmpCaps
    //
    static dx8DiagnosticsNameValue bitfieldZCmpCaps[] =
    	{
    	dx8DiagnosticsNameValue(D3DPCMPCAPS_NEVER)
    	dx8DiagnosticsNameValue(D3DPCMPCAPS_LESS)
    	dx8DiagnosticsNameValue(D3DPCMPCAPS_EQUAL)
    	dx8DiagnosticsNameValue(D3DPCMPCAPS_LESSEQUAL)
    	dx8DiagnosticsNameValue(D3DPCMPCAPS_GREATER)
    	dx8DiagnosticsNameValue(D3DPCMPCAPS_NOTEQUAL)
    	dx8DiagnosticsNameValue(D3DPCMPCAPS_GREATEREQUAL)
    	dx8DiagnosticsNameValue(D3DPCMPCAPS_ALWAYS)
    	{ NULL, 0}
    	};

    static dx8DiagnosticsNameValue *bitfieldAlphaCmpCaps = bitfieldZCmpCaps;

    //
    // SourceBlendCaps, DestBlendCaps
    //

    #define D3DPBLENDCAPS_BLENDFACTOR       0x00002000L /* DX9: Supports both D3DBLEND_BLENDFACTOR and D3DBLEND_INVBLENDFACTOR */

    static dx8DiagnosticsNameValue bitfieldSrcBlendCaps[] =
    	{
    	dx8DiagnosticsNameValue(D3DPBLENDCAPS_ZERO)
    	dx8DiagnosticsNameValue(D3DPBLENDCAPS_ONE)
    	dx8DiagnosticsNameValue(D3DPBLENDCAPS_SRCCOLOR)
    	dx8DiagnosticsNameValue(D3DPBLENDCAPS_INVSRCCOLOR)
    	dx8DiagnosticsNameValue(D3DPBLENDCAPS_SRCALPHA)
    	dx8DiagnosticsNameValue(D3DPBLENDCAPS_INVSRCALPHA)
    	dx8DiagnosticsNameValue(D3DPBLENDCAPS_DESTALPHA)
    	dx8DiagnosticsNameValue(D3DPBLENDCAPS_INVDESTALPHA)
    	dx8DiagnosticsNameValue(D3DPBLENDCAPS_DESTCOLOR)
    	dx8DiagnosticsNameValue(D3DPBLENDCAPS_INVDESTCOLOR)
    	dx8DiagnosticsNameValue(D3DPBLENDCAPS_SRCALPHASAT)
    	dx8DiagnosticsNameValue(D3DPBLENDCAPS_BOTHSRCALPHA)
    	dx8DiagnosticsNameValue(D3DPBLENDCAPS_BOTHINVSRCALPHA)
    	dx8DiagnosticsNameValue_DX9(D3DPBLENDCAPS_BLENDFACTOR)
    	{ NULL, 0}
    	};

    static dx8DiagnosticsNameValue *bitfieldDestBlendCaps = bitfieldSrcBlendCaps;

    //
    // ShadeCaps
    //
    static dx8DiagnosticsNameValue bitfieldShadeCaps[] =
    	{
    	dx8DiagnosticsNameValue(D3DPSHADECAPS_COLORGOURAUDRGB)
    	dx8DiagnosticsNameValue(D3DPSHADECAPS_SPECULARGOURAUDRGB)
    	dx8DiagnosticsNameValue(D3DPSHADECAPS_ALPHAGOURAUDBLEND)
    	dx8DiagnosticsNameValue(D3DPSHADECAPS_FOGGOURAUD)
    	{ NULL, 0}
    	};

    //
    // TextureCaps
    //
    #define D3DPTEXTURECAPS_NOPROJECTEDBUMPENV  0x00200000L /* DX9: Device does not support projected bump env lookup operation in programmable and fixed function pixel shaders */
    static dx8DiagnosticsNameValue bitfieldTextureCaps[] =
    	{
    	dx8DiagnosticsNameValue(D3DPTEXTURECAPS_PERSPECTIVE)
    	dx8DiagnosticsNameValue(D3DPTEXTURECAPS_POW2)
    	dx8DiagnosticsNameValue(D3DPTEXTURECAPS_ALPHA)
    	dx8DiagnosticsNameValue(D3DPTEXTURECAPS_SQUAREONLY)
    	dx8DiagnosticsNameValue(D3DPTEXTURECAPS_TEXREPEATNOTSCALEDBYSIZE)
    	dx8DiagnosticsNameValue(D3DPTEXTURECAPS_ALPHAPALETTE)
    	dx8DiagnosticsNameValue(D3DPTEXTURECAPS_NONPOW2CONDITIONAL)
    	dx8DiagnosticsNameValue(D3DPTEXTURECAPS_PROJECTED)
    	dx8DiagnosticsNameValue(D3DPTEXTURECAPS_CUBEMAP)
    	dx8DiagnosticsNameValue(D3DPTEXTURECAPS_VOLUMEMAP)
    	dx8DiagnosticsNameValue(D3DPTEXTURECAPS_MIPMAP)
    	dx8DiagnosticsNameValue(D3DPTEXTURECAPS_MIPVOLUMEMAP)
    	dx8DiagnosticsNameValue(D3DPTEXTURECAPS_MIPCUBEMAP)
    	dx8DiagnosticsNameValue(D3DPTEXTURECAPS_CUBEMAP_POW2)
    	dx8DiagnosticsNameValue(D3DPTEXTURECAPS_VOLUMEMAP_POW2)
    	dx8DiagnosticsNameValue_DX9(D3DPTEXTURECAPS_NOPROJECTEDBUMPENV)
    	{ NULL, 0}
    	};

    //
    // TextureFilterCaps
    //

    // old
    #define D3DPTFILTERCAPS_NEAREST         0x00000001L
    #define D3DPTFILTERCAPS_LINEAR          0x00000002L
    #define D3DPTFILTERCAPS_MIPNEAREST      0x00000004L
    #define D3DPTFILTERCAPS_MIPLINEAR       0x00000008L
    #define D3DPTFILTERCAPS_LINEARMIPNEAREST 0x00000010L
    #define D3DPTFILTERCAPS_LINEARMIPLINEAR 0x00000020L

    // dx9
    #define D3DPTFILTERCAPS_MINFPYRAMIDALQUAD   0x00000800L
    #define D3DPTFILTERCAPS_MINFGAUSSIANQUAD    0x00001000L
    #define D3DPTFILTERCAPS_MAGFPYRAMIDALQUAD   0x08000000L
    #define D3DPTFILTERCAPS_MAGFGAUSSIANQUAD    0x10000000L

    static dx8DiagnosticsNameValue bitfieldTextureFilterCaps[] =
    	{
    	dx8DiagnosticsNameValue_OLD(D3DPTFILTERCAPS_NEAREST)
    	dx8DiagnosticsNameValue_OLD(D3DPTFILTERCAPS_LINEAR)
    	dx8DiagnosticsNameValue_OLD(D3DPTFILTERCAPS_MIPNEAREST)
    	dx8DiagnosticsNameValue_OLD(D3DPTFILTERCAPS_MIPLINEAR)
    	dx8DiagnosticsNameValue_OLD(D3DPTFILTERCAPS_LINEARMIPNEAREST)
    	dx8DiagnosticsNameValue_OLD(D3DPTFILTERCAPS_LINEARMIPLINEAR)
    	dx8DiagnosticsNameValue(D3DPTFILTERCAPS_MINFPOINT)
    	dx8DiagnosticsNameValue(D3DPTFILTERCAPS_MINFLINEAR)
    	dx8DiagnosticsNameValue(D3DPTFILTERCAPS_MINFANISOTROPIC)
    	dx8DiagnosticsNameValue(D3DPTFILTERCAPS_MIPFPOINT)
    	dx8DiagnosticsNameValue(D3DPTFILTERCAPS_MIPFLINEAR)
    	dx8DiagnosticsNameValue_DX9(D3DPTFILTERCAPS_MINFPYRAMIDALQUAD)
    	dx8DiagnosticsNameValue_DX9(D3DPTFILTERCAPS_MINFGAUSSIANQUAD)
    	dx8DiagnosticsNameValue(D3DPTFILTERCAPS_MAGFPOINT)
    	dx8DiagnosticsNameValue(D3DPTFILTERCAPS_MAGFLINEAR)
    	dx8DiagnosticsNameValue(D3DPTFILTERCAPS_MAGFANISOTROPIC)
    	dx8DiagnosticsNameValue(D3DPTFILTERCAPS_MAGFAFLATCUBIC)
    	dx8DiagnosticsNameValue(D3DPTFILTERCAPS_MAGFGAUSSIANCUBIC)
    	dx8DiagnosticsNameValue_DX9(D3DPTFILTERCAPS_MAGFPYRAMIDALQUAD)
    	dx8DiagnosticsNameValue_DX9(D3DPTFILTERCAPS_MAGFGAUSSIANQUAD)
    	{ NULL, 0}
    	};

    static dx8DiagnosticsNameValue *bitfieldCubeTextureFilterCaps = bitfieldTextureFilterCaps;
    static dx8DiagnosticsNameValue *bitfieldVolumeTextureFilterCaps = bitfieldTextureFilterCaps;

    //
    // TextureAddressCaps
    //
    static dx8DiagnosticsNameValue bitfieldTextureAddressCaps[] =
    	{
    	dx8DiagnosticsNameValue(D3DPTADDRESSCAPS_WRAP)
    	dx8DiagnosticsNameValue(D3DPTADDRESSCAPS_MIRROR)
    	dx8DiagnosticsNameValue(D3DPTADDRESSCAPS_CLAMP)
    	dx8DiagnosticsNameValue(D3DPTADDRESSCAPS_BORDER)
    	dx8DiagnosticsNameValue(D3DPTADDRESSCAPS_INDEPENDENTUV)
    	dx8DiagnosticsNameValue(D3DPTADDRESSCAPS_MIRRORONCE)
    	{ NULL, 0}
    	};

    static dx8DiagnosticsNameValue *bitfieldVolumeTextureAddressCaps = bitfieldTextureAddressCaps;

    //
    // StencilCaps
    //

    // dx9
    #define D3DSTENCILCAPS_TWOSIDED         0x00000100L
    static dx8DiagnosticsNameValue bitfieldStencilCaps[] =
    	{
    	dx8DiagnosticsNameValue(D3DSTENCILCAPS_KEEP)
    	dx8DiagnosticsNameValue(D3DSTENCILCAPS_ZERO)
    	dx8DiagnosticsNameValue(D3DSTENCILCAPS_REPLACE)
    	dx8DiagnosticsNameValue(D3DSTENCILCAPS_INCRSAT)
    	dx8DiagnosticsNameValue(D3DSTENCILCAPS_DECRSAT)
    	dx8DiagnosticsNameValue(D3DSTENCILCAPS_INVERT)
    	dx8DiagnosticsNameValue(D3DSTENCILCAPS_INCR)
    	dx8DiagnosticsNameValue(D3DSTENCILCAPS_DECR)
    	dx8DiagnosticsNameValue(D3DSTENCILCAPS_TWOSIDED)
    	{ NULL, 0}
    	};

    //
    // TextureOpCaps
    //
    static dx8DiagnosticsNameValue bitfieldTextureOpCaps[] =
    	{
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_DISABLE)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_SELECTARG1)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_SELECTARG2)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_MODULATE)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_MODULATE2X)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_MODULATE4X)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_ADD)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_ADDSIGNED)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_ADDSIGNED2X)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_SUBTRACT)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_ADDSMOOTH)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_BLENDDIFFUSEALPHA)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_BLENDTEXTUREALPHA)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_BLENDFACTORALPHA)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_BLENDTEXTUREALPHAPM)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_BLENDCURRENTALPHA)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_PREMODULATE)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_MODULATEALPHA_ADDCOLOR)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_MODULATECOLOR_ADDALPHA)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_MODULATEINVALPHA_ADDCOLOR)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_MODULATEINVCOLOR_ADDALPHA)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_BUMPENVMAP)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_BUMPENVMAPLUMINANCE)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_DOTPRODUCT3)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_MULTIPLYADD)
    	dx8DiagnosticsNameValue(D3DTEXOPCAPS_LERP)
    	{ NULL, 0}
    	};

    //
    // FVFCaps
    //
    static dx8DiagnosticsNameValue bitfieldFVFCaps[] =
    	{
    	dx8DiagnosticsNameValue(D3DFVFCAPS_DONOTSTRIPELEMENTS)
    	dx8DiagnosticsNameValue(D3DFVFCAPS_PSIZE)
    	{ NULL, 0}
    	};

    //
    // VertexProcessingCaps
    //

    // dx9
    #define D3DVTXPCAPS_TEXGEN_SPHEREMAP    0x00000100L /* DX9: device supports D3DTSS_TCI_SPHEREMAP */
    #define D3DVTXPCAPS_NO_TEXGEN_NONLOCALVIEWER   0x00000200L /* DX9: device does not support TexGen in non-local viewer mode */

    static dx8DiagnosticsNameValue bitfieldVertexProcessingCaps[] =
    	{
    	dx8DiagnosticsNameValue(D3DVTXPCAPS_TEXGEN)
    	dx8DiagnosticsNameValue(D3DVTXPCAPS_MATERIALSOURCE7)
    	dx8DiagnosticsNameValue(D3DVTXPCAPS_DIRECTIONALLIGHTS)
    	dx8DiagnosticsNameValue(D3DVTXPCAPS_POSITIONALLIGHTS)
    	dx8DiagnosticsNameValue(D3DVTXPCAPS_LOCALVIEWER)
    	dx8DiagnosticsNameValue(D3DVTXPCAPS_TWEENING)
    	dx8DiagnosticsNameValue(D3DVTXPCAPS_NO_VSDT_UBYTE4)
    	dx8DiagnosticsNameValue_DX9(D3DVTXPCAPS_TEXGEN_SPHEREMAP)
    	dx8DiagnosticsNameValue_DX9(D3DVTXPCAPS_NO_TEXGEN_NONLOCALVIEWER)
    	{ NULL, 0}
    	};

    // new D3DFORMATs for DX9
    #define D3DFMT_A8B8G8R8             32
    #define	D3DFMT_X8B8G8R8             33
    #define	D3DFMT_A2R10G10B10          35
    #define	D3DFMT_A16B16G16R16         36
    #define D3DFMT_R8G8_B8G8            MAKEFOURCC('R', 'G', 'B', 'G')
    #define D3DFMT_G8R8_G8B8            MAKEFOURCC('G', 'R', 'G', 'B')
    #define D3DFMT_D32F_LOCKABLE        82
    #define D3DFMT_D24FS8               83
    #define D3DFMT_L16                  81
    #define D3DFMT_Q16W16V16U16         110
    #define D3DFMT_MULTI2_ARGB8         MAKEFOURCC('M','E','T','1')
    #define D3DFMT_R16F                 111
    #define D3DFMT_G16R16F              112
    #define D3DFMT_A16B16G16R16F        113
    #define D3DFMT_R32F                 114
    #define D3DFMT_G32R32F              115
    #define D3DFMT_A32B32G32R32F        116
    #define D3DFMT_CxV8U8               117

    static dx8DiagnosticsNameValue enumSurfaceFormats[] =
    	{
    	dx8DiagnosticsNameValue(D3DFMT_R8G8B8)
    	dx8DiagnosticsNameValue(D3DFMT_A8R8G8B8)
    	dx8DiagnosticsNameValue(D3DFMT_X8R8G8B8)
    	dx8DiagnosticsNameValue(D3DFMT_R5G6B5)
    	dx8DiagnosticsNameValue(D3DFMT_X1R5G5B5)
    	dx8DiagnosticsNameValue(D3DFMT_A1R5G5B5)
    	dx8DiagnosticsNameValue(D3DFMT_A4R4G4B4)
    	dx8DiagnosticsNameValue(D3DFMT_R3G3B2)
    	dx8DiagnosticsNameValue(D3DFMT_A8)
    	dx8DiagnosticsNameValue(D3DFMT_A8R3G3B2)
    	dx8DiagnosticsNameValue(D3DFMT_X4R4G4B4)
    	dx8DiagnosticsNameValue(D3DFMT_A2B10G10R10)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_A8B8G8R8)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_X8B8G8R8)
    	dx8DiagnosticsNameValue(D3DFMT_G16R16)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_A2R10G10B10)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_A16B16G16R16)
    	dx8DiagnosticsNameValue(D3DFMT_A8P8)
    	dx8DiagnosticsNameValue(D3DFMT_P8)
    	dx8DiagnosticsNameValue(D3DFMT_L8)
    	dx8DiagnosticsNameValue(D3DFMT_A8L8)
    	dx8DiagnosticsNameValue(D3DFMT_A4L4)
    	dx8DiagnosticsNameValue(D3DFMT_V8U8)
    	dx8DiagnosticsNameValue(D3DFMT_L6V5U5)
    	dx8DiagnosticsNameValue(D3DFMT_X8L8V8U8)
    	dx8DiagnosticsNameValue(D3DFMT_Q8W8V8U8)
    	dx8DiagnosticsNameValue(D3DFMT_V16U16)
    	dx8DiagnosticsNameValue(D3DFMT_A2W10V10U10)
    	dx8DiagnosticsNameValue(D3DFMT_UYVY)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_R8G8_B8G8)
    	dx8DiagnosticsNameValue(D3DFMT_YUY2)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_G8R8_G8B8)
    	dx8DiagnosticsNameValue(D3DFMT_DXT1)
    	dx8DiagnosticsNameValue(D3DFMT_DXT2)
    	dx8DiagnosticsNameValue(D3DFMT_DXT3)
    	dx8DiagnosticsNameValue(D3DFMT_DXT4)
    	dx8DiagnosticsNameValue(D3DFMT_DXT5)
    	dx8DiagnosticsNameValue(D3DFMT_D16_LOCKABLE)
    	dx8DiagnosticsNameValue(D3DFMT_D32)
    	dx8DiagnosticsNameValue(D3DFMT_D15S1)
    	dx8DiagnosticsNameValue(D3DFMT_D24S8)
    	dx8DiagnosticsNameValue(D3DFMT_D24X8)
    	dx8DiagnosticsNameValue(D3DFMT_D24X4S4)
    	dx8DiagnosticsNameValue(D3DFMT_D16)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_D32F_LOCKABLE)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_D24FS8)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_L16)
    	dx8DiagnosticsNameValue(D3DFMT_VERTEXDATA)
    	dx8DiagnosticsNameValue(D3DFMT_INDEX16)
    	dx8DiagnosticsNameValue(D3DFMT_INDEX32)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_Q16W16V16U16)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_MULTI2_ARGB8)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_R16F)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_G16R16F)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_A16B16G16R16F)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_R32F)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_G32R32F)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_A32B32G32R32F)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_CxV8U8)
    	{ NULL, 0}
    	};

    static dx8DiagnosticsNameValue enumUncompressedSurfaceFormats[] =
    	{
    	dx8DiagnosticsNameValue(D3DFMT_R8G8B8)
    	dx8DiagnosticsNameValue(D3DFMT_A8R8G8B8)
    	dx8DiagnosticsNameValue(D3DFMT_X8R8G8B8)
    	dx8DiagnosticsNameValue(D3DFMT_R5G6B5)
    	dx8DiagnosticsNameValue(D3DFMT_X1R5G5B5)
    	dx8DiagnosticsNameValue(D3DFMT_A1R5G5B5)
    	dx8DiagnosticsNameValue(D3DFMT_A4R4G4B4)
    	dx8DiagnosticsNameValue(D3DFMT_R3G3B2)
    	dx8DiagnosticsNameValue(D3DFMT_A8)
    	dx8DiagnosticsNameValue(D3DFMT_A8R3G3B2)
    	dx8DiagnosticsNameValue(D3DFMT_X4R4G4B4)
    	dx8DiagnosticsNameValue(D3DFMT_A2B10G10R10)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_A8B8G8R8)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_X8B8G8R8)
    	dx8DiagnosticsNameValue(D3DFMT_G16R16)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_A2R10G10B10)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_A16B16G16R16)
    	dx8DiagnosticsNameValue(D3DFMT_A8P8)
    	dx8DiagnosticsNameValue(D3DFMT_P8)
    	dx8DiagnosticsNameValue(D3DFMT_L8)
    	dx8DiagnosticsNameValue(D3DFMT_A8L8)
    	dx8DiagnosticsNameValue(D3DFMT_A4L4)
    	dx8DiagnosticsNameValue(D3DFMT_V8U8)
    	dx8DiagnosticsNameValue(D3DFMT_L6V5U5)
    	dx8DiagnosticsNameValue(D3DFMT_X8L8V8U8)
    	dx8DiagnosticsNameValue(D3DFMT_Q8W8V8U8)
    	dx8DiagnosticsNameValue(D3DFMT_V16U16)
    	dx8DiagnosticsNameValue(D3DFMT_A2W10V10U10)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_Q16W16V16U16)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_MULTI2_ARGB8)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_R16F)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_G16R16F)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_A16B16G16R16F)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_R32F)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_G32R32F)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_A32B32G32R32F)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_CxV8U8)
    	{ NULL, 0}
    	};

    static dx8DiagnosticsNameValue enumCompressedSurfaceFormats[] =
    	{
    	dx8DiagnosticsNameValue(D3DFMT_UYVY)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_R8G8_B8G8)
    	dx8DiagnosticsNameValue(D3DFMT_YUY2)
    	dx8DiagnosticsNameValue_DX9(D3DFMT_G8R8_G8B8)
    	dx8DiagnosticsNameValue(D3DFMT_DXT1)
    	dx8DiagnosticsNameValue(D3DFMT_DXT2)
    	dx8DiagnosticsNameValue(D3DFMT_DXT3)
    	dx8DiagnosticsNameValue(D3DFMT_DXT4)
    	dx8DiagnosticsNameValue(D3DFMT_DXT5)
    	{ NULL, 0}
    	};

    class printerD3D8 : public dx8DiagnosticsPrinter
    	{
    	public:

    	bool wantWHQL;
    	IDirect3D8 *d3d;
    	HRESULT d3dStatusCode;

    	int adapterCount;
    	D3DADAPTER_IDENTIFIER8 *identifiers;
    	HRESULT d3dAdapterStatusCode;

    	printerD3D8(bool wantWHQL = false)
    		{
    		this->wantWHQL = wantWHQL;
    		adapterCount = 0;
    		d3dAdapterStatusCode = S_OK;
    		if (!dx8DynamicIsAvailable())
    			{
    			d3d = NULL;
    			identifiers = NULL;
    			d3dStatusCode = dx8DynamicStatus();
    			}
    		else
    			{
    			d3d = dx8DynamicDirect3DCreate8(D3D_SDK_VERSION);
    			if (d3d == NULL)
    				d3dStatusCode = E_FAIL;
    			else
    				{
    				d3dStatusCode = S_OK;
    				adapterCount = d3d->GetAdapterCount();
    				identifiers = (D3DADAPTER_IDENTIFIER8 *)calloc(adapterCount, sizeof(D3DADAPTER_IDENTIFIER8));
    				if (identifiers != NULL)
    					{
    					int i;
    					for (i = 0; i < adapterCount; i++)
    						{
    						d3dAdapterStatusCode = d3d->GetAdapterIdentifier(i, wantWHQL ? D3DENUM_NO_WHQL_LEVEL : 0, identifiers + i);
    						if (d3dAdapterStatusCode != S_OK)
    							break;
    						}
    					}
    				}
    			}
    		}

    	virtual ~printerD3D8(void)
    		{
    		if (d3d != NULL)
    			{
    			d3d->Release();
    			d3d = NULL;
    			}
    		d3dStatusCode = E_FAIL;
    		}

    	virtual HRESULT printTOC(dx8Diagnostics *diagnostics)
    		{
    		HRESULT returnValue = S_OK;

    		dx8DiagnosticsOutput *output = diagnostics->output;

    		if ((adapterCount == 0) || (d3dAdapterStatusCode != S_OK))
    			{
    			RETURN(output->print("<dd><a href=#displayadapters><code><b>display adapters</b></code></a>\n"));
    			}

    		int i;
    		for (i = 0; i < adapterCount; i++)
    			{
    			ASSERT_SUCCESS(output->print("<dd><a href=#displayadapter%d><code><b>display adapter %d</b></code></a><code><b>:</b></code> <b>%s</b>\n", i, i, identifiers[i].Description));
    			}
    EXIT:
    		return returnValue;
    		}

    	static int __cdecl displayModeSorter(const D3DDISPLAYMODE *mode1, const D3DDISPLAYMODE *mode2)
    		{
    		if (mode1->Format != mode2->Format)
    			return (int)mode1->Format - (int)mode2->Format;
    		if (mode1->Width != mode2->Width)
    			return (int)mode1->Width - (int)mode2->Width;
    		if (mode1->Height != mode2->Height)
    			return (int)mode1->Height - (int)mode2->Height;
    		return (int)mode1->RefreshRate - (int)mode2->RefreshRate;
    		}

    	virtual HRESULT printBody(dx8Diagnostics *diagnostics)
    		{
    		HRESULT returnValue = S_OK;

    		dx8DiagnosticsOutput *output = diagnostics->output;

    		if ((adapterCount == 0) || (d3dAdapterStatusCode != S_OK))
    			{
    			ASSERT_SUCCESS(output->printHeading("display adapters"));
    			ASSERT_SUCCESS(output->startBody("Direct3D version 8 could not be started!"));
    			ASSERT_SUCCESS(output->printInteger("<code>Direct3DCreate8()</code> status code", d3dStatusCode));
    			ASSERT_SUCCESS(output->printInteger("<code>GetAdapterIdentifier()</code> status code", d3dAdapterStatusCode));
    			RETURN(output->endBody());
    			}

    		int i;
    		for (i = 0; i < adapterCount; i++)
    			{
    	#define PRINT_IDENTIFIER_FIELD(name, type) ASSERT_SUCCESS(PRINT_FIELD_ ## type(name, identifiers[i]))

    			char buffer[256];
    			sprintf(buffer, "display adapter %d", i);
    			ASSERT_SUCCESS(output->printHeading(buffer));
    			ASSERT_SUCCESS(output->startBody(identifiers[i].Description));

    			PRINT_IDENTIFIER_FIELD(VendorId, Integer);
    			PRINT_IDENTIFIER_FIELD(DeviceId, Integer);
    			PRINT_IDENTIFIER_FIELD(SubSysId, Integer);
    			PRINT_IDENTIFIER_FIELD(Revision, Integer);

    			ASSERT_SUCCESS(output->printSubheading("driver"));

    			PRINT_IDENTIFIER_FIELD(Driver, String);
    			PRINT_IDENTIFIER_FIELD(DriverVersion, Version);
    			PRINT_IDENTIFIER_FIELD(DeviceIdentifier, GUID);
    			if (wantWHQL)
    				{
    				PRINT_IDENTIFIER_FIELD(WHQLLevel, WHQLLevel);
    				}

    			UINT totalModeCount = d3d->GetAdapterModeCount(i);
    			sprintf(buffer, "%d supported modes", totalModeCount);

    			// cache supported display formats for the benefit of compressed textures below
    			UINT supportedFormatCount = 0;
    			D3DFORMAT *supportedFormats = NULL;

    			ASSERT_SUCCESS(output->printSubheading(buffer));
    			if (!totalModeCount)
    				{
    				ASSERT_SUCCESS(output->print("<i>no display modes reported!  woah, man!</i>"));
    				}
    			else
    				{
    				D3DDISPLAYMODE *modes = (D3DDISPLAYMODE *)malloc(totalModeCount * sizeof(D3DDISPLAYMODE));
    				UINT enumeratedModeCount = 0;
    				UINT j;
    				for (j = 0; j < totalModeCount; j++)
    					{
    					if (SUCCEEDED(d3d->EnumAdapterModes(i, j, modes + enumeratedModeCount)))
    						enumeratedModeCount++;
    					}

    				if (enumeratedModeCount == 0)
    					{
    					ASSERT_SUCCESS(output->print("<i>could not enumerate display modes!</i>"));
    					}
    				else
    					{
    					// sort the modes
    					qsort(modes, enumeratedModeCount, sizeof(D3DDISPLAYMODE),
    						(int (__cdecl *)(const void *, const void *))displayModeSorter);

    					bool firstResolutionInFormat;
    					bool firstRefreshRateInResolution;

    					for (j = 0; j < enumeratedModeCount; j++)
    						{
    						D3DDISPLAYMODE *mode = modes + j;
    						D3DDISPLAYMODE *lastmode = modes + j - 1;

    						// changing formats
    						if (!j || (mode->Format != lastmode->Format))
    							{
    							char *stringValue = "unknown?";
    							dx8DiagnosticsNameValue *namevalue = dx8DiagnosticsNameValueFind(enumSurfaceFormats, mode->Format);
    							if (namevalue != NULL)
    								stringValue = namevalue->name;
    							ASSERT_SUCCESS(output->print("%s<font size=+1><code>%s</code></font><blockquote>\n", (!j ? "" : "\n</blockquote>\n"), stringValue));
    							firstResolutionInFormat = true;
    							supportedFormatCount++;
    							}

    						// changing resolutions
    						if (firstResolutionInFormat || (mode->Width != lastmode->Width) || (mode->Height != lastmode->Height))
    							{
    							ASSERT_SUCCESS(output->print("%s<code><b>%s%d x %s%d</b></code>: ", (firstResolutionInFormat ? "" : "<br>\n"), ((mode->Width < 1000) ? " " : ""), mode->Width, ((mode->Height < 1000) ? " " : ""), mode->Height));
    							firstResolutionInFormat = false;
    							firstRefreshRateInResolution = true;
    							}

    						if (mode->RefreshRate != 0)
    							sprintf(buffer, "%dHz", mode->RefreshRate);
    						else
    							strcpy(buffer, "adapter default");
    						ASSERT_SUCCESS(output->print("%s%s", (firstRefreshRateInResolution ? "" : ", "), buffer));
    						firstRefreshRateInResolution = false;
    						}
    					ASSERT_SUCCESS(output->print("</blockquote>\n"));
    					}

    				supportedFormats = (D3DFORMAT *)malloc(sizeof(D3DFORMAT) * supportedFormatCount);
    				D3DFORMAT *trace = supportedFormats;
    				for (j = 0; j < enumeratedModeCount; j++)
    					{
    					D3DDISPLAYMODE *mode = modes + j;
    					D3DDISPLAYMODE *lastmode = modes + j - 1;

    					if (!j || (mode->Format != lastmode->Format))
    						*trace++ = mode->Format;
    					}

    				free(modes);
    				}

    			ASSERT_SUCCESS(output->printSubheading("compressed texture support"));
    			dx8DiagnosticsNameValue *namevalue;
    			int count = 0;
    			ASSERT_SUCCESS(output->print("<dt><b>Supported compressed texture formats:</b><dd><blockquote>\n"));
    			for (namevalue = enumCompressedSurfaceFormats; namevalue->name != NULL; namevalue++)
    				{
    				*buffer = 0;
    				DWORD j;
    				for (j = 0; j < supportedFormatCount; j++)
    					{
    					D3DFORMAT format = supportedFormats[j];
    					if (SUCCEEDED(d3d->CheckDeviceFormat(i, D3DDEVTYPE_HAL, D3DFMT_R5G6B5, 0, D3DRTYPE_TEXTURE, format)))
    						{
    						char *stringValue = "unknown?";
    						dx8DiagnosticsNameValue *namevalue = dx8DiagnosticsNameValueFind(enumSurfaceFormats, format);
    						if (namevalue != NULL)
    							stringValue = namevalue->name;
    						sprintf(buffer + strlen(buffer), " <code>%s</code>", stringValue);
    						}
    					}
    				if (*buffer)
    					{
    					ASSERT_SUCCESS(output->print("%s<code><b>%s</b></code>%s",
    						(count ? "<br>\n" : ""),
    						namevalue->name,
    						buffer
    						));
    					count++;
    					}
    				}
    			if (!count)
    				{
    				ASSERT_SUCCESS(output->print("<i>no compressed texture formats supported!</i>\n"));
    				}

    			ASSERT_SUCCESS(output->print("</blockquote>\n"));
    			free(supportedFormats);

    			ASSERT_SUCCESS(output->printSubheading("capabilities"));
    			#define PRINT_CAPS_FIELD(name, type) ASSERT_SUCCESS(PRINT_FIELD_ ## type(name, caps))
    			D3DCAPS8 caps;
    			memset(&caps, 0, sizeof(caps));
    			HRESULT result = d3d->GetDeviceCaps(i, D3DDEVTYPE_HAL, &caps);
    			if (result != D3D_OK)
    				{
    				ASSERT_SUCCESS(output->printInteger("<code>GetDeviceCaps()</code> status code", result));
    				}
    			else
    				{
    				PRINT_CAPS_FIELD(DeviceType, Enum);
    				PRINT_CAPS_FIELD(AdapterOrdinal, Integer);
    				PRINT_CAPS_FIELD(Caps, Bitfield);
    				PRINT_CAPS_FIELD(Caps2, Bitfield);
    				PRINT_CAPS_FIELD(Caps3, Bitfield);
    				PRINT_CAPS_FIELD(PresentationIntervals, Bitfield);
    				PRINT_CAPS_FIELD(CursorCaps, Bitfield);
    				PRINT_CAPS_FIELD(DevCaps, Bitfield);
    				PRINT_CAPS_FIELD(PrimitiveMiscCaps, Bitfield);
    				PRINT_CAPS_FIELD(RasterCaps, Bitfield);
    				PRINT_CAPS_FIELD(ZCmpCaps, Bitfield);
    				PRINT_CAPS_FIELD(SrcBlendCaps, Bitfield);
    				PRINT_CAPS_FIELD(DestBlendCaps, Bitfield);
    				PRINT_CAPS_FIELD(AlphaCmpCaps, Bitfield);
    				PRINT_CAPS_FIELD(ShadeCaps, Bitfield);
    				PRINT_CAPS_FIELD(TextureCaps, Bitfield);
    				PRINT_CAPS_FIELD(TextureFilterCaps, Bitfield);          // D3DPTFILTERCAPS for IDirect3DTexture8's
    				PRINT_CAPS_FIELD(CubeTextureFilterCaps, Bitfield);      // D3DPTFILTERCAPS for IDirect3DCubeTexture8's
    				PRINT_CAPS_FIELD(VolumeTextureFilterCaps, Bitfield);    // D3DPTFILTERCAPS for IDirect3DVolumeTexture8's
    				PRINT_CAPS_FIELD(TextureAddressCaps, Bitfield);         // D3DPTADDRESSCAPS for IDirect3DTexture8's
    				PRINT_CAPS_FIELD(VolumeTextureAddressCaps, Bitfield);   // D3DPTADDRESSCAPS for IDirect3DVolumeTexture8's
    				PRINT_CAPS_FIELD(LineCaps, Bitfield);                   // D3DLINECAPS
    				PRINT_CAPS_FIELD(MaxTextureWidth, Integer);
    				PRINT_CAPS_FIELD(MaxTextureHeight, Integer);
    				PRINT_CAPS_FIELD(MaxVolumeExtent, Integer);
    				PRINT_CAPS_FIELD(MaxTextureRepeat, Integer);
    				PRINT_CAPS_FIELD(MaxTextureAspectRatio, Integer);
    				PRINT_CAPS_FIELD(MaxAnisotropy, Integer);
    				PRINT_CAPS_FIELD(MaxVertexW, Float);
    				PRINT_CAPS_FIELD(GuardBandLeft, Float);
    				PRINT_CAPS_FIELD(GuardBandTop, Float);
    				PRINT_CAPS_FIELD(GuardBandRight, Float);
    				PRINT_CAPS_FIELD(GuardBandBottom, Float);
    				PRINT_CAPS_FIELD(ExtentsAdjust, Float);
    				PRINT_CAPS_FIELD(StencilCaps, Bitfield);
    				ASSERT_SUCCESS(output->printInteger("FVF Maximum Texture Coordinate Sets", caps.FVFCaps & D3DFVFCAPS_TEXCOORDCOUNTMASK));
    				ASSERT_SUCCESS(output->printBitfield("FVFCaps", caps.FVFCaps & ~D3DFVFCAPS_TEXCOORDCOUNTMASK, bitfieldFVFCaps));
    				PRINT_CAPS_FIELD(TextureOpCaps, Bitfield);
    				PRINT_CAPS_FIELD(MaxTextureBlendStages, Integer);
    				PRINT_CAPS_FIELD(MaxSimultaneousTextures, Integer);
    				PRINT_CAPS_FIELD(VertexProcessingCaps, Bitfield);
    				PRINT_CAPS_FIELD(MaxActiveLights, Integer);
    				PRINT_CAPS_FIELD(MaxUserClipPlanes, Integer);
    				PRINT_CAPS_FIELD(MaxVertexBlendMatrices, Integer);
    				PRINT_CAPS_FIELD(MaxVertexBlendMatrixIndex, Integer);
    				PRINT_CAPS_FIELD(MaxPointSize, Float);
    				PRINT_CAPS_FIELD(MaxPrimitiveCount, Integer);          // max number of primitives per DrawPrimitive call
    				PRINT_CAPS_FIELD(MaxVertexIndex, Integer);
    				PRINT_CAPS_FIELD(MaxStreams, Integer);
    				PRINT_CAPS_FIELD(MaxStreamStride, Integer);            // max stride for SetStreamSource
    				PRINT_CAPS_FIELD(VertexShaderVersion, Integer);
    				PRINT_CAPS_FIELD(MaxVertexShaderConst, Integer);       // number of vertex shader constant registers
    				PRINT_CAPS_FIELD(PixelShaderVersion, Integer);
    				PRINT_CAPS_FIELD(MaxPixelShaderValue, Float);        // max value of pixel shader arithmetic component
    				}
    			ASSERT_SUCCESS(output->endBody());
    			}
    EXIT:
    		return returnValue;
    		}
    	};

    ///////////////////////////////////////////////////////////////////////////
    //
    //
    // direct sound 8
    //
    //

    static dx8DiagnosticsNameValue bitfielddwFlags[] =
    	{
    	dx8DiagnosticsNameValue(DSCAPS_PRIMARYMONO)
    	dx8DiagnosticsNameValue(DSCAPS_PRIMARYSTEREO)
    	dx8DiagnosticsNameValue(DSCAPS_PRIMARY8BIT)
    	dx8DiagnosticsNameValue(DSCAPS_PRIMARY16BIT)
    	dx8DiagnosticsNameValue(DSCAPS_CONTINUOUSRATE)
    	dx8DiagnosticsNameValue(DSCAPS_EMULDRIVER)
    	dx8DiagnosticsNameValue(DSCAPS_CERTIFIED)
    	dx8DiagnosticsNameValue(DSCAPS_SECONDARYMONO)
    	dx8DiagnosticsNameValue(DSCAPS_SECONDARYSTEREO)
    	dx8DiagnosticsNameValue(DSCAPS_SECONDARY8BIT)
    	dx8DiagnosticsNameValue(DSCAPS_SECONDARY16BIT)
    	{ NULL, 0 }
    	};

    static BOOL CALLBACK dSound8CallbackTOC(LPGUID guid, LPCSTR description, LPCSTR module, LPVOID context);
    static BOOL CALLBACK dSound8CallbackBody(LPGUID guid, LPCSTR description, LPCSTR module, LPVOID context);

    class printerDSound8 : public dx8DiagnosticsPrinter
    	{
    	public:

    	DWORD counter;
    	dx8DiagnosticsOutput *output;
    	bool noDevices;

    	printerDSound8(void)
    		{
    		noDevices = false;
    		}

    	virtual HRESULT printTOC(dx8Diagnostics *diagnostics)
    		{
    		this->output = diagnostics->output;

    		if (!dx8DynamicIsAvailable())
    			return output->print("<dd><a href=#soundadapters><code><b>sound adapters</b></code></a>\n");

    		counter = 0;
    		dx8DynamicDirectSoundEnumerateA(dSound8CallbackTOC, this);
    		if (counter == 0)
    			{
    			noDevices = true;
    			return output->print("<dd><a href=#soundadapters><code><b>sound adapters</b></code></a>\n");
    			}
    		return S_OK;
    		}

    	BOOL tocCallback(LPGUID guid, LPCSTR description, LPCSTR module)
    		{
    		output->print("<dd><a href=#soundadapter%d><code><b>sound adapter %d</b></code></a><code><b>:</b></code> <b>%s</b>\n", counter, counter, description);
    		counter++;
    		return TRUE;
    		}

    	virtual HRESULT printBody(dx8Diagnostics *diagnostics)
    		{
    		HRESULT returnValue = S_OK;
    		this->output = diagnostics->output;

    		if (!dx8DynamicIsAvailable())
    			{
    			ASSERT_SUCCESS(output->printHeading("sound adapters"));
    			ASSERT_SUCCESS(output->startBody("DirectSound version 8 could not be started!"));
    			RETURN(output->endBody());
    			}

    		if (noDevices)
    			{
    			ASSERT_SUCCESS(output->printHeading("sound adapters"));
    			ASSERT_SUCCESS(output->startBody("No sound adapters detected"));
    			RETURN(output->endBody());
    			}

    		counter = 0;
    		dx8DynamicDirectSoundEnumerateA(dSound8CallbackBody, this);
    EXIT:
    		return returnValue;
    		}

    	BOOL bodyCallback(LPGUID guid, LPCSTR description, LPCSTR module)
    		{
    		HRESULT returnValue = S_OK;
    		LPDIRECTSOUND8 ds8 = NULL;
    		HRESULT hResult;

    		char buffer[64];
    		sprintf(buffer, "sound adapter %d", counter++);
    		ASSERT_SUCCESS(output->printHeading(buffer));
    		ASSERT_SUCCESS(output->startBody(description));

    		hResult = dx8DynamicDirectSoundCreate8(guid, &ds8, NULL);
    		if (ds8 == NULL)
    			{
    			ASSERT_SUCCESS(output->printInteger("<code>DirectSoundCreate8()</code> status code", hResult));
    			RETURN(output->endBody());
    			}

    		DSCAPS dscaps;
    		memset(&dscaps, 0, sizeof(dscaps));
    		dscaps.dwSize = sizeof(dscaps);
    		ds8->GetCaps(&dscaps);

    		#define PRINT_DSCAPS_FIELD(name, type) ASSERT_SUCCESS(PRINT_FIELD_ ## type(name, dscaps))

    		PRINT_DSCAPS_FIELD(dwFlags, Bitfield);
    		PRINT_DSCAPS_FIELD(dwMinSecondarySampleRate, Integer);
    		PRINT_DSCAPS_FIELD(dwMaxSecondarySampleRate, Integer);
    		PRINT_DSCAPS_FIELD(dwPrimaryBuffers, Integer);
    		PRINT_DSCAPS_FIELD(dwMaxHwMixingAllBuffers, Integer);
    		PRINT_DSCAPS_FIELD(dwMaxHwMixingStaticBuffers, Integer);
    		PRINT_DSCAPS_FIELD(dwMaxHwMixingStreamingBuffers, Integer);
    		PRINT_DSCAPS_FIELD(dwFreeHwMixingAllBuffers, Integer);
    		PRINT_DSCAPS_FIELD(dwFreeHwMixingStaticBuffers, Integer);
    		PRINT_DSCAPS_FIELD(dwFreeHwMixingStreamingBuffers, Integer);
    		PRINT_DSCAPS_FIELD(dwMaxHw3DAllBuffers, Integer);
    		PRINT_DSCAPS_FIELD(dwMaxHw3DStaticBuffers, Integer);
    		PRINT_DSCAPS_FIELD(dwMaxHw3DStreamingBuffers, Integer);
    		PRINT_DSCAPS_FIELD(dwFreeHw3DAllBuffers, Integer);
    		PRINT_DSCAPS_FIELD(dwFreeHw3DStaticBuffers, Integer);
    		PRINT_DSCAPS_FIELD(dwFreeHw3DStreamingBuffers, Integer);
    		PRINT_DSCAPS_FIELD(dwTotalHwMemBytes, Integer);
    		PRINT_DSCAPS_FIELD(dwFreeHwMemBytes, Integer);
    		PRINT_DSCAPS_FIELD(dwMaxContigFreeHwMemBytes, Integer);
    		PRINT_DSCAPS_FIELD(dwUnlockTransferRateHwBuffers, Integer);
    		PRINT_DSCAPS_FIELD(dwPlayCpuOverheadSwBuffers, Integer);

    		RETURN(output->endBody());

    EXIT:
    		if (ds8 != NULL)
    			ds8->Release();
    		return TRUE;
    		}
    	};

    static BOOL CALLBACK dSound8CallbackTOC(LPGUID guid, LPCSTR description, LPCSTR module, LPVOID context)
    	{
    	printerDSound8 *printer = (printerDSound8 *)context;
    	return printer->tocCallback(guid, description, module);
    	}

    static BOOL CALLBACK dSound8CallbackBody(LPGUID guid, LPCSTR description, LPCSTR module, LPVOID context)
    	{
    	printerDSound8 *printer = (printerDSound8 *)context;
    	return printer->bodyCallback(guid, description, module);
    	}

    ///////////////////////////////////////////////////////////////////////////
    //
    //
    // direct input 8
    //
    //

    static dx8DiagnosticsNameValue inputType[] =
    	{
    	dx8DiagnosticsNameValue(DI8DEVTYPE_DEVICE)
    	dx8DiagnosticsNameValue(DI8DEVTYPE_MOUSE)
    	dx8DiagnosticsNameValue(DI8DEVTYPE_KEYBOARD)
    	dx8DiagnosticsNameValue(DI8DEVTYPE_JOYSTICK)
    	dx8DiagnosticsNameValue(DI8DEVTYPE_GAMEPAD)
    	dx8DiagnosticsNameValue(DI8DEVTYPE_DRIVING)
    	dx8DiagnosticsNameValue(DI8DEVTYPE_FLIGHT)
    	dx8DiagnosticsNameValue(DI8DEVTYPE_1STPERSON)
    	dx8DiagnosticsNameValue(DI8DEVTYPE_DEVICECTRL)
    	dx8DiagnosticsNameValue(DI8DEVTYPE_SCREENPOINTER)
    	dx8DiagnosticsNameValue(DI8DEVTYPE_REMOTE)
    	dx8DiagnosticsNameValue(DI8DEVTYPE_SUPPLEMENTAL)
    	{ NULL, 0 }
    	};

    static dx8DiagnosticsNameValue inputSubtypeMouse[] =
    	{
    	dx8DiagnosticsNameValue(DI8DEVTYPEMOUSE_UNKNOWN)
    	dx8DiagnosticsNameValue(DI8DEVTYPEMOUSE_TRADITIONAL)
    	dx8DiagnosticsNameValue(DI8DEVTYPEMOUSE_FINGERSTICK)
    	dx8DiagnosticsNameValue(DI8DEVTYPEMOUSE_TOUCHPAD)
    	dx8DiagnosticsNameValue(DI8DEVTYPEMOUSE_TRACKBALL)
    	dx8DiagnosticsNameValue(DI8DEVTYPEMOUSE_ABSOLUTE)
    	{ NULL, 0 }
    	};

    static dx8DiagnosticsNameValue inputSubtypeKeyboard[] =
    	{
    	dx8DiagnosticsNameValue(DI8DEVTYPEKEYBOARD_UNKNOWN)
    	dx8DiagnosticsNameValue(DI8DEVTYPEKEYBOARD_PCXT)
    	dx8DiagnosticsNameValue(DI8DEVTYPEKEYBOARD_OLIVETTI)
    	dx8DiagnosticsNameValue(DI8DEVTYPEKEYBOARD_PCAT)
    	dx8DiagnosticsNameValue(DI8DEVTYPEKEYBOARD_PCENH)
    	dx8DiagnosticsNameValue(DI8DEVTYPEKEYBOARD_NOKIA1050)
    	dx8DiagnosticsNameValue(DI8DEVTYPEKEYBOARD_NOKIA9140)
    	dx8DiagnosticsNameValue(DI8DEVTYPEKEYBOARD_NEC98)
    	dx8DiagnosticsNameValue(DI8DEVTYPEKEYBOARD_NEC98LAPTOP)
    	dx8DiagnosticsNameValue(DI8DEVTYPEKEYBOARD_NEC98106)
    	dx8DiagnosticsNameValue(DI8DEVTYPEKEYBOARD_JAPAN106)
    	dx8DiagnosticsNameValue(DI8DEVTYPEKEYBOARD_JAPANAX)
    	dx8DiagnosticsNameValue(DI8DEVTYPEKEYBOARD_J3100)
    	{ NULL, 0 }
    	};

    static dx8DiagnosticsNameValue inputSubtypeJoystick[] =
    	{
    	dx8DiagnosticsNameValue(DI8DEVTYPEJOYSTICK_LIMITED)
    	dx8DiagnosticsNameValue(DI8DEVTYPEJOYSTICK_STANDARD)
    	{ NULL, 0 }
    	};

    static dx8DiagnosticsNameValue inputSubtypeGamepad[] =
    	{
    	dx8DiagnosticsNameValue(DI8DEVTYPEGAMEPAD_LIMITED)
    	dx8DiagnosticsNameValue(DI8DEVTYPEGAMEPAD_STANDARD)
    	dx8DiagnosticsNameValue(DI8DEVTYPEGAMEPAD_TILT)
    	{ NULL, 0 }
    	};

    static dx8DiagnosticsNameValue inputSubtypeDriving[] =
    	{
    	dx8DiagnosticsNameValue(DI8DEVTYPEDRIVING_LIMITED)
    	dx8DiagnosticsNameValue(DI8DEVTYPEDRIVING_COMBINEDPEDALS)
    	dx8DiagnosticsNameValue(DI8DEVTYPEDRIVING_DUALPEDALS)
    	dx8DiagnosticsNameValue(DI8DEVTYPEDRIVING_THREEPEDALS)
    	dx8DiagnosticsNameValue(DI8DEVTYPEDRIVING_HANDHELD)
    	{ NULL, 0 }
    	};

    static dx8DiagnosticsNameValue inputSubtypeFlight[] =
    	{
    	dx8DiagnosticsNameValue(DI8DEVTYPEFLIGHT_LIMITED)
    	dx8DiagnosticsNameValue(DI8DEVTYPEFLIGHT_STICK)
    	dx8DiagnosticsNameValue(DI8DEVTYPEFLIGHT_YOKE)
    	dx8DiagnosticsNameValue(DI8DEVTYPEFLIGHT_RC)
    	{ NULL, 0 }
    	};

    static dx8DiagnosticsNameValue inputSubtype1stPerson[] =
    	{
    	dx8DiagnosticsNameValue(DI8DEVTYPE1STPERSON_LIMITED)
    	dx8DiagnosticsNameValue(DI8DEVTYPE1STPERSON_UNKNOWN)
    	dx8DiagnosticsNameValue(DI8DEVTYPE1STPERSON_SIXDOF)
    	dx8DiagnosticsNameValue(DI8DEVTYPE1STPERSON_SHOOTER)
    	{ NULL, 0 }
    	};

    static dx8DiagnosticsNameValue inputSubtypeScreenPointer[] =
    	{
    	dx8DiagnosticsNameValue(DI8DEVTYPESCREENPTR_UNKNOWN)
    	dx8DiagnosticsNameValue(DI8DEVTYPESCREENPTR_LIGHTGUN)
    	dx8DiagnosticsNameValue(DI8DEVTYPESCREENPTR_LIGHTPEN)
    	dx8DiagnosticsNameValue(DI8DEVTYPESCREENPTR_TOUCH)
    	{ NULL, 0 }
    	};

    static dx8DiagnosticsNameValue inputSubtypeRemote[] =
    	{
    	dx8DiagnosticsNameValue(DI8DEVTYPEREMOTE_UNKNOWN)
    	{ NULL, 0 }
    	};

    static dx8DiagnosticsNameValue inputSubtypeDeviceCtrl[] =
    	{
    	dx8DiagnosticsNameValue(DI8DEVTYPEDEVICECTRL_UNKNOWN)
    	dx8DiagnosticsNameValue(DI8DEVTYPEDEVICECTRL_COMMSSELECTION)
    	dx8DiagnosticsNameValue(DI8DEVTYPEDEVICECTRL_COMMSSELECTION_HARDWIRED)
    	{ NULL, 0 }
    	};

    static dx8DiagnosticsNameValue inputSubtypeSupplemental[] =
    	{
    	dx8DiagnosticsNameValue(DI8DEVTYPESUPPLEMENTAL_UNKNOWN)
    	dx8DiagnosticsNameValue(DI8DEVTYPESUPPLEMENTAL_2NDHANDCONTROLLER)
    	dx8DiagnosticsNameValue(DI8DEVTYPESUPPLEMENTAL_HEADTRACKER)
    	dx8DiagnosticsNameValue(DI8DEVTYPESUPPLEMENTAL_HANDTRACKER)
    	dx8DiagnosticsNameValue(DI8DEVTYPESUPPLEMENTAL_SHIFTSTICKGATE)
    	dx8DiagnosticsNameValue(DI8DEVTYPESUPPLEMENTAL_SHIFTER)
    	dx8DiagnosticsNameValue(DI8DEVTYPESUPPLEMENTAL_THROTTLE)
    	dx8DiagnosticsNameValue(DI8DEVTYPESUPPLEMENTAL_SPLITTHROTTLE)
    	dx8DiagnosticsNameValue(DI8DEVTYPESUPPLEMENTAL_COMBINEDPEDALS)
    	dx8DiagnosticsNameValue(DI8DEVTYPESUPPLEMENTAL_DUALPEDALS)
    	dx8DiagnosticsNameValue(DI8DEVTYPESUPPLEMENTAL_THREEPEDALS)
    	dx8DiagnosticsNameValue(DI8DEVTYPESUPPLEMENTAL_RUDDERPEDALS)
    	{ NULL, 0 }
    	};

    static dx8DiagnosticsNameValue inputSubtypeInvalid[] =
    	{
    	{ NULL, 0 }
    	};

    static BOOL CALLBACK dInput8CallbackTOC(LPCDIDEVICEINSTANCE ddi, LPVOID context);
    static BOOL CALLBACK dInput8CallbackBody(LPCDIDEVICEINSTANCE ddi, LPVOID context);

    class printerDInput8 : public dx8DiagnosticsPrinter
    	{
    	public:

    	DWORD counter;
    	dx8DiagnosticsOutput *output;

    	IDirectInput8 *dInput;
    	bool noDevices;
    	HRESULT enumStatusCode;
    	HRESULT createStatusCode;

    	printerDInput8()
    		{
    		noDevices = false;
    		enumStatusCode = S_OK;
    		dInput = NULL;
    		enumStatusCode = E_FAIL;
    		if (!dx8DynamicIsAvailable())
    			createStatusCode = E_FAIL;
    		else
    			createStatusCode = dx8DynamicDirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8A, (void **)&dInput, NULL);
    		}

    	virtual ~printerDInput8()
    		{
    		if (dInput != NULL)
    			{
    			dInput->Release();
    			dInput = NULL;
    			}
    		}

    	virtual HRESULT printTOC(dx8Diagnostics *diagnostics)
    		{
    		counter = 0;
    		this->output = diagnostics->output;

    		if (createStatusCode != S_OK)
    			goto EXIT;

    		enumStatusCode = dInput->EnumDevices(DI8DEVCLASS_ALL, dInput8CallbackTOC, this, DIEDFL_ALLDEVICES);

    EXIT:
    		if (counter == 0)
    			{
    			noDevices = true;
    			return output->print("<dd><a href=inputdevice><code><b>input devices</b></code></a>\n");
    			}
    		return S_OK;
    		}

    	virtual HRESULT printBody(dx8Diagnostics *diagnostics)
    		{
    		HRESULT returnValue = S_OK;
    		this->output = diagnostics->output;

    		if (noDevices)
    			{
    			ASSERT_SUCCESS(output->printHeading("input devices"));
    			ASSERT_SUCCESS(output->startBody("No input devices detected"));
    			ASSERT_SUCCESS(output->printInteger("<code>DirectInput8Create()</code> status code", createStatusCode));
    			ASSERT_SUCCESS(output->printInteger("<code>EnumDevices()</code> status code", enumStatusCode));
    			RETURN(output->endBody());
    			}

    		counter = 0;
    		dInput->EnumDevices(DI8DEVCLASS_ALL, dInput8CallbackBody, this, DIEDFL_ALLDEVICES);
    EXIT:
    		return returnValue;
    		}

    	BOOL tocCallback(LPCDIDEVICEINSTANCE ddi)
    		{
    		output->print("<dd><a href=#inputdevice%d><code><b>input device %d</b></code></a><code><b>:</b></code> <b>%s</b>\n", counter, counter, ddi->tszInstanceName);
    		counter++;
    		return TRUE;
    		}

    	BOOL bodyCallback(LPCDIDEVICEINSTANCE ddi)
    		{
    		HRESULT returnValue = S_OK;

    		char buffer[64];
    		sprintf(buffer, "input device %d", counter++);
    		ASSERT_SUCCESS(output->printHeading(buffer));
    		ASSERT_SUCCESS(output->startBody(ddi->tszInstanceName));

    	#define PRINT_DDI_FIELD(name, type) ASSERT_SUCCESS(PRINT_FIELD_ ## type(name, (*ddi)))

    		PRINT_DDI_FIELD(tszProductName, String);
    		PRINT_DDI_FIELD(guidInstance, GUID);
    		PRINT_DDI_FIELD(guidProduct, GUID);

    		ASSERT_SUCCESS(output->printEnum("dwDevType", GET_DIDEVICE_TYPE(ddi->dwDevType), inputType));
    		dx8DiagnosticsNameValue *subtype;
    		subtype = inputSubtypeInvalid;
    		switch (GET_DIDEVICE_TYPE(ddi->dwDevType))
    			{
    			case DI8DEVTYPE_MOUSE:
    				subtype = inputSubtypeMouse;
    				break;
    			case DI8DEVTYPE_KEYBOARD:
    				subtype = inputSubtypeKeyboard;
    				break;
    			case DI8DEVTYPE_JOYSTICK:
    				subtype = inputSubtypeJoystick;
    				break;
    			case DI8DEVTYPE_GAMEPAD:
    				subtype = inputSubtypeGamepad;
    				break;
    			case DI8DEVTYPE_DRIVING:
    				subtype = inputSubtypeDriving;
    				break;
    			case DI8DEVTYPE_FLIGHT:
    				subtype = inputSubtypeFlight;
    				break;
    			case DI8DEVTYPE_1STPERSON:
    				subtype = inputSubtype1stPerson;
    				break;
    			case DI8DEVTYPE_DEVICECTRL:
    				subtype = inputSubtypeDeviceCtrl;
    				break;
    			case DI8DEVTYPE_SCREENPOINTER:
    				subtype = inputSubtypeScreenPointer;
    				break;
    			case DI8DEVTYPE_REMOTE:
    				subtype = inputSubtypeRemote;
    				break;
    			case DI8DEVTYPE_SUPPLEMENTAL:
    				subtype = inputSubtypeSupplemental;
    				break;
    			}
    		ASSERT_SUCCESS(output->printEnum("dwDevSubType", GET_DIDEVICE_SUBTYPE(ddi->dwDevType), subtype));
    		ASSERT_SUCCESS(output->endBody());

    EXIT:
    		return TRUE;
    		}
    	};

    static BOOL CALLBACK dInput8CallbackTOC(LPCDIDEVICEINSTANCE ddi, LPVOID context)
    	{
    	printerDInput8 *printer = (printerDInput8 *)context;
    	return printer->tocCallback(ddi);
    	}

    static BOOL CALLBACK dInput8CallbackBody(LPCDIDEVICEINSTANCE ddi, LPVOID context)
    	{
    	printerDInput8 *printer = (printerDInput8 *)context;
    	return printer->bodyCallback(ddi);
    	}

    ///////////////////////////////////////////////////////////////////////////
    //
    //
    // dlls
    //
    //

    class printerDLLs : public dx8DiagnosticsPrinter
    	{
    	public:

    	virtual HRESULT printTOC(dx8Diagnostics *diagnostics)
    		{
    		dx8DiagnosticsOutput *output = diagnostics->output;
    		return output->print("<dd><a href=#dlls><code><b>dlls</b></code></a>\n");
    		}

    	virtual HRESULT printDLL(dx8DiagnosticsOutput *output, char *filename, char *filePath)
    		{
    		HRESULT returnValue = S_OK;

    		DWORD stupidVariable;
    		char buffer[256];
    		char versionBuffer[128];

    		strcpy(buffer, "version ");
    		strcpy(versionBuffer, "<i>unknown!</i>");

    		DWORD versionSize = GetFileVersionInfoSize(filePath, &stupidVariable);
    		if (versionSize)
    			{
    			void *version = malloc(versionSize);
    			if (GetFileVersionInfo(filePath, 0, versionSize, version) == 0)
    				sprintf(versionBuffer, "could not be determined (<code>GetFileVersionInfo()</code> returned %d)", GetLastError());
    			else
    				{
    				// the "file version" is often stored in two places
    				// in a Win32 PE header.  (the mark of EXCELLENT design.)
    				// one is a normal string in StringFileInfo, the other
    				// is four words in the FIXEDFILEINFO structure.
    				// which one to use?  it seems like if the string is set,
    				// use that, otherwise use the four words.

    				unsigned int length;
    				char *value;
    				VerQueryValue(version,
    					TEXT("\\StringFileInfo\\040904E4\\FileVersion"),
    					(void **)&value, &length);

    				if ((value != NULL) && length)
    					strcpy(versionBuffer, value);
    				else
    					{
    					VS_FIXEDFILEINFO *ffi;
    					VerQueryValue(version, "\\", (void **)&ffi, &length);
    					sprintf(versionBuffer, "%d.%d.%d.%d",
    						HIWORD(ffi->dwProductVersionMS),
    						LOWORD(ffi->dwProductVersionMS),
    						HIWORD(ffi->dwProductVersionLS),
    						LOWORD(ffi->dwProductVersionLS));
    					}
    				}
    			free(version);
    			}

    		strcat(buffer, versionBuffer);
    		strcat(buffer, ", size ");
    		HANDLE hFile = CreateFile(filePath, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
    		if (hFile == INVALID_HANDLE_VALUE)
    			sprintf(buffer + strlen(buffer), "<i>unknown!</i> <code>CreateFile()</code> returned %d", GetLastError());
    		else
    			{
    			DWORD size = GetFileSize(hFile, NULL);
    			if (size == INVALID_FILE_SIZE)
    				sprintf(buffer + strlen(buffer), "<i>unknown!</i> <code>GetFileSize()</code> returned %d", GetLastError());
    			else
    				sprintf(buffer + strlen(buffer), "%d bytes", size);
    			CloseHandle(hFile);
    			}

    		RETURN(output->printString(filename, buffer));

    EXIT:
    		return returnValue;
    		}

    	HRESULT findDLL(dx8DiagnosticsOutput *output, char *filename)
    		{
    		char filePath[_MAX_PATH];
    		char *ignored;
    		if (SearchPath(NULL, filename, NULL, sizeof(filePath), filePath, &ignored))
    			return printDLL(output, filename, filePath);

    		GetSystemDirectory(filePath, sizeof(filePath));
    		char *trace = filePath + strlen(filePath) - 1;
    		while ((*trace == '/') || (*trace == '\\'))
    			trace--;
    		trace++;
    		*trace++ = '\\';

    	#define TRY_DIRECTORY(x) \
    		strcpy(trace, x "\\"); \
    		strcat(trace, filename); \
    		if (GetFileAttributes(filePath) != 0xFFFFffff) \
    			return printDLL(output, filename, filePath) \

    		TRY_DIRECTORY("drivers");
    		TRY_DIRECTORY("dxxpdbg");
    		TRY_DIRECTORY("dllcache");

    		return output->printString(filename, "<i>not found!</i>");
    		}

    	HRESULT printBody(dx8Diagnostics *diagnostics)
    		{
    		HRESULT returnValue = S_OK;

    		dx8DiagnosticsOutput *output = diagnostics->output;

    		ASSERT_SUCCESS(output->printHeading("dlls"));
    		ASSERT_SUCCESS(output->startBody());

    	#define PROCESS_DLL(name) ASSERT_SUCCESS(findDLL(output, #name));

    		ASSERT_SUCCESS(output->printSubheading("directx"));
    		PROCESS_DLL(d3d8.dll)
    		PROCESS_DLL(d3d8d.dll)
    		PROCESS_DLL(d3d8thk.dll)
    		PROCESS_DLL(d3d9.dll)
    		PROCESS_DLL(d3d9d.dll)
    		PROCESS_DLL(d3dim.dll)
    		PROCESS_DLL(d3dim700.dll)
    		PROCESS_DLL(d3dpmesh.dll)
    		PROCESS_DLL(d3dramp.dll)
    		PROCESS_DLL(d3dref.dll)
    		PROCESS_DLL(d3dref8.dll)
    		PROCESS_DLL(d3dref9.dll)
    		PROCESS_DLL(d3drm.dll)
    		PROCESS_DLL(d3dx8d.dll)
    		PROCESS_DLL(d3dx9d.dll)
    		PROCESS_DLL(d3dxof.dll)
    		PROCESS_DLL(ddraw.dll)
    		PROCESS_DLL(ddrawex.dll)
    		PROCESS_DLL(diactfrm.dll)
    		PROCESS_DLL(dimap.dll)
    		PROCESS_DLL(dinput.dll)
    		PROCESS_DLL(dinput8.dll)
    		PROCESS_DLL(dinput8d.dll)
    		PROCESS_DLL(directx.cpl)
    		PROCESS_DLL(dmband.dll)
    		PROCESS_DLL(dmbandd.dll)
    		PROCESS_DLL(dmcompod.dll)
    		PROCESS_DLL(dmcompos.dll)
    		PROCESS_DLL(dmime.dll)
    		PROCESS_DLL(dmimed.dll)
    		PROCESS_DLL(dmloaded.dll)
    		PROCESS_DLL(dmloader.dll)
    		PROCESS_DLL(dmscripd.dll)
    		PROCESS_DLL(dmscript.dll)
    		PROCESS_DLL(dmstyle.dll)
    		PROCESS_DLL(dmstyled.dll)
    		PROCESS_DLL(dmsynth.dll)
    		PROCESS_DLL(dmsynthd.dll)
    		PROCESS_DLL(dmusic.dll)
    		PROCESS_DLL(dmusicd.dll)
    		PROCESS_DLL(dplay.dll)
    		PROCESS_DLL(dplaysvr.exe)
    		PROCESS_DLL(dplayx.dll)
    		PROCESS_DLL(dpmodemx.dll)
    		PROCESS_DLL(dpnaddr.dll)
    		PROCESS_DLL(dpnet.dll)
    		PROCESS_DLL(dpnetd.dll)
    		PROCESS_DLL(dpnhpast.dll)
    		PROCESS_DLL(dpnhpastd.dll)
    		PROCESS_DLL(dpnhupnp.dll)
    		PROCESS_DLL(dpnhupnpd.dll)
    		PROCESS_DLL(dpnlobby.dll)
    		PROCESS_DLL(dpnsvr.exe)
    		PROCESS_DLL(dpnsvrd.exe)
    		PROCESS_DLL(dpserial.dll)
    		PROCESS_DLL(dpvacm.dll)
    		PROCESS_DLL(dpvacmd.dll)
    		PROCESS_DLL(dpvoice.dll)
    		PROCESS_DLL(dpvoiced.dll)
    		PROCESS_DLL(dpvsetup.exe)
    		PROCESS_DLL(dpvvox.dll)
    		PROCESS_DLL(dpvvoxd.dll)
    		PROCESS_DLL(dpwsock.dll)
    		PROCESS_DLL(dpwsockx.dll)
    		PROCESS_DLL(dsdmo.dll)
    		PROCESS_DLL(dsdmoprp.dll)
    		PROCESS_DLL(dsound.dll)
    		PROCESS_DLL(dsound3d.dll)
    		PROCESS_DLL(dswave.dll)
    		PROCESS_DLL(dswaved.dll)
    		PROCESS_DLL(dx7vb.dll)
    		PROCESS_DLL(dx8vb.dll)
    		PROCESS_DLL(dxapi.sys)
    		PROCESS_DLL(dxdiagn.dll)
    		PROCESS_DLL(gcdef.dll)
    		PROCESS_DLL(joy.cpl)
    		PROCESS_DLL(Microsoft.DirectX.AudioVideoPlayback.dll)
    		PROCESS_DLL(Microsoft.DirectX.Diagnostics.dll)
    		PROCESS_DLL(Microsoft.DirectX.Direct3D.dll)
    		PROCESS_DLL(Microsoft.DirectX.Direct3DX.dll)
    		PROCESS_DLL(Microsoft.DirectX.DirectDraw.dll)
    		PROCESS_DLL(Microsoft.DirectX.DirectInput.dll)
    		PROCESS_DLL(Microsoft.DirectX.DirectPlay.dll)
    		PROCESS_DLL(Microsoft.DirectX.DirectSound.dll)
    		PROCESS_DLL(Microsoft.DirectX.dll)
    		PROCESS_DLL(pid.dll)
    		PROCESS_DLL(system.dll)

    		ASSERT_SUCCESS(output->printSubheading("c run-time library"));
    		PROCESS_DLL(mfc40.dll)
    		PROCESS_DLL(mfc42.dll)
    		PROCESS_DLL(msvcirt.dll)
    		PROCESS_DLL(msvcirtd.dll)
    		PROCESS_DLL(msvcp50.dll)
    		PROCESS_DLL(msvcp60.dll)
    		PROCESS_DLL(msvcp60d.dll)
    		PROCESS_DLL(msvcrt.dll)
    		PROCESS_DLL(msvcrtd.dll)

    		ASSERT_SUCCESS(output->printSubheading("windows"));
    		PROCESS_DLL(comctl32.dll)
    		PROCESS_DLL(comdlg32.dll)
    		PROCESS_DLL(ctl3d32.dll)
    		PROCESS_DLL(gdi32.dll)
    		PROCESS_DLL(kernel32.dll)
    		PROCESS_DLL(user32.dll)
    		PROCESS_DLL(version.dll)
    		PROCESS_DLL(wsock32.dll)

    		ASSERT_SUCCESS(output->printSubheading("internet explorer"));
    		PROCESS_DLL(shdocvw.dll)

    		ASSERT_SUCCESS(output->printSubheading("miscellaneous"));
    		PROCESS_DLL(amstream.dll)
    		PROCESS_DLL(bdaplgin.ax)
    		PROCESS_DLL(bdasup.sys)
    		PROCESS_DLL(ccdecode.sys)
    		PROCESS_DLL(devenum.dll)
    		PROCESS_DLL(dxmasf.dll)
    		PROCESS_DLL(encapi.dll)
    		PROCESS_DLL(iac25_32.ax)
    		PROCESS_DLL(ipsink.ax)
    		PROCESS_DLL(ir41_32.ax)
    		PROCESS_DLL(ir41_qc.dll)
    		PROCESS_DLL(ir41_qcx.dll)
    		PROCESS_DLL(ir50_32.dll)
    		PROCESS_DLL(ir50_qc.dll)
    		PROCESS_DLL(ir50_qcx.dll)
    		PROCESS_DLL(ivfsrc.ax)
    		PROCESS_DLL(ks.sys)
    		PROCESS_DLL(ksproxy.ax)
    		PROCESS_DLL(kstvtune.ax)
    		PROCESS_DLL(ksuser.dll)
    		PROCESS_DLL(kswdmcap.ax)
    		PROCESS_DLL(ksxbar.ax)
    		PROCESS_DLL(mciqtz32.dll)
    		PROCESS_DLL(mpe.sys)
    		PROCESS_DLL(mpeg2data.ax)
    		PROCESS_DLL(mpg2splt.ax)
    		PROCESS_DLL(msdmo.dll)
    		PROCESS_DLL(msdv.sys)
    		PROCESS_DLL(msdvbnp.ax)
    		PROCESS_DLL(mskssrv.sys)
    		PROCESS_DLL(mspclock.sys)
    		PROCESS_DLL(mspqm.sys)
    		PROCESS_DLL(mstee.sys)
    		PROCESS_DLL(msvidctl.dll)
    		PROCESS_DLL(mswebdvd.dll)
    		PROCESS_DLL(msyuv.dll)
    		PROCESS_DLL(nabtsfec.sys)
    		PROCESS_DLL(ndisip.sys)
    		PROCESS_DLL(psisdecd.dll)
    		PROCESS_DLL(psisrndr.ax)
    		PROCESS_DLL(qasf.dll)
    		PROCESS_DLL(qcap.dll)
    		PROCESS_DLL(qdv.dll)
    		PROCESS_DLL(qdvd.dll)
    		PROCESS_DLL(qedit.dll)
    		PROCESS_DLL(qedwipes.dll)
    		PROCESS_DLL(quartz.dll)
    		PROCESS_DLL(slip.sys)
    		PROCESS_DLL(stream.sys)
    		PROCESS_DLL(streamip.sys)
    		PROCESS_DLL(strmdll.dll)
    		PROCESS_DLL(swenum.sys)
    		PROCESS_DLL(vbisurf.ax)
    		PROCESS_DLL(vfwwdm32.dll)
    		PROCESS_DLL(wstcodec.sys)
    		PROCESS_DLL(wstdecod.dll)

    		RETURN(output->endBody());

    EXIT:
    		return returnValue;
    		}
    	};

    ///////////////////////////////////////////////////////////////////////////
    //
    //
    // finally! our main entry point.
    //
    //

    dx8Diagnostics::dx8Diagnostics(char *applicationName)
    	{
    	output = NULL;
    	head = tail = NULL;
    	this->applicationName = applicationName;
    	}

    dx8Diagnostics::~dx8Diagnostics(void)
    	{
    	if (output != NULL)
    		{
    		delete output;
    		output = NULL;
    		}
    	dx8DiagnosticsPrinter *trace = head;
    	while (trace != NULL)
    		{
    		dx8DiagnosticsPrinter *next = trace->next;
    		delete trace;
    		trace = next;
    		}
    	head = tail = NULL;
    	}

    HRESULT dx8Diagnostics::append(dx8DiagnosticsPrinter *printer)
    	{
    	if (head == NULL)
    		head = printer;
    	else
    		tail->next = printer;

    	printer->next = NULL;
    	tail = printer;
    	return S_OK;
    	}

    HRESULT dx8Diagnostics::write(void)
    	{
    	if (head == NULL)
    		dx8DiagnosticsAddDiagnostics(this, DX8DIAGNOSTICS_DEFAULT);

    	if (output == NULL)
    		dx8DiagnosticsWriteToFile(this, "dx8diagnostics.html");

    	if ((head == NULL) || (output == NULL))
    		return E_FAIL;

    	startTime = GetTickCount();

    	dx8DiagnosticsPrinter *trace = head;
    	while (trace != NULL)
    		{
    		trace->printBody(this);
    		trace = trace->next;
    		}
    	return S_OK;
    	}

    HRESULT dx8DiagnosticsStartup(void)
    	{
    	CoInitialize(NULL);
    	dx8DynamicStartup();
    	return S_OK;
    	}

    HRESULT dx8DiagnosticsShutdown(void)
    	{
    	dx8DynamicShutdown();
    	CoUninitialize();
    	return S_OK;
    	}

    HRESULT dx8DiagnosticsCreate(dx8Diagnostics **diagnostics, char *applicationName)
    	{
    	*diagnostics = new dx8Diagnostics(applicationName);
    	return S_OK;
    	}

    HRESULT dx8DiagnosticsWrite(dx8Diagnostics *diagnostics)
    	{
    	return diagnostics->write();
    	}

    HRESULT dx8DiagnosticsDestroy(dx8Diagnostics **diagnostics)
    	{
    	dx8Diagnostics *d = *diagnostics;
    	*diagnostics = NULL;
    	delete d;

    	return S_OK;
    	}

    HRESULT dx8DiagnosticsWriteToFile(dx8Diagnostics *diagnostics, char *filename)
    	{
    	dx8DiagnosticsOutputFile *outputFile = new dx8DiagnosticsOutputFile();
    	outputFile->initialize(filename);
    	diagnostics->output = outputFile;

    	return S_OK;
    	}

    HRESULT dx8DiagnosticsWriteToMemory(dx8Diagnostics *diagnostics, char *buffer, size_t bufferSize)
    	{
    	dx8DiagnosticsOutputMemory *outputMemory = new dx8DiagnosticsOutputMemory();
    	outputMemory->initialize(buffer, bufferSize);
    	diagnostics->output = outputMemory;

    	return S_OK;
    	}

    HRESULT dx8DiagnosticsAddDiagnostics(dx8Diagnostics *diagnostics, DWORD whichDiagnostics)
    	{
    #define HANDLE_BITFIELD(bit, class) \
    	if (whichDiagnostics & bit) \
    		{ \
    		dx8DiagnosticsPrinter *printer = new class; \
    		diagnostics->append(printer); \
    		} \

    	HANDLE_BITFIELD(DX8DIAGNOSTICS_TITLE, printerTitle)
    	HANDLE_BITFIELD(DX8DIAGNOSTICS_TABLE_OF_CONTENTS, printerTOC)
    	HANDLE_BITFIELD(DX8DIAGNOSTICS_HARDWARE, printerHardware)
    	HANDLE_BITFIELD(DX8DIAGNOSTICS_OPERATING_SYSTEM, printerOS)
    	if (whichDiagnostics & (DX8DIAGNOSTICS_D3D8 | DX8DIAGNOSTICS_D3D8_WITH_WHQL))
    		{
    		printerD3D8 *printer = new printerD3D8((whichDiagnostics & DX8DIAGNOSTICS_D3D8_WITH_WHQL) != 0);
    		diagnostics->append(printer);
    		}
    	HANDLE_BITFIELD(DX8DIAGNOSTICS_DSOUND8, printerDSound8)
    	HANDLE_BITFIELD(DX8DIAGNOSTICS_DINPUT8, printerDInput8)
    	HANDLE_BITFIELD(DX8DIAGNOSTICS_DLLS, printerDLLs)
    	HANDLE_BITFIELD(DX8DIAGNOSTICS_FOOTER, printerFooter)

    	return S_OK;
    	}

Currently browsing [dx8diagnostics.zip] (84,112 bytes) - [dx8dynamic.cpp] - (2,868 bytes)

    /*
    ** Copyright (C) 2003 Larry Hastings, larry@hastings.org
    **
    ** This software is provided 'as-is', without any express or implied warranty.  In no event will the authors be held liable for any damages arising from the use of this software.
    **
    ** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
    **
    ** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
    ** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
    ** 3. This notice may not be removed or altered from any source distribution.
    **
    ** The dx8Diagnostics / dx8Dynamic homepage is here:
    **		http://www.midwinter.com/~lch/programming/dx8diagnostics/
    */

    #include "dx8dynamic.h"

    #define PROCESS_MODULE(hmodule, filename) \
    	static HMODULE hmodule = NULL;

    #define PROCESS_ENTRYPOINT(function, hmodule, returnType, arguments) \
    	function ## _fn dx8Dynamic ## function = NULL;

    DX8DYNAMIC_MODULES_AND_ENTRYPOINTS;

    #undef PROCESS_MODULE
    #undef PROCESS_ENTRYPOINT

    static DWORD dx8DynamicStartupCount = 0;
    static HRESULT dx8DynamicStatusCode = E_FAIL;

    HRESULT dx8DynamicStartup(void)
    	{
    	dx8DynamicStartupCount++;

    #define PROCESS_MODULE(hmodule, filename) \
    	if (hmodule == NULL) \
    		{ \
    		hmodule = LoadLibrary(filename); \
    		if (hmodule == NULL) \
    		goto FAIL; \
    		} \

    #define PROCESS_ENTRYPOINT(function, hmodule, returnType, arguments) \
    	if (dx8Dynamic ## function == NULL) \
    		{ \
    		dx8Dynamic ## function = (function ## _fn)GetProcAddress(hmodule, #function); \
    		if (dx8Dynamic ## function == NULL) \
    		goto FAIL; \
    		} \

    	DX8DYNAMIC_MODULES_AND_ENTRYPOINTS;

    #undef PROCESS_MODULE
    #undef PROCESS_ENTRYPOINT

    	dx8DynamicStatusCode = S_OK;
    	return S_OK;
    FAIL:
    	dx8DynamicStatusCode = E_FAIL;
    	return E_FAIL;
    	}

    HRESULT dx8DynamicStatus(void)
    	{
    	return dx8DynamicStatusCode;
    	}

    BOOL dx8DynamicIsAvailable(void)
    	{
    	return dx8DynamicStatusCode == S_OK;
    	}

    HRESULT dx8DynamicShutdown(void)
    	{
    	if (dx8DynamicStartupCount > 1)
    		{
    		dx8DynamicStartupCount--;
    		return S_OK;
    		}

    	dx8DynamicStartupCount = 0;

    #define PROCESS_MODULE(hmodule, filename) \
    	if (hmodule != NULL) \
    		{ \
    		FreeLibrary(hmodule); \
    		hmodule = NULL; \
    		} \

    #define PROCESS_ENTRYPOINT(function, hmodule, returnType, arguments) \
    	dx8Dynamic ## function = NULL;

    	DX8DYNAMIC_MODULES_AND_ENTRYPOINTS

    #undef PROCESS_MODULE
    #undef PROCESS_ENTRYPOINT

    	dx8DynamicStatusCode = E_FAIL;
    	return S_OK;
    	}

Currently browsing [dx8diagnostics.zip] (84,112 bytes) - [main.cpp] - (1,378 bytes)

    /*
    ** Copyright (C) 2003 Larry Hastings, larry@hastings.org
    **
    ** This software is provided 'as-is', without any express or implied warranty.  In no event will the authors be held liable for any damages arising from the use of this software.
    **
    ** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
    **
    ** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
    ** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
    ** 3. This notice may not be removed or altered from any source distribution.
    **
    ** The dx8Diagnostics / dx8Dynamic homepage is here:
    **		http://www.midwinter.com/~lch/programming/dx8diagnostics/
    */

    #include "dx8Diagnostics.h"

    int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
    	{
    	if (dx8DiagnosticsStartup() == S_OK)
    		{
    		dx8Diagnostics *diagnostics;
    		dx8DiagnosticsCreate(&diagnostics, "directx 8");
    		dx8DiagnosticsWrite(diagnostics);
    		dx8DiagnosticsDestroy(&diagnostics);

    		dx8DiagnosticsShutdown();
    		}
    	return 0;
    	}

Currently browsing [dx8diagnostics.zip] (84,112 bytes) - [dx8diagnostics.h] - (6,605 bytes)

    /*
    ** Copyright (C) 2003 Larry Hastings, larry@hastings.org
    **
    ** This software is provided 'as-is', without any express or implied warranty.  In no event will the authors be held liable for any damages arising from the use of this software.
    **
    ** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
    **
    ** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
    ** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
    ** 3. This notice may not be removed or altered from any source distribution.
    **
    ** The dx8Diagnostics / dx8Dynamic homepage is here:
    **		http://www.midwinter.com/~lch/programming/dx8diagnostics/
    */

    #ifndef __DX8DIAGNOSTICS_H
    #define __DX8DIAGNOSTICS_H

    #include "dx8dynamic.h"

    struct dx8Diagnostics;
    typedef struct dx8Diagnostics dx8Diagnostics;

    #ifdef __cplusplus

    ///////////////////////////////////////////////////////////////////////////
    //
    //
    // dx8DiagnosticsNameValue
    // just a structure for storing bitfield/enum ids
    //
    //
    struct dx8DiagnosticsNameValue
    	{
    	char *name;
    	DWORD value;
    	};

    #define dx8DiagnosticsNameValue(name)	{ #name, name },
    #define dx8DiagnosticsNameValue_OLD(name)	{ #name " <b><i>(deprecated)</i></b>", name },
    #define dx8DiagnosticsNameValue_DX9(name)	{ #name " <b><i>(DX9)</i></b>", name },

    extern "C" dx8DiagnosticsNameValue *dx8DiagnosticsNameValueFind(dx8DiagnosticsNameValue *array, DWORD value);

    struct colors
    	{
    	char *background;
    	char *foreground;
    	};

    struct colorScheme
    	{
    	colors header;
    	colors subheader;
    	colors body;
    	};

    ///////////////////////////////////////////////////////////////////////////
    //
    //
    // dx8DiagnosticsOutput
    //
    //
    class dx8DiagnosticsOutput
    	{
    	public:

    	bool eos;

    	dx8DiagnosticsOutput(void);

    	virtual HRESULT print(char *format, ...) = 0;

    	colorScheme *schemes;
    	colorScheme *scheme;

    	void nextScheme(void);

    #define PRINT_FIELD_Integer(name, struct) output->printInteger(#name, struct.name);
    	HRESULT printInteger(char *name, DWORD value);

    #define PRINT_FIELD_Float(name, struct) output->printFloat(#name, struct.name);
    	HRESULT printFloat(char *name, double value);

    #define PRINT_FIELD_String(name, struct) output->printString(#name, struct.name);
    	HRESULT printString(char *name, const char *value);

    #define PRINT_FIELD_Enum(name, struct) output->printEnum(#name, struct.name, enum ## name);
    	HRESULT printEnum(char *name, DWORD value, dx8DiagnosticsNameValue *enumValues);

    #define PRINT_FIELD_Bitfield(name, struct) output->printBitfield(#name, struct.name, bitfield ## name);
    	HRESULT printBitfield(char *name, DWORD value, dx8DiagnosticsNameValue *bitValues);

    #define PRINT_FIELD_Version(name, struct) output->printVersion(#name, struct.name);
    	HRESULT printVersion(char *name, LARGE_INTEGER v);

    #define PRINT_FIELD_GUID(name, struct) output->printGUID(#name, &(struct.name));
    	HRESULT printGUID(char *name, const GUID *value);

    #define PRINT_FIELD_WHQLLevel(name, struct) output->printWHQLLevel(#name, struct.name);
    	HRESULT printWHQLLevel(char *name, DWORD level);

    	HRESULT printWednesdaySpaced(char *string);

    	HRESULT printHeading(char *title, int size=5);
    	HRESULT printSubheading(char *title, int size=4);

    	HRESULT startBody(const char *subtitle = NULL, int size=5);
    	HRESULT endBody(void);
    	};

    ///////////////////////////////////////////////////////////////////////////
    //
    //
    // dx8DiagnosticsOutputMemory
    //
    //
    class dx8DiagnosticsOutputMemory : public dx8DiagnosticsOutput
    	{
    	public:

    	char *buffer;
    	DWORD bufferSize;

    	char *trace;
    	char *end;

    	dx8DiagnosticsOutputMemory();

    	void initialize(char *buffer, DWORD bufferSize);
    	HRESULT print(char *format, ...);
    	};

    ///////////////////////////////////////////////////////////////////////////
    //
    //
    // dx8DiagnosticsOutputFile
    //
    //
    class dx8DiagnosticsOutputFile : public dx8DiagnosticsOutput
    	{
    	public:

    	HANDLE hFile;

    	dx8DiagnosticsOutputFile();
    	void initialize(char *filename);
    	~dx8DiagnosticsOutputFile();
    	HRESULT print(char *format, ...);
    	};

    ///////////////////////////////////////////////////////////////////////////
    //
    //
    // dx8DiagnosticsPrinter
    //
    //
    struct dx8DiagnosticsPrinter
    	{
    	dx8DiagnosticsPrinter *next;

    	dx8DiagnosticsPrinter()
    		{
    		next = NULL;
    		}
    	virtual ~dx8DiagnosticsPrinter()
    		{
    		}

    	virtual HRESULT printTOC(dx8Diagnostics *diagnostics) = 0;
    	virtual HRESULT printBody(dx8Diagnostics *diagnostics) = 0;
    	};

    struct dx8Diagnostics
    	{
    	char *applicationName;

    	dx8DiagnosticsOutput *output;

    	dx8DiagnosticsPrinter *head;
    	dx8DiagnosticsPrinter *tail;

    	DWORD startTime;
    	DWORD endTime;

    	dx8Diagnostics(char *applicationName);
    	~dx8Diagnostics(void);

    	HRESULT append(dx8DiagnosticsPrinter *printer);

    	HRESULT write(void);
    	};

    extern "C" {

    #endif /* __cplusplus */

    extern HRESULT dx8DiagnosticsStartup(void);
    extern HRESULT dx8DiagnosticsShutdown(void);

    extern HRESULT dx8DiagnosticsCreate(dx8Diagnostics **diagnostic, char *applicationName);
    extern HRESULT dx8DiagnosticsWrite(dx8Diagnostics *diagnostic);
    extern HRESULT dx8DiagnosticsDestroy(dx8Diagnostics **diagnostic);

    extern HRESULT dx8DiagnosticsWriteToFile(dx8Diagnostics *diagnostic, char *filename);
    extern HRESULT dx8DiagnosticsWriteToMemory(dx8Diagnostics *diagnostic, char *buffer, size_t bufferSize);

    extern HRESULT dx8DiagnosticsAddDiagnostics(dx8Diagnostics *diagnostic, DWORD whichDiagnostics);

    #define DX8DIAGNOSTICS_TITLE				(1)
    #define DX8DIAGNOSTICS_TABLE_OF_CONTENTS	(2)
    #define DX8DIAGNOSTICS_HARDWARE			(4)
    #define DX8DIAGNOSTICS_OPERATING_SYSTEM	(8)
    #define DX8DIAGNOSTICS_D3D8				(16)
    #define DX8DIAGNOSTICS_D3D8_WITH_WHQL	(32)
    #define DX8DIAGNOSTICS_DSOUND8			(64)
    #define DX8DIAGNOSTICS_DINPUT8			(128)
    #define DX8DIAGNOSTICS_DLLS				(256)
    #define DX8DIAGNOSTICS_FOOTER			(512)

    #define DX8DIAGNOSTICS_DEFAULT			(1023 ^ DX8DIAGNOSTICS_D3D8_WITH_WHQL)
    #define DX8DIAGNOSTICS_COMPLETE			(1023 ^ DX8DIAGNOSTICS_D3D8)

    #define DX8DIAGNOSTICS_VERSION "1.0"

    #ifdef __cplusplus
    	};
    #endif /* __cplusplus */

    #endif /* __DX8DIAGNOSTICS_H */

Currently browsing [dx8diagnostics.zip] (84,112 bytes) - [dx8dynamic.h] - (2,814 bytes)

    /*
    ** Copyright (C) 2003 Larry Hastings, larry@hastings.org
    **
    ** This software is provided 'as-is', without any express or implied warranty.  In no event will the authors be held liable for any damages arising from the use of this software.
    **
    ** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
    **
    ** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
    ** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
    ** 3. This notice may not be removed or altered from any source distribution.
    **
    ** The dx8Diagnostics / dx8Dynamic homepage is here:
    **		http://www.midwinter.com/~lch/programming/dx8diagnostics/
    */

    #ifndef __DX8DYNAMIC_H
    #define __DX8DYNAMIC_H

    #include <windows.h>
    #include <exdisp.h>

    #ifndef DIRECTINPUT_VERSION
    #define DIRECTINPUT_VERSION 0x0800
    #endif /* DIRECTINPUT_VERSION */

    #include <d3d8.h>
    #include <d3dx8.h>
    #include <dsound.h>
    #include <dinput.h>

    #ifdef __cplusplus
    extern "C" {
    #endif /* __cplusplus */

    /*/////////////////////////////////////////////////////////////////////////
    //
    //
    // runtime dll binding
    //
    //
    */

    #define DX8DYNAMIC_MODULES_AND_ENTRYPOINTS \
    	PROCESS_MODULE(hModuleD3d8, "d3d8.dll") \
    	PROCESS_ENTRYPOINT(Direct3DCreate8, hModuleD3d8, IDirect3D8 *, (UINT SDKVersion)) \
    	PROCESS_MODULE(hModuleDsound, "dsound.dll") \
    	PROCESS_ENTRYPOINT(DirectSoundCreate8, hModuleDsound, HRESULT, (LPCGUID pcGuidDevice, LPDIRECTSOUND8 *ppDS8, LPUNKNOWN pUnkOuter)) \
    	PROCESS_ENTRYPOINT(DirectSoundEnumerateA, hModuleDsound, HRESULT, (LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext)) \
    	PROCESS_MODULE(hModuleDinput8, "dinput8.dll") \
    	PROCESS_ENTRYPOINT(DirectInput8Create, hModuleDinput8, HRESULT, (HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter)) \

    #define PROCESS_MODULE(hmodule, filename)

    #define PROCESS_ENTRYPOINT(function, hmodule, returnType, arguments) \
    	typedef returnType ( WINAPI * function ## _fn ) arguments; \
    	extern function ## _fn dx8Dynamic ## function; \

    DX8DYNAMIC_MODULES_AND_ENTRYPOINTS;

    #undef PROCESS_MODULE
    #undef PROCESS_ENTRYPOINT

    #define DX8DYNAMIC_VERSION "1.0"

    extern HRESULT dx8DynamicStartup(void);
    extern HRESULT dx8DynamicStatus(void);
    extern BOOL    dx8DynamicIsAvailable(void);
    extern HRESULT dx8DynamicShutdown(void);

    #ifdef __cplusplus
    	};
    #endif /* __cplusplus */

    #endif /* __DX8DYNAMIC_H */

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.