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

//
// $Log: unreal.cpp $
// Revision 1.31  1998/11/14 20:17:15  MIKE
// Fixed a BIG bug in DSLLV (was using SA instead of GPR[RS]), and added DSLL.
// Revision 1.30  1998/05/25 20:56:47  BREAKPOINT
// Removed debugger code from release builds.  Only usable when _DEBUG is #defined (a debug build).
// Revision 1.29  1998/05/25 04:39:24  BREAKPOINT
// Added 'Call Stack' to debugger which shows the history of JAL[R]/JR RA instructions.
// Revision 1.28  1998/05/24 20:48:57  BREAKPOINT
// Beginning of TLB implementation... fixed c0reg.Context to be 32 bit, changed exception handler to support TLB and modified
// assembly core to be re-entrant.
// Revision 1.27  1998/05/24 14:25:12  BREAKPOINT
// Upped version number after adding all new RDP stuff.
// Revision 1.26  1998/05/18 01:27:09  BREAKPOINT
// Upped version number (again) and added a #define to compile a "Fast" version with REALTIME priority.
// Revision 1.25  1998/05/16 14:21:02  BREAKPOINT
// Upped version number after adding VMULF/VMACF/etc..
// Revision 1.24  1998/05/16 02:50:26  BREAKPOINT
// Added extra debugging code to exception handlers to see if access violation was due to DMA or not.
// Revision 1.23  1998/05/14 08:34:07  BREAKPOINT
// Upped version number after adding basic RDP emulation...
// Revision 1.22  1998/05/06 23:29:42  BREAKPOINT
// Upped version number and made *.bin be a default in the File->Open dialog.
// Revision 1.21  1998/05/03 21:49:29  BREAKPOINT
// Added 'Raw Dump' to debugger to dump out code/data.
// Revision 1.20  1998/04/26 13:55:55  BREAKPOINT
// Moved to .14 after all the vector ops last night.. :)
// Revision 1.19  1998/04/25 12:26:53  BREAKPOINT
// Upped version to 0.4a.13 due to all the RSP stuff working :)
// Revision 1.18  1998/04/19 16:32:57  BREAKPOINT
// Added Trap Exceptions and included TEQ opcode.
// Revision 1.17  1998/04/05 23:50:43  BREAKPOINT
// Added RSP register debug window
// Revision 1.16  1998/04/05 19:46:20  BREAKPOINT
// Added RSP debugger *ONLY* when _DEBUG is defined!
// Revision 1.15  1998/04/05 15:38:16  BREAKPOINT
// Added *.z64 support to file->open.
// Revision 1.14  1998/04/05 01:34:11  BREAKPOINT
// Added exception handling code to determine memory accesses outside the normal N64 memory map.
// Revision 1.13  1998/04/04 16:41:41  BREAKPOINT
// New loading code done, speeds up things a bit :)
// Revision 1.12  1998/04/02 21:18:28  BREAKPOINT
// Implemented new memory mapping routines and changed ROM loading code.
// Revision 1.11  1998/03/29 20:28:58  BREAKPOINT
// Added arithmetic overflow checks to numerous opcodes, removed CACHE callback, added overlow callback.
// Revision 1.10  1998/03/22 22:56:50  BREAKPOINT
// Change to version 0.4a.4.
// Revision 1.9  1998/03/22 17:53:13  BREAKPOINT
// Final changes to fix audio stuff.
// Revision 1.8  1998/03/10 22:02:55  BREAKPOINT
// Second pass with DeltaPlayer in Project UnReality... now working quite
// well, just need streaming buffer playback and static buffers for emulation.
// Revision 1.7  1998/03/10 01:12:58  BREAKPOINT
// First pass at adding DeltaPlayer to Project UnReality...
// Revision 1.6  1998/03/08 22:07:23  BREAKPOINT
// Removed LFE code (no longer need LFE.CPP, LFE.H or LFE_DATA.H)
// Revision 1.5  1998/03/08 19:12:24  BREAKPOINT
// Version 0.3b.3 checkin... I need to really check this stuff in more often..
// Revision 1.4  1998/01/31 19:53:24  BREAKPOINT
// Added code to doubleword swap ROM image.
// Revision 1.3  1998/01/20 00:31:36  BREAKPOINT
// Added new loading routine to dswap() data for faster access...
// Also added 'LastPath' to registry - now File->Open recovers in last
// directory opened with.
// Revision 1.2  1998/01/10 21:57:34  BREAKPOINT
// Initial changes to move to assembly version of R4300i assembly core
// Revision 1.1  1998/01/10 01:28:19  BREAKPOINT
// Initial revision
//

static char *rcsid = "$Id: unreal.cpp 1.31 1998/11/14 20:17:15 MIKE Exp $";

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

#include <stdio.h>
#include <windows.h>
#include <commctrl.h>
#include <types.h>

#include "pcx\title.h"
#define NO_EXTERN_UNREAL_H
#include "unreal.h"
#include "emu.h"
#include "gfx.h"
#include "debugger.h"
#include "resource.h"
#include "cfb.h"
#include "ddraw.h"
#include "R4300i.h"
#include "input.h"
#include "audio.h"
#include "heap.h"
#include "DeltaPlayer.h"
#include "cod_map.h"
#include "rsp.h"

extern "C"
{
    bool running;
    bool want_to_exit;
    HANDLE hRunEvent;
	HANDLE hDebugEvent;

	LPVOID N64Memory, RDRAM, RDRAM_REGS, RDRAM_TEMP_MEM, SP_DATA_MEM, SP_INST_MEM, SP_REGS, SP_PC, SP_IMEM_BIST, DP_CTRL_REGS;
	LPVOID DP_SPAN_REGS, MIPS_REGS, VIDEO_REGS, AUDIO_REGS, PERIF_REGS, RDRAM_INTF_REGS, SERIAL_REGS, PIF_ROM, PIF_RAM, LEO;
	LPVOID CART1DOM;
};

// Include inline functions...
#include "inline.cpp"
#include <windowsx.h>

#define UNREAL_VERSION      "0.4a.-2"

// Uncomment for builds which change priority to REALTIME.
//#define FAST_PUR

HINSTANCE MainhInst;
bool has_focus = FALSE;
bool loaded = FALSE, Load_WantToQuit = FALSE, inDebugger = FALSE, loaded_images = FALSE;
bool stop_on_unhandled = TRUE, log_mmio_reg = FALSE, audiofile_paused = FALSE;
HWND MainhWnd, hWnd_loadprog;
HMENU MainhMenu;
HANDLE hEmuThread;
Unreal_Config cfg;
V64_Header v64h;
blitbuf buf_title;
uchar argv0[256];
ulong ROM_size = 0, win_Xsize, win_Ysize;
bool emu_thread_running = FALSE;
int cont_to_cfg;

EXCEPTION_POINTERS xp;
EXCEPTION_RECORD xr; 
CONTEXT cr; 

typedef struct
{
    bool isNull[16];
    GUID devguid[16];
    char *devdesc[16];
} DEVDATA, *LPDEVDATA;

typedef struct
{
    int id[16];
    char *name[16];
} AudioDevListStruct;

static DEVDATA devinfo;
static AudioDevListStruct AudioDevList;
static int dev_num = 0, cur_aud_num = 0;

LRESULT APIENTRY WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void Read_Config(void);
void Save_Config(void);
void Check_FileTypes(bool *t_v64, bool *t_u64, bool *t_n64, bool *t_bin);
void Register_FileTypes(bool t_v64, bool t_u64, bool t_n64, bool t_bin);

BOOL APIENTRY LoadProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_INITDIALOG:
            SetDlgItemText(hDlg, IDC_LOAD_PCT,  "");
            SetDlgItemText(hDlg, IDC_LOAD_NAME, "");
            SendMessage(hWnd_loadprog, PBM_SETPOS, 0, 0);
            return TRUE;
    }

    return FALSE;
}

bool DoOpenFileDialog(char *fname, int len)
{
    OPENFILENAME ofn;
    BOOL res;
	char *sptr, *dptr;

    fname[0] = 0;
    memset(&ofn, 0, sizeof(ofn));

    ofn.lStructSize		= sizeof(ofn);
    ofn.hwndOwner		= MainhWnd;
    ofn.lpstrFile		= fname;
    ofn.nMaxFile		= len;
	ofn.lpstrInitialDir = (cfg.LastPath[0] == 0) ? NULL : cfg.LastPath;
    ofn.lpstrFilter		= TEXT("ROM Image *.n64,*.u64,*.v64,*.z64,*.bin\0*.n64;*.u64;*.v64;*.z64;*.bin\0All Files *.*\0*.*\0\0");
    ofn.nFilterIndex	= 1;
    ofn.Flags			= OFN_FILEMUSTEXIST;

    res = GetOpenFileName(&ofn);
    if (!res) return FALSE;

	sptr = strchr(fname, '\\');
	if (sptr != NULL)
	{
		sptr++;
		strcpy(cfg.LastPath, fname);

		sptr = cfg.LastPath;
		do
		{
			dptr = strchr(sptr, '\\');
			if (dptr) sptr = dptr + 1;
		} while (dptr != NULL);

		*sptr = 0;
		Save_Config();				// Save new path out to registry...
	}

    return TRUE;
}

void dswap(uchar *dest, uchar *src, ulong blocks)
{
	uchar *sptr = src, *dptr = dest;

	while (blocks--)
	{
		*(dptr    ) = *(sptr + 3);
		*(dptr + 1) = *(sptr + 2);
		*(dptr + 2) = *(sptr + 1);
		*(dptr + 3) = *(sptr    );

		sptr += 4;
		dptr += 4;
	}
}

BOOL APIENTRY PrepareProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_INITDIALOG:
            return TRUE;
    }

    return FALSE;
}

bool LoadROM(char *force_load)
{
    char fname[128], buf[128], *ptr1, *ptr2;
    uchar *rom_ptr, *bswap_buf, *dswap_buf;
    ulong ID, t;
    FARPROC loadproc;
    HWND loaddlg, hWnd_loadfname, hWnd_loadpct;
    MSG msg;
    FILE *rom_fp;
	bool done;
    FARPROC prepareproc;
    HWND preparedlg;

	UnAcquireDevices();
    Load_WantToQuit = FALSE;

    if (force_load == NULL)
    {
        if (DoOpenFileDialog(fname, sizeof(fname)) == FALSE)
            return FALSE;
    }
    else
    {
        strcpy(fname, force_load);
    }

    if (loaded)
    {
        running = FALSE;
        loaded  = FALSE;

        CheckMenuItem(MainhMenu,  ID_FILE_PAUSE,     MF_UNCHECKED);
        EnableMenuItem(MainhMenu, ID_FILE_SAVESTATE, MF_GRAYED);
        EnableMenuItem(MainhMenu, ID_FILE_INFO,      MF_GRAYED);
        EnableMenuItem(MainhMenu, ID_FILE_RESET,     MF_GRAYED);
        EnableMenuItem(MainhMenu, ID_FILE_PAUSE,     MF_GRAYED);
    }

    // Disable menus so we can be non-reentrant.
    EnableMenuItem(MainhMenu, ID_FILE_OPENROM,   MF_GRAYED);
    EnableMenuItem(MainhMenu, ID_FILE_LOADSTATE, MF_GRAYED);
    EnableMenuItem(MainhMenu, ID_FILE_EXIT,      MF_GRAYED);
    EnableMenuItem(MainhMenu, ID_HELP_ABOUT,     MF_GRAYED);

#ifdef _DEBUG
    EnableMenuItem(MainhMenu, ID_DEBUG_ENTERDEBUGGER, MF_GRAYED);
#endif // _DEBUG

	// Put up a dialog saying we're resetting memory...
    prepareproc = MakeProcInstance((FARPROC)PrepareProc, MainhInst);
    preparedlg  = CreateDialog(MainhInst, MAKEINTRESOURCE(IDD_PREPARING_MEMORY), MainhWnd, prepareproc);

	// Zap current Cartridge 1 Domain.
	if (ROM_size == 0)
		memset(CART1DOM, 0, CART1DOM_SIZE);
	else
		memset(CART1DOM, 0, ROM_size);

	// Load the ROM!
    rom_fp = fopen(fname, "rb");
    if (rom_fp == NULL)
    {
        MessageBox(MainhWnd, "Unable to open ROM!", NULL, MB_OK);
        return FALSE;
    }

    fread(&ID, 1, 4, rom_fp);
    if (ID != 0x12408037 && ID != 0x40123780)
    {
        MessageBox(MainhWnd, "Not a valid ROM image!", NULL, MB_OK);
        return FALSE;
    }

	// Allocate dword swap buffer.
	dswap_buf = new uchar[16384];

    // Allocate byte swap buffer, if necessary.
    if (ID == 0x12408037)
        bswap_buf = new uchar[16384];

    // Remove the dialog box.
    DestroyWindow(preparedlg);
    FreeProcInstance(prepareproc);

    fseek(rom_fp, 0, 2);
    ROM_size = ftell(rom_fp);
    rewind(rom_fp);

    rom_ptr = (uchar *)CART1DOM;

    // Open 'Loading ROM' modeless dialog...
    loadproc = MakeProcInstance((FARPROC)LoadProc, MainhInst);
    loaddlg = CreateDialog(MainhInst, MAKEINTRESOURCE(IDD_LOADING), MainhWnd, loadproc);
    
    hWnd_loadfname = GetDlgItem(loaddlg, IDC_LOAD_NAME);
    hWnd_loadpct   = GetDlgItem(loaddlg, IDC_LOAD_PCT);
    hWnd_loadprog  = GetDlgItem(loaddlg, IDC_LOAD_PROGRESS);

    SendMessage(hWnd_loadprog, PBM_SETRANGE, 0, MAKELPARAM(0, ROM_size / 16384));
    SendMessage(hWnd_loadprog, PBM_SETSTEP, (WPARAM)1, 0);

    sprintf(buf, "Loading %s...  [%d Mbit]", fname, ROM_size / 131072);
    SetDlgItemText(loaddlg, IDC_LOAD_NAME, buf);

	// ******** Load 16k aligned ROM data ********
    t = ROM_size / 16384;
    while (t--)
    {
        SendMessage(hWnd_loadprog, PBM_STEPIT, 0, 0);
        
        sprintf(buf, "%d%%", (int)(((float)ftell(rom_fp) / (float)ROM_size) * 100.0));
        SetDlgItemText(loaddlg, IDC_LOAD_PCT, buf);

        // Handle byte swapping, if necessary.
        if (ID == 0x12408037)
        {
            fread(bswap_buf, 1, 16384, rom_fp);
            _swab((char *)bswap_buf, (char *)dswap_buf, 16384);
        }
        else
            fread(dswap_buf, 1, 16384, rom_fp);

		dswap(rom_ptr, dswap_buf, 4096);
        rom_ptr += 16384;

        if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
        {
            GetMessage(&msg, NULL, 0, 0);
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        if (Load_WantToQuit)
        {
            running = FALSE;
            loaded  = FALSE;

            CheckMenuItem(MainhMenu,  ID_FILE_PAUSE,     MF_UNCHECKED);
            EnableMenuItem(MainhMenu, ID_FILE_SAVESTATE, MF_GRAYED);
            EnableMenuItem(MainhMenu, ID_FILE_INFO,      MF_GRAYED);
            EnableMenuItem(MainhMenu, ID_FILE_RESET,     MF_GRAYED);
            EnableMenuItem(MainhMenu, ID_FILE_PAUSE,     MF_GRAYED);

            if (ID == 0x12408037)
                delete []bswap_buf;
            fclose(rom_fp);

            DestroyWindow(loaddlg);
            FreeProcInstance(loadproc);
            return FALSE;
        }
    }

	// ******** Load remainder of ROM data ********
    t = ROM_size % 16384;
	if (t)
	{
		memset(dswap_buf, 0, 16384);

		// Handle byte swapping, if necessary.
		if (ID == 0x12408037)
		{
			memset(bswap_buf, 0, 16384);
			fread(bswap_buf, 1, t, rom_fp);
			_swab((char *)bswap_buf, (char *)dswap_buf, 16384);
		}
		else
			fread(dswap_buf, 1, t, rom_fp);

		dswap(rom_ptr, dswap_buf, 4096);
	}

    if (ID == 0x12408037)
        delete []bswap_buf;

	delete []dswap_buf;

    memset(&v64h, 0, sizeof(V64_Header));
    memcpy(&v64h.Filename, &fname, 255);

    v64h.EntryPoint = *((ulong *)&((uchar *)CART1DOM)[0x08]);
    v64h.Checksum1  = *((ulong *)&((uchar *)CART1DOM)[0x10]);
    v64h.Checksum2  = *((ulong *)&((uchar *)CART1DOM)[0x14]);

	// Use 'buf' to temporarily hold the header of the V64 image.
	dswap((uchar *)buf, &((uchar *)CART1DOM)[0], 16);
    memcpy(&v64h.Name, &buf[0x20], 20);
    memcpy(&v64h.ID,   &buf[0x3C],  2);
    v64h.Manufacturer = buf[0x3B];
    v64h.Country      = buf[0x3E];

    // Load any COD or MAP file that might be available to this ROM.
    LoadCOD_MAP(fname);

    InitEmulation();
    loaded = TRUE;

    EnableMenuItem(MainhMenu, ID_FILE_SAVESTATE, MF_ENABLED);
    EnableMenuItem(MainhMenu, ID_FILE_INFO,      MF_ENABLED);
    EnableMenuItem(MainhMenu, ID_FILE_RESET,     MF_ENABLED);
    EnableMenuItem(MainhMenu, ID_FILE_PAUSE,     MF_ENABLED);

    // Reenable menus that we disabled earlier...
    EnableMenuItem(MainhMenu, ID_FILE_OPENROM,   MF_ENABLED);
    EnableMenuItem(MainhMenu, ID_FILE_LOADSTATE, MF_ENABLED);
    EnableMenuItem(MainhMenu, ID_FILE_EXIT,      MF_ENABLED);
    EnableMenuItem(MainhMenu, ID_HELP_ABOUT,     MF_ENABLED);

#ifdef _DEBUG
    EnableMenuItem(MainhMenu, ID_DEBUG_ENTERDEBUGGER, MF_ENABLED);
#endif // _DEBUG

    DestroyWindow(loaddlg);
    FreeProcInstance(loadproc);

    if (inDebugger) RefreshDebugAll();

    loaded_images = FALSE;
	CFB_BitDepth = 8;
    Clear();
    Flip();

//    ShowCursor(FALSE);
//    SetCursor(NULL);

    InitLog();

    if (!inDebugger)
    {
        running = TRUE;
        SetEvent(hRunEvent);

		// Acquire emulation-related devices for DirectInput...
		AcquireDevices(MainhWnd);
    }

	// Cosmetic: set window title bar to filename of ROM.
	ptr1 = (char *)&v64h.Filename[0];
	done = FALSE;

	while (!done)
	{
		ptr2 = strchr(ptr1, '\\');

		if (ptr2)
			ptr1 = (ptr2 + 1);
		else
			done = TRUE;
	}

	sprintf(buf, "[ %s ] - Project UnReality %s", ptr1, UNREAL_VERSION);
	SetWindowText(MainhWnd, buf);
	return TRUE;
}

char *V64ManufToStr(char m)
{
    static char buf[64];

    switch (m)
    {
        case 'N': strcpy(buf, "Nintendo"); break;
        default:  strcpy(buf, "Unknown");  break;
    }

    return buf;
}

char *V64CountryToStr(char c)
{
    static char buf[64];

    switch (c)
    {
        case 'E': strcpy(buf, "America"); break;
        case 'P': strcpy(buf, "Europe");  break;
        case 'J': strcpy(buf, "Japan");   break;
        default:  strcpy(buf, "Unknown"); break;
    }

    return buf;
}

BOOL APIENTRY ROMProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    char buf[128];

    switch (message)
    {
        case WM_INITDIALOG:
            SetDlgItemText(hDlg, IDC_ROM_FILENAME, (char *)v64h.Filename);
            SetDlgItemText(hDlg, IDC_ROM_NAME,     (char *)v64h.Name);
            SetDlgItemText(hDlg, IDC_ROM_CARTID,   (char *)v64h.ID);
            
            sprintf(buf, "%c [%s]", (v64h.Manufacturer == 0) ? ' ' : v64h.Manufacturer, V64ManufToStr(v64h.Manufacturer));
            SetDlgItemText(hDlg, IDC_ROM_MANUFACTURER, buf);

            sprintf(buf, "%c [%s]", (v64h.Country == 0) ? ' ' : v64h.Country, V64CountryToStr(v64h.Country));
            SetDlgItemText(hDlg, IDC_ROM_COUNTRY, buf);

            sprintf(buf, "%08Xh", v64h.EntryPoint);
            SetDlgItemText(hDlg, IDC_ROM_ENTRYPOINT, buf);

            sprintf(buf, "%08Xh", v64h.Checksum1);
            SetDlgItemText(hDlg, IDC_ROM_CHECKSUM1, buf);

            sprintf(buf, "%08Xh", v64h.Checksum2);
            SetDlgItemText(hDlg, IDC_ROM_CHECKSUM2, buf);
            return TRUE;

        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK)
                EndDialog(hDlg, TRUE);
            return TRUE;
    }

    return FALSE;
}

BOOL APIENTRY AboutBoxProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_INITDIALOG:
            SetDlgItemText(hDlg, IDC_TEXT_VERSION, UNREAL_VERSION);
            return TRUE;

        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK)
                EndDialog(hDlg, TRUE);
            return TRUE;
    }

    return FALSE;
}

BOOL WINAPI DDrawDriverListCallback(LPGUID lpGuid, LPTSTR lpstrDescription, LPTSTR lpstrModule, LPVOID lpContext)
{
    LPDEVDATA lpDev = (LPDEVDATA)lpContext;
    GUID *g = lpGuid;

    if (lpGuid == NULL)
    {
        lpDev->isNull[dev_num] = TRUE;
    }
    else
    {
        lpDev->isNull[dev_num] = FALSE;
        memcpy(&lpDev->devguid[dev_num], lpGuid, sizeof(GUID));
    }

    lpDev->devdesc[dev_num] = (char *)malloc(strlen(lpstrDescription) + 1);
    strcpy(lpDev->devdesc[dev_num], lpstrDescription);

    dev_num++;
    if (dev_num == 16) return FALSE;
    return TRUE;
}

BOOL APIENTRY VideoOptsProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    int t;

    switch (message)
    {
        case WM_INITDIALOG:
            dev_num = 0;
            for (t = 0; t < 16; t++)
                devinfo.devdesc[t] = NULL;

            DirectDrawEnumerate((LPDDENUMCALLBACK)&DDrawDriverListCallback, &devinfo);
            SendMessage(GetDlgItem(hDlg, IDC_DDRAW_DEV), CB_INITSTORAGE, 16, 1024);

            for (t = 0; t < dev_num; t++)
                SendMessage(GetDlgItem(hDlg, IDC_DDRAW_DEV), CB_ADDSTRING, 0, (LPARAM)devinfo.devdesc[t]);

            SendMessage(GetDlgItem(hDlg, IDC_DDRAW_DEV), CB_SETCURSEL, cfg.DDrawDev, 0);

            if (cfg.Windowed)
            {
                CheckRadioButton(hDlg, IDC_OUT_WINDOWED, IDC_OUT_FULLSCREEN, IDC_OUT_WINDOWED);
        	    EnableWindow(GetDlgItem(hDlg, IDC_OUT_RESIZEABLE), TRUE);
        	    EnableWindow(GetDlgItem(hDlg, IDC_DDRAW_DEV),  FALSE);
            }
            else
            {
                CheckRadioButton(hDlg, IDC_OUT_WINDOWED, IDC_OUT_FULLSCREEN, IDC_OUT_FULLSCREEN);
        	    EnableWindow(GetDlgItem(hDlg, IDC_OUT_RESIZEABLE), FALSE);
        	    EnableWindow(GetDlgItem(hDlg, IDC_DDRAW_DEV),  TRUE);
            }

            if (cfg.Resizeable)
                CheckDlgButton(hDlg, IDC_OUT_RESIZEABLE, BST_CHECKED);
            else
                CheckDlgButton(hDlg, IDC_OUT_RESIZEABLE, BST_UNCHECKED);

			if (cfg.Upscale320)
				CheckDlgButton(hDlg, IDC_UP320, BST_CHECKED);
			else
				CheckDlgButton(hDlg, IDC_UP320, BST_UNCHECKED);

			if (cfg.ShowFPS)
				CheckDlgButton(hDlg, IDC_SHOWFPS, BST_CHECKED);
			else
				CheckDlgButton(hDlg, IDC_SHOWFPS, BST_UNCHECKED);

			if (cfg.FPSLock)
				CheckDlgButton(hDlg, IDC_FPSLOCK, BST_CHECKED);
			else
				CheckDlgButton(hDlg, IDC_FPSLOCK, BST_UNCHECKED);
            return TRUE;

        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
                case IDOK:
                    // Update config block with data from dialog.
                    if (IsDlgButtonChecked(hDlg, IDC_OUT_WINDOWED) == BST_CHECKED)
                        cfg.Windowed = TRUE;
                    else
                        cfg.Windowed = FALSE;

                    if (IsDlgButtonChecked(hDlg, IDC_OUT_RESIZEABLE) == BST_CHECKED)
                        cfg.Resizeable = TRUE;
                    else
                        cfg.Resizeable = FALSE;

					if (IsDlgButtonChecked(hDlg, IDC_UP320) == BST_CHECKED)
						cfg.Upscale320 = TRUE;
					else
						cfg.Upscale320 = FALSE;

					if (IsDlgButtonChecked(hDlg, IDC_SHOWFPS) == BST_CHECKED)
						cfg.ShowFPS = TRUE;
					else
						cfg.ShowFPS = FALSE;

					if (IsDlgButtonChecked(hDlg, IDC_FPSLOCK) == BST_CHECKED)
					{
						if (cfg.FPSLock == FALSE)
						{
							// Start up the sync thread so it'll work.
							CFB_StartSyncThread();
						}

						cfg.FPSLock = TRUE;
					}
					else
					{
						if (cfg.FPSLock == TRUE)
						{
							// Stop the sync thread to increase performance.
							CFB_StopSyncThread();
						}

						cfg.FPSLock = FALSE;
					}

                    cfg.DDrawDev = SendMessage(GetDlgItem(hDlg, IDC_DDRAW_DEV), CB_GETCURSEL, 0, 0);

					// Check to see if fullscreen is on and upscale is off.  If so, make sure 320x240x16 and x32 exist.
					// If not, warn user.
					if ((cfg.Windowed == FALSE) && (cfg.Upscale320 == FALSE))
					{
						if (ModeExist(320, 240, 16) == FALSE)
						{
							MessageBox(MainhWnd, "Your display driver does not support DirectDraw in 320x240 @ 16bpp mode.\n\n"
								"In order to use fullscreen, you will need to check the 'Render 320x240 as 640x480' box.", NULL, MB_OK);
							return TRUE;
						}

						if (ModeExist(320, 240, 32) == FALSE)
						{
							MessageBox(MainhWnd, "Your display driver does not support DirectDraw in 320x240 @ 32bpp mode.\n\n"
								"In order to use fullscreen, you will need to check the 'Render 320x240 as 640x480' box.", NULL, MB_OK);
							return TRUE;
						}
					}

					// Reset the CFB only if it's been previously initialized.
					if (CFB_BitDepth != 8) CFB_Reset();

                    // Save config block to registry.
                    Save_Config();
                    EndDialog(hDlg, TRUE);
                    break;

                case IDCANCEL:
                    EndDialog(hDlg, TRUE);
                    break;

                case IDC_OUT_WINDOWED:
                    CheckRadioButton(hDlg, IDC_OUT_WINDOWED, IDC_OUT_FULLSCREEN, IDC_OUT_WINDOWED);
        	        EnableWindow(GetDlgItem(hDlg, IDC_OUT_RESIZEABLE), TRUE);
        	        EnableWindow(GetDlgItem(hDlg, IDC_DDRAW_DEV), FALSE);
                    break;

                case IDC_OUT_FULLSCREEN:
                    CheckRadioButton(hDlg, IDC_OUT_WINDOWED, IDC_OUT_FULLSCREEN, IDC_OUT_FULLSCREEN);
        	        EnableWindow(GetDlgItem(hDlg, IDC_OUT_RESIZEABLE), FALSE);
        	        EnableWindow(GetDlgItem(hDlg, IDC_DDRAW_DEV), TRUE);
                    break;
            }
            return TRUE;
    }

    return FALSE;
}

void list_drivers_callback(uint drv, uchar *name)
{
    char *nm = (char *)name;

    if (cur_aud_num == 16) return;

    AudioDevList.id[cur_aud_num] = drv;
    AudioDevList.name[cur_aud_num] = new char[strlen(nm) + 1];
    strcpy(AudioDevList.name[cur_aud_num], nm);
    cur_aud_num++;
}

void GetAudioDevList(HWND hDlg)
{
    int t;

    for (t = 0; t < cur_aud_num; t++)
    {
        if (AudioDevList.name[t])
        {
            delete AudioDevList.name[t];
            AudioDevList.name[t] = NULL;
        }
    }

    cur_aud_num = 0;

    if (cfg.ForceSoftwareMix)
        audio_dev = new Device(DEVICE_WAVEOUT, DEVMODE_PLAYBACK, MainhWnd, 0);
    else
        audio_dev = new Device(DEVMODE_PLAYBACK, MainhWnd, 0);

    audio_dev->ListDrivers(list_drivers_callback);

    delete audio_dev;
    audio_dev = NULL;

    SendMessage(GetDlgItem(hDlg, IDC_SOUND_DEVICE), CB_INITSTORAGE, 16, 1024);
    SendMessage(GetDlgItem(hDlg, IDC_SOUND_DEVICE), CB_RESETCONTENT, 0, 0);

    for (t = 0; t < cur_aud_num; t++)
        SendMessage(GetDlgItem(hDlg, IDC_SOUND_DEVICE), CB_ADDSTRING, 0, (LPARAM)AudioDevList.name[t]);

    SendMessage(GetDlgItem(hDlg, IDC_SOUND_DEVICE), CB_SETCURSEL, cfg.SoundDev, 0);
}

BOOL APIENTRY AudioOptsProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_INITDIALOG:
            GetAudioDevList(hDlg);

            if (cfg.DisableAudio)
				CheckDlgButton(hDlg, IDC_DISABLE_AUDIO, BST_CHECKED);
			else
				CheckDlgButton(hDlg, IDC_DISABLE_AUDIO, BST_UNCHECKED);

            if (cfg.ForceSoftwareMix)
				CheckDlgButton(hDlg, IDC_SOFTWARE_MIXING, BST_CHECKED);
			else
				CheckDlgButton(hDlg, IDC_SOFTWARE_MIXING, BST_UNCHECKED);

            if (cfg.ThreadMix)
				CheckDlgButton(hDlg, IDC_THREAD_MIXING, BST_CHECKED);
			else
				CheckDlgButton(hDlg, IDC_THREAD_MIXING, BST_UNCHECKED);

            switch (cfg.SoundBitDepth)
            {
                case 8:  CheckRadioButton(hDlg, IDC_8BIT, IDC_16BIT, IDC_8BIT);  break;
                case 16: CheckRadioButton(hDlg, IDC_8BIT, IDC_16BIT, IDC_16BIT); break;
            }

            switch (cfg.SoundMixRate)
            {
                case 48: CheckRadioButton(hDlg, IDC_48KHZ, IDC_11KHZ, IDC_48KHZ); break;
                case 44: CheckRadioButton(hDlg, IDC_48KHZ, IDC_11KHZ, IDC_44KHZ); break;
                case 22: CheckRadioButton(hDlg, IDC_48KHZ, IDC_11KHZ, IDC_22KHZ); break;
                case 11: CheckRadioButton(hDlg, IDC_48KHZ, IDC_11KHZ, IDC_11KHZ); break;
            }

            switch (cfg.SoundChannels)
            {
                case 1: CheckRadioButton(hDlg, IDC_MONO, IDC_STEREO, IDC_MONO);   break;
                case 2: CheckRadioButton(hDlg, IDC_MONO, IDC_STEREO, IDC_STEREO); break;
            }

            if (cfg.SoundHighQ)
				CheckDlgButton(hDlg, IDC_HIGHQ, BST_CHECKED);
			else
				CheckDlgButton(hDlg, IDC_HIGHQ, BST_UNCHECKED);

            if (cfg.SoundInterp)
				CheckDlgButton(hDlg, IDC_INTERP, BST_CHECKED);
			else
				CheckDlgButton(hDlg, IDC_INTERP, BST_UNCHECKED);

            switch (cfg.SoundPriority)
            {
                case 0: CheckRadioButton(hDlg, IDC_PRI_NORMAL, IDC_PRI_REALTIME, IDC_PRI_NORMAL);   break;
                case 1: CheckRadioButton(hDlg, IDC_PRI_NORMAL, IDC_PRI_REALTIME, IDC_PRI_HIGH);     break;
                case 2: CheckRadioButton(hDlg, IDC_PRI_NORMAL, IDC_PRI_REALTIME, IDC_PRI_REALTIME); break;
            }

            if (cfg.SoundEmuCubic)
				CheckDlgButton(hDlg, IDC_EMU_CUBIC, BST_CHECKED);
			else
				CheckDlgButton(hDlg, IDC_EMU_CUBIC, BST_UNCHECKED);

            if (cfg.SoundEmuXTCPlay)
				CheckDlgButton(hDlg, IDC_EMU_XTCP, BST_CHECKED);
			else
				CheckDlgButton(hDlg, IDC_EMU_XTCP, BST_UNCHECKED);
            return TRUE;

        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
                case IDDETECT:
                    Audio_Autodetect();
                    SendMessage(hDlg, WM_INITDIALOG, 0, 0);
                    break;

                case IDC_SOFTWARE_MIXING:
                    GetAudioDevList(hDlg);
                    break;

                case IDOK:
                    // Update config block with data from dialog.
                    cfg.SoundDev = SendMessage(GetDlgItem(hDlg, IDC_SOUND_DEVICE), CB_GETCURSEL, 0, 0);

                    if (IsDlgButtonChecked(hDlg, IDC_DISABLE_AUDIO) == BST_CHECKED)
                        cfg.DisableAudio = TRUE;
                    else
                        cfg.DisableAudio = FALSE;

                    if (IsDlgButtonChecked(hDlg, IDC_SOFTWARE_MIXING) == BST_CHECKED)
                        cfg.ForceSoftwareMix = TRUE;
                    else
                        cfg.ForceSoftwareMix = FALSE;

                    if (IsDlgButtonChecked(hDlg, IDC_THREAD_MIXING) == BST_CHECKED)
                        cfg.ThreadMix = TRUE;
                    else
                        cfg.ThreadMix = FALSE;

                    if (IsDlgButtonChecked(hDlg, IDC_8BIT) == BST_CHECKED)
                        cfg.SoundBitDepth = 8;
                    else
                        cfg.SoundBitDepth = 16;

					if (IsDlgButtonChecked(hDlg, IDC_11KHZ) == BST_CHECKED)
						cfg.SoundMixRate = 11;
					else if (IsDlgButtonChecked(hDlg, IDC_22KHZ) == BST_CHECKED)
						cfg.SoundMixRate = 22;
					else if (IsDlgButtonChecked(hDlg, IDC_44KHZ) == BST_CHECKED)
						cfg.SoundMixRate = 44;
					else if (IsDlgButtonChecked(hDlg, IDC_48KHZ) == BST_CHECKED)
						cfg.SoundMixRate = 48;

                    if (IsDlgButtonChecked(hDlg, IDC_MONO) == BST_CHECKED)
                        cfg.SoundChannels = 1;
                    else
                        cfg.SoundChannels = 2;

                    if (IsDlgButtonChecked(hDlg, IDC_HIGHQ) == BST_CHECKED)
                        cfg.SoundHighQ = TRUE;
                    else
                        cfg.SoundHighQ = FALSE;

                    if (IsDlgButtonChecked(hDlg, IDC_INTERP) == BST_CHECKED)
                        cfg.SoundInterp = TRUE;
                    else
                        cfg.SoundInterp = FALSE;

					if (IsDlgButtonChecked(hDlg, IDC_PRI_NORMAL) == BST_CHECKED)
						cfg.SoundPriority = 0;
					else if (IsDlgButtonChecked(hDlg, IDC_PRI_HIGH) == BST_CHECKED)
						cfg.SoundPriority = 1;
					else if (IsDlgButtonChecked(hDlg, IDC_PRI_REALTIME) == BST_CHECKED)
						cfg.SoundPriority = 2;

                    if (IsDlgButtonChecked(hDlg, IDC_EMU_CUBIC) == BST_CHECKED)
                        cfg.SoundEmuCubic = TRUE;
                    else
                        cfg.SoundEmuCubic = FALSE;

                    if (IsDlgButtonChecked(hDlg, IDC_EMU_XTCP) == BST_CHECKED)
                        cfg.SoundEmuXTCPlay = TRUE;
                    else
                        cfg.SoundEmuXTCPlay = FALSE;

                    // Save config block to registry.
                    Save_Config();
                    EndDialog(hDlg, TRUE);
                    return TRUE;

                case IDCANCEL:
                    EndDialog(hDlg, TRUE);
                    return TRUE;
            }
            break;
    }

    return FALSE;
}

BOOL APIENTRY RegisterFileTypesProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    bool t_v64, t_u64, t_n64, t_bin;

    switch (message)
    {
        case WM_INITDIALOG:
            CheckDlgButton(hDlg, IDC_REG_V64, BST_UNCHECKED);
            CheckDlgButton(hDlg, IDC_REG_U64, BST_UNCHECKED);
            CheckDlgButton(hDlg, IDC_REG_N64, BST_UNCHECKED);
            CheckDlgButton(hDlg, IDC_REG_BIN, BST_UNCHECKED);

            Check_FileTypes(&t_v64, &t_u64, &t_n64, &t_bin);

            if (t_v64) CheckDlgButton(hDlg, IDC_REG_V64, BST_CHECKED);
            if (t_u64) CheckDlgButton(hDlg, IDC_REG_U64, BST_CHECKED);
            if (t_n64) CheckDlgButton(hDlg, IDC_REG_N64, BST_CHECKED);
            if (t_bin) CheckDlgButton(hDlg, IDC_REG_BIN, BST_CHECKED);
            return TRUE;

        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK)
            {
                t_v64 = FALSE;
                t_u64 = FALSE;
                t_n64 = FALSE;
                t_bin = FALSE;

                if (IsDlgButtonChecked(hDlg, IDC_REG_V64) == BST_CHECKED) t_v64 = TRUE;
                if (IsDlgButtonChecked(hDlg, IDC_REG_U64) == BST_CHECKED) t_u64 = TRUE;
                if (IsDlgButtonChecked(hDlg, IDC_REG_N64) == BST_CHECKED) t_n64 = TRUE;
                if (IsDlgButtonChecked(hDlg, IDC_REG_BIN) == BST_CHECKED) t_bin = TRUE;

                Register_FileTypes(t_v64, t_u64, t_n64, t_bin);
                EndDialog(hDlg, TRUE);
                return TRUE;
            }

            if (LOWORD(wParam) == IDCANCEL)
                EndDialog(hDlg, TRUE);
            return TRUE;
    }

    return FALSE;
}

BOOL APIENTRY ConfigureControllerProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	static bool in_device_config = FALSE;
	char buf[64];
	uchar foo;

    switch (message)
    {
        case WM_INITDIALOG:
			sprintf(buf, "Controller %d", cont_to_cfg + 1);
			SetDlgItemText(hDlg, IDC_CONT_ENABLED, buf);

			if (cfg.Cont[cont_to_cfg].Toggle_Active == FALSE)
			{
				// If toggle not active, allow analog input.
				EnableWindow(GetDlgItem(hDlg, IDC_TOGGLE), FALSE);
				EnableWindow(GetDlgItem(hDlg, IDC_ANALOG), TRUE);
	            CheckDlgButton(hDlg, IDC_TOGGLE_ACTIVE, BST_UNCHECKED);
			}
			else
			{
				EnableWindow(GetDlgItem(hDlg, IDC_TOGGLE), TRUE);
				EnableWindow(GetDlgItem(hDlg, IDC_ANALOG), FALSE);
	            CheckDlgButton(hDlg, IDC_TOGGLE_ACTIVE, BST_CHECKED);

				CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type_Toggle, cfg.Cont[cont_to_cfg].Val_Toggle);
				SetDlgItemText(hDlg, IDC_TOGGLE, buf);
			}

			if (cfg.Cont[cont_to_cfg].Controller_Active == FALSE)
			{
	            CheckDlgButton(hDlg, IDC_CONT_ENABLED, BST_UNCHECKED);

        	    EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_A), FALSE);
        	    EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_B), FALSE);
        	    EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_Z), FALSE);
        	    EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_L), FALSE);
        	    EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_R), FALSE);
        	    EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_START), FALSE);
        	    EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_C_UP), FALSE);
        	    EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_C_LEFT), FALSE);
        	    EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_C_RIGHT), FALSE);
        	    EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_C_DOWN), FALSE);

        	    EnableWindow(GetDlgItem(hDlg, IDC_DPAD_UP), FALSE);
        	    EnableWindow(GetDlgItem(hDlg, IDC_DPAD_LEFT), FALSE);
        	    EnableWindow(GetDlgItem(hDlg, IDC_DPAD_RIGHT), FALSE);
        	    EnableWindow(GetDlgItem(hDlg, IDC_DPAD_DOWN), FALSE);
        	    EnableWindow(GetDlgItem(hDlg, IDC_ANALOG), FALSE);

        	    EnableWindow(GetDlgItem(hDlg, IDC_TOGGLE_ACTIVE), FALSE);
        	    EnableWindow(GetDlgItem(hDlg, IDC_TOGGLE), FALSE);
			}
			else
			{
	            CheckDlgButton(hDlg, IDC_CONT_ENABLED, BST_CHECKED);
			}

			CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_A], cfg.Cont[cont_to_cfg].Val[BUTTON_A]); SetDlgItemText(hDlg, IDC_BUTTON_A, buf);
			CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_B], cfg.Cont[cont_to_cfg].Val[BUTTON_B]); SetDlgItemText(hDlg, IDC_BUTTON_B, buf);
			CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_Z], cfg.Cont[cont_to_cfg].Val[BUTTON_Z]); SetDlgItemText(hDlg, IDC_BUTTON_Z, buf);
			CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_L], cfg.Cont[cont_to_cfg].Val[BUTTON_L]); SetDlgItemText(hDlg, IDC_BUTTON_L, buf);
			CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_R], cfg.Cont[cont_to_cfg].Val[BUTTON_R]); SetDlgItemText(hDlg, IDC_BUTTON_R, buf);
			CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_START], cfg.Cont[cont_to_cfg].Val[BUTTON_START]); SetDlgItemText(hDlg, IDC_BUTTON_START, buf);
			CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_C_UP], cfg.Cont[cont_to_cfg].Val[BUTTON_C_UP]); SetDlgItemText(hDlg, IDC_BUTTON_C_UP, buf);
			CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_C_LEFT], cfg.Cont[cont_to_cfg].Val[BUTTON_C_LEFT]); SetDlgItemText(hDlg, IDC_BUTTON_C_LEFT, buf);
			CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_C_RIGHT], cfg.Cont[cont_to_cfg].Val[BUTTON_C_RIGHT]); SetDlgItemText(hDlg, IDC_BUTTON_C_RIGHT, buf);
			CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_C_DOWN], cfg.Cont[cont_to_cfg].Val[BUTTON_C_DOWN]); SetDlgItemText(hDlg, IDC_BUTTON_C_DOWN, buf);
			CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_DPAD_UP], cfg.Cont[cont_to_cfg].Val[BUTTON_DPAD_UP]); SetDlgItemText(hDlg, IDC_DPAD_UP, buf);
			CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_DPAD_LEFT], cfg.Cont[cont_to_cfg].Val[BUTTON_DPAD_LEFT]); SetDlgItemText(hDlg, IDC_DPAD_LEFT, buf);
			CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_DPAD_RIGHT], cfg.Cont[cont_to_cfg].Val[BUTTON_DPAD_RIGHT]); SetDlgItemText(hDlg, IDC_DPAD_RIGHT, buf);
			CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_DPAD_DOWN], cfg.Cont[cont_to_cfg].Val[BUTTON_DPAD_DOWN]); SetDlgItemText(hDlg, IDC_DPAD_DOWN, buf);

			CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type_Analog, 0); SetDlgItemText(hDlg, IDC_ANALOG, buf);
			CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type_Toggle, cfg.Cont[cont_to_cfg].Val_Toggle); SetDlgItemText(hDlg, IDC_TOGGLE, buf);
            return TRUE;

        case WM_COMMAND:
			// Check for SETFOCUS re-entry.
			if (HIWORD(wParam) == CBN_SETFOCUS)
			{
				if (in_device_config == TRUE)
					return FALSE;

				in_device_config = TRUE;
			}

			switch (LOWORD(wParam))
			{
				case ID_DONE:
					if (ScanControllersForDupes() == FALSE)
					{
						Save_Config();
						EndDialog(hDlg, TRUE);
					}
					return TRUE;

				case IDCANCEL:
					Read_Config();
			        EndDialog(hDlg, TRUE);
					return TRUE;

				case IDC_TOGGLE_ACTIVE:
					if (IsDlgButtonChecked(hDlg, IDC_TOGGLE_ACTIVE) == BST_CHECKED)
					{
						cfg.Cont[cont_to_cfg].Toggle_Active = TRUE;
						EnableWindow(GetDlgItem(hDlg, IDC_TOGGLE), TRUE);
						EnableWindow(GetDlgItem(hDlg, IDC_ANALOG), FALSE);
					}
					else
					{
						cfg.Cont[cont_to_cfg].Toggle_Active = FALSE;
						EnableWindow(GetDlgItem(hDlg, IDC_TOGGLE), FALSE);
						EnableWindow(GetDlgItem(hDlg, IDC_ANALOG), TRUE);
					}
					return TRUE;

				case IDC_CONT_ENABLED:
	                if (IsDlgButtonChecked(hDlg, IDC_CONT_ENABLED) == BST_CHECKED)
					{
						cfg.Cont[cont_to_cfg].Controller_Active = TRUE;

        				EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_A), TRUE);
        				EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_B), TRUE);
        				EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_Z), TRUE);
        				EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_L), TRUE);
        				EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_R), TRUE);
        				EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_START), TRUE);
        				EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_C_UP), TRUE);
        				EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_C_LEFT), TRUE);
        				EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_C_RIGHT), TRUE);
        				EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_C_DOWN), TRUE);

        				EnableWindow(GetDlgItem(hDlg, IDC_DPAD_UP), TRUE);
        				EnableWindow(GetDlgItem(hDlg, IDC_DPAD_LEFT), TRUE);
        				EnableWindow(GetDlgItem(hDlg, IDC_DPAD_RIGHT), TRUE);
        				EnableWindow(GetDlgItem(hDlg, IDC_DPAD_DOWN), TRUE);
        				EnableWindow(GetDlgItem(hDlg, IDC_ANALOG), TRUE);

        				EnableWindow(GetDlgItem(hDlg, IDC_TOGGLE_ACTIVE), TRUE);
						if (IsDlgButtonChecked(hDlg, IDC_TOGGLE_ACTIVE) == BST_CHECKED)
						{
							EnableWindow(GetDlgItem(hDlg, IDC_TOGGLE), TRUE);
							EnableWindow(GetDlgItem(hDlg, IDC_ANALOG), FALSE);
						}
						else
						{
							EnableWindow(GetDlgItem(hDlg, IDC_TOGGLE), FALSE);
							EnableWindow(GetDlgItem(hDlg, IDC_ANALOG), TRUE);
						}
					}
					else
					{
						cfg.Cont[cont_to_cfg].Controller_Active = FALSE;

        				EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_A), FALSE);
        				EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_B), FALSE);
        				EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_Z), FALSE);
        				EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_L), FALSE);
        				EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_R), FALSE);
        				EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_START), FALSE);
        				EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_C_UP), FALSE);
        				EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_C_LEFT), FALSE);
        				EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_C_RIGHT), FALSE);
        				EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_C_DOWN), FALSE);

        				EnableWindow(GetDlgItem(hDlg, IDC_DPAD_UP), FALSE);
        				EnableWindow(GetDlgItem(hDlg, IDC_DPAD_LEFT), FALSE);
        				EnableWindow(GetDlgItem(hDlg, IDC_DPAD_RIGHT), FALSE);
        				EnableWindow(GetDlgItem(hDlg, IDC_DPAD_DOWN), FALSE);
        				EnableWindow(GetDlgItem(hDlg, IDC_ANALOG), FALSE);

        				EnableWindow(GetDlgItem(hDlg, IDC_TOGGLE_ACTIVE), FALSE);
						EnableWindow(GetDlgItem(hDlg, IDC_TOGGLE), FALSE);
					}
					return TRUE;

				case IDC_BUTTON_A:
					if (HIWORD(wParam) == CBN_SETFOCUS)
					{
						ScanDeviceInput(&cfg.Cont[cont_to_cfg].Type[BUTTON_A], &cfg.Cont[cont_to_cfg].Val[BUTTON_A], TYPE_INPUT_DIGITAL);
						CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_A], cfg.Cont[cont_to_cfg].Val[BUTTON_A]);
						SetDlgItemText(hDlg, IDC_BUTTON_A, buf);
					}
					break;

				case IDC_BUTTON_B:
					if (HIWORD(wParam) == CBN_SETFOCUS)
					{
						ScanDeviceInput(&cfg.Cont[cont_to_cfg].Type[BUTTON_B], &cfg.Cont[cont_to_cfg].Val[BUTTON_B], TYPE_INPUT_DIGITAL);
						CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_B], cfg.Cont[cont_to_cfg].Val[BUTTON_B]);
						SetDlgItemText(hDlg, IDC_BUTTON_B, buf);
					}
					break;

				case IDC_BUTTON_Z:
					if (HIWORD(wParam) == CBN_SETFOCUS)
					{
						ScanDeviceInput(&cfg.Cont[cont_to_cfg].Type[BUTTON_Z], &cfg.Cont[cont_to_cfg].Val[BUTTON_Z], TYPE_INPUT_DIGITAL);
						CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_Z], cfg.Cont[cont_to_cfg].Val[BUTTON_Z]);
						SetDlgItemText(hDlg, IDC_BUTTON_Z, buf);
					}
					break;

				case IDC_BUTTON_L:
					if (HIWORD(wParam) == CBN_SETFOCUS)
					{
						ScanDeviceInput(&cfg.Cont[cont_to_cfg].Type[BUTTON_L], &cfg.Cont[cont_to_cfg].Val[BUTTON_L], TYPE_INPUT_DIGITAL);
						CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_L], cfg.Cont[cont_to_cfg].Val[BUTTON_L]);
						SetDlgItemText(hDlg, IDC_BUTTON_L, buf);
					}
					break;

				case IDC_BUTTON_R:
					if (HIWORD(wParam) == CBN_SETFOCUS)
					{
						ScanDeviceInput(&cfg.Cont[cont_to_cfg].Type[BUTTON_R], &cfg.Cont[cont_to_cfg].Val[BUTTON_R], TYPE_INPUT_DIGITAL);
						CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_R], cfg.Cont[cont_to_cfg].Val[BUTTON_R]);
						SetDlgItemText(hDlg, IDC_BUTTON_R, buf);
					}
					break;

				case IDC_BUTTON_START:
					if (HIWORD(wParam) == CBN_SETFOCUS)
					{
						ScanDeviceInput(&cfg.Cont[cont_to_cfg].Type[BUTTON_START], &cfg.Cont[cont_to_cfg].Val[BUTTON_START], TYPE_INPUT_DIGITAL);
						CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_START], cfg.Cont[cont_to_cfg].Val[BUTTON_START]);
						SetDlgItemText(hDlg, IDC_BUTTON_START, buf);
					}
					break;

				case IDC_BUTTON_C_UP:
					if (HIWORD(wParam) == CBN_SETFOCUS)
					{
						ScanDeviceInput(&cfg.Cont[cont_to_cfg].Type[BUTTON_C_UP], &cfg.Cont[cont_to_cfg].Val[BUTTON_C_UP], TYPE_INPUT_DIGITAL);
						CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_C_UP], cfg.Cont[cont_to_cfg].Val[BUTTON_C_UP]);
						SetDlgItemText(hDlg, IDC_BUTTON_C_UP, buf);
					}
					break;

				case IDC_BUTTON_C_LEFT:
					if (HIWORD(wParam) == CBN_SETFOCUS)
					{
						ScanDeviceInput(&cfg.Cont[cont_to_cfg].Type[BUTTON_C_LEFT], &cfg.Cont[cont_to_cfg].Val[BUTTON_C_LEFT], TYPE_INPUT_DIGITAL);
						CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_C_LEFT], cfg.Cont[cont_to_cfg].Val[BUTTON_C_LEFT]);
						SetDlgItemText(hDlg, IDC_BUTTON_C_LEFT, buf);
					}
					break;

				case IDC_BUTTON_C_RIGHT:
					if (HIWORD(wParam) == CBN_SETFOCUS)
					{
						ScanDeviceInput(&cfg.Cont[cont_to_cfg].Type[BUTTON_C_RIGHT], &cfg.Cont[cont_to_cfg].Val[BUTTON_C_RIGHT], TYPE_INPUT_DIGITAL);
						CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_C_RIGHT], cfg.Cont[cont_to_cfg].Val[BUTTON_C_RIGHT]);
						SetDlgItemText(hDlg, IDC_BUTTON_C_RIGHT, buf);
					}
					break;

				case IDC_BUTTON_C_DOWN:
					if (HIWORD(wParam) == CBN_SETFOCUS)
					{
						ScanDeviceInput(&cfg.Cont[cont_to_cfg].Type[BUTTON_C_DOWN], &cfg.Cont[cont_to_cfg].Val[BUTTON_C_DOWN], TYPE_INPUT_DIGITAL);
						CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_C_DOWN], cfg.Cont[cont_to_cfg].Val[BUTTON_C_DOWN]);
						SetDlgItemText(hDlg, IDC_BUTTON_C_DOWN, buf);
					}
					break;

				case IDC_DPAD_UP:
					if (HIWORD(wParam) == CBN_SETFOCUS)
					{
						ScanDeviceInput(&cfg.Cont[cont_to_cfg].Type[BUTTON_DPAD_UP], &cfg.Cont[cont_to_cfg].Val[BUTTON_DPAD_UP], TYPE_INPUT_DIGITAL);
						CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_DPAD_UP], cfg.Cont[cont_to_cfg].Val[BUTTON_DPAD_UP]);
						SetDlgItemText(hDlg, IDC_DPAD_UP, buf);
					}
					break;

				case IDC_DPAD_LEFT:
					if (HIWORD(wParam) == CBN_SETFOCUS)
					{
						ScanDeviceInput(&cfg.Cont[cont_to_cfg].Type[BUTTON_DPAD_LEFT], &cfg.Cont[cont_to_cfg].Val[BUTTON_DPAD_LEFT], TYPE_INPUT_DIGITAL);
						CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_DPAD_LEFT], cfg.Cont[cont_to_cfg].Val[BUTTON_DPAD_LEFT]);
						SetDlgItemText(hDlg, IDC_DPAD_LEFT, buf);
					}
					break;

				case IDC_DPAD_RIGHT:
					if (HIWORD(wParam) == CBN_SETFOCUS)
					{
						ScanDeviceInput(&cfg.Cont[cont_to_cfg].Type[BUTTON_DPAD_RIGHT], &cfg.Cont[cont_to_cfg].Val[BUTTON_DPAD_RIGHT], TYPE_INPUT_DIGITAL);
						CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_DPAD_RIGHT], cfg.Cont[cont_to_cfg].Val[BUTTON_DPAD_RIGHT]);
						SetDlgItemText(hDlg, IDC_DPAD_RIGHT, buf);
					}
					break;

				case IDC_DPAD_DOWN:
					if (HIWORD(wParam) == CBN_SETFOCUS)
					{
						ScanDeviceInput(&cfg.Cont[cont_to_cfg].Type[BUTTON_DPAD_DOWN], &cfg.Cont[cont_to_cfg].Val[BUTTON_DPAD_DOWN], TYPE_INPUT_DIGITAL);
						CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type[BUTTON_DPAD_DOWN], cfg.Cont[cont_to_cfg].Val[BUTTON_DPAD_DOWN]);
						SetDlgItemText(hDlg, IDC_DPAD_DOWN, buf);
					}
					break;

				case IDC_ANALOG:
					if (HIWORD(wParam) == CBN_SETFOCUS)
					{
						ScanDeviceInput(&cfg.Cont[cont_to_cfg].Type_Analog, &foo, TYPE_INPUT_ANALOG);
						CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type_Analog, 0);
						SetDlgItemText(hDlg, IDC_ANALOG, buf);
					}
					break;

				case IDC_TOGGLE:
					if (HIWORD(wParam) == CBN_SETFOCUS)
					{
						ScanDeviceInput(&cfg.Cont[cont_to_cfg].Type_Toggle, &cfg.Cont[cont_to_cfg].Val_Toggle, TYPE_INPUT_DIGITAL);
						CreateDeviceString(buf, cfg.Cont[cont_to_cfg].Type_Toggle, cfg.Cont[cont_to_cfg].Val_Toggle);
						SetDlgItemText(hDlg, IDC_TOGGLE, buf);
					}
					break;
			}

			if (HIWORD(wParam) == CBN_SETFOCUS)
			{
				in_device_config = FALSE;
				SetFocus(hDlg);
				return TRUE;
			}
			break;
    }

    return FALSE;
}

void ShowROMInfo(void)
{
    FARPROC romproc;

    romproc = MakeProcInstance((FARPROC)ROMProc, MainhInst);
    DialogBox(MainhInst, MAKEINTRESOURCE(IDD_ROMINFO), MainhWnd, romproc);
    FreeProcInstance(romproc);
}

void ShowAboutBox(void)
{
    FARPROC aboutproc;

    aboutproc = MakeProcInstance((FARPROC)AboutBoxProc, MainhInst);
    DialogBox(MainhInst, MAKEINTRESOURCE(IDD_ABOUT), MainhWnd, aboutproc);
    FreeProcInstance(aboutproc);
}

void DoVideoOptions(void)
{
    FARPROC videooptsproc;

    videooptsproc = MakeProcInstance((FARPROC)VideoOptsProc, MainhInst);
    DialogBox(MainhInst, MAKEINTRESOURCE(IDD_VIDEO_CONFIG), MainhWnd, videooptsproc);
    FreeProcInstance(videooptsproc);
}

void DoAudioOptions(void)
{
    FARPROC audiooptsproc;

    // Reset DeltaPlayer menus back to all grayed...
    EnableMenuItem(MainhMenu, ID_DP_PLAY,  MF_GRAYED);
    EnableMenuItem(MainhMenu, ID_DP_PAUSE, MF_GRAYED);
    EnableMenuItem(MainhMenu, ID_DP_STOP,  MF_GRAYED);

    Audio_Deinit();
    audiooptsproc = MakeProcInstance((FARPROC)AudioOptsProc, MainhInst);
    DialogBox(MainhInst, MAKEINTRESOURCE(IDD_AUDIO_CONFIG), MainhWnd, audiooptsproc);
    FreeProcInstance(audiooptsproc);

    if ((Audio_Init() == FALSE) && (cfg.DisableAudio == FALSE))
    {
        MessageBox(NULL, "Audio Initialization Failed!  Audio will be disabled.", NULL, MB_OK);
        Audio_Disable = TRUE;
    }
}

void DoRegisterFileTypes(void)
{
    FARPROC filetypesproc;

    filetypesproc = MakeProcInstance((FARPROC)RegisterFileTypesProc, MainhInst);
    DialogBox(MainhInst, MAKEINTRESOURCE(IDD_REGISTER_FILETYPES), MainhWnd, filetypesproc);
    FreeProcInstance(filetypesproc);
}

void DoConfigureControllers(void)
{
    FARPROC controllersproc;
	static bool in_config = FALSE;

	if (in_config) return;
	in_config = TRUE;

    controllersproc = MakeProcInstance((FARPROC)ConfigureControllerProc, MainhInst);
    DialogBox(MainhInst, MAKEINTRESOURCE(IDD_CONTROL_CONFIG), MainhWnd, controllersproc);
    FreeProcInstance(controllersproc);

	in_config = FALSE;
}

void RestoreDebuggerPositions(void)
{
#ifdef _DEBUG
	SetWindowPos(rspdebughWnd,    NULL, cfg.RSPDebugXpos, cfg.RSPDebugYpos, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
	SetWindowPos(rspregisterhWnd, NULL, cfg.RSPRegXpos,   cfg.RSPRegYpos,   0, 0, SWP_NOSIZE | SWP_NOZORDER);
	SetWindowPos(debughWnd,     NULL, cfg.DebugXpos, cfg.DebugYpos, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
	SetWindowPos(registerhWnd,  NULL, cfg.RegXpos,   cfg.RegYpos,   0, 0, SWP_NOSIZE | SWP_NOZORDER);
	SetWindowPos(memoryhWnd,    NULL, cfg.MemXpos,   cfg.MemYpos,   0, 0, SWP_NOSIZE | SWP_NOZORDER);
	SetWindowPos(callstackhWnd, NULL, cfg.CallXpos,  cfg.CallYpos,  0, 0, SWP_NOSIZE | SWP_NOZORDER);
#endif // _DEBUG
}

void SaveDebuggerPositions(void)
{
	WINDOWPLACEMENT wp;

#ifdef _DEBUG
	wp.length = sizeof(WINDOWPLACEMENT);
	GetWindowPlacement(debughWnd, &wp);     cfg.DebugXpos = wp.rcNormalPosition.left; cfg.DebugYpos = wp.rcNormalPosition.top;
	GetWindowPlacement(registerhWnd, &wp);  cfg.RegXpos   = wp.rcNormalPosition.left; cfg.RegYpos   = wp.rcNormalPosition.top;
	GetWindowPlacement(memoryhWnd, &wp);    cfg.MemXpos   = wp.rcNormalPosition.left; cfg.MemYpos   = wp.rcNormalPosition.top;
	GetWindowPlacement(callstackhWnd, &wp); cfg.CallXpos  = wp.rcNormalPosition.left; cfg.CallYpos  = wp.rcNormalPosition.top;
	GetWindowPlacement(rspdebughWnd,    &wp); cfg.RSPDebugXpos = wp.rcNormalPosition.left; cfg.RSPDebugYpos = wp.rcNormalPosition.top;
	GetWindowPlacement(rspregisterhWnd, &wp); cfg.RSPRegXpos   = wp.rcNormalPosition.left; cfg.RSPRegYpos   = wp.rcNormalPosition.top;

    Save_Config();
#endif // _DEBUG
}

HWND CreateMainWindow(uint xsize, uint ysize, HINSTANCE hInstance, int nCmdShow, char *name)
{
    HWND hwnd;
    WNDCLASS wc;

    // Create the Window class and the Window ...
    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc   = WindowProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(BLACK_BRUSH);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = name;
    if (!RegisterClass(&wc)) return FALSE;

    hwnd = CreateWindowEx(0, name, name, WS_OVERLAPPEDWINDOW, 0, 0, xsize, ysize, NULL, NULL, hInstance, NULL);
    if (!hwnd) return 0;

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    SetFocus(hwnd);

    return hwnd;
}

LRESULT APIENTRY WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
/*
// Need better interface... mouse should reappear when it is on top of the menu bar...

        case WM_SETCURSOR:
            if (running)
            {
                SetCursor(NULL);
                ShowCursor(FALSE);
            }
            break;
*/

		case WM_ACTIVATEAPP:
			if (wParam)
			{
				// Restore surfaces upon activation ONLY if the CFB has been initialized once before.
				if (CFB_BitDepth != 8) RestoreSurfaces();
                has_focus = TRUE;
			}
			else
			{
                has_focus = FALSE;
			}
			break;

		case WM_CREATE:
            break;

        case WM_SIZE:
            win_Xsize = LOWORD(lParam);
            win_Ysize = HIWORD(lParam);
            break;

        case WM_PAINT:
            if (loaded_images)
            {
                Clear();
                Lock();
                BlitBuf(80, 0, &buf_title);
                Unlock();
                Flip();
            }
            else
            {
				// If we got a paint message and we're running, paint.  If we're not running (stopped in the debugger), paint
				// ONLY if the CFB has been initialized once before...
                if (inDebugger && CFB_BitDepth != 8) Flip();
            }
            break;

        case WM_DESTROY:
            Load_WantToQuit = TRUE;
            PostQuitMessage(0);
            break;

        case WM_QUIT:
			if (inDebugger) SaveDebuggerPositions();
            Load_WantToQuit = TRUE;
            break;

        case WM_CLOSE:
			if (inDebugger) SaveDebuggerPositions();
            Load_WantToQuit = TRUE;
            break;

        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
                case ID_FILE_OPENROM:
                    if (LoadROM(NULL) == TRUE)
						loaded_images = FALSE;
                    return 0;

                case ID_FILE_LOADSTATE:
                    return 0;

                case ID_FILE_SAVESTATE:
                    return 0;
                    
                case ID_FILE_INFO:
                    ShowROMInfo();
                    return 0;

                case ID_FILE_RESET:
                    InitEmulation();
					InitLog();

                    if (inDebugger) 
                        RefreshDebugAll();
                    else
                    {
                        running = TRUE;
                        SetEvent(hRunEvent);
						AcquireDevices(MainhWnd);
                    }

                    loaded_images = FALSE;
                    return 0;

                case ID_FILE_PAUSE:
                    if (!inDebugger)
                    {
                        if (running)
                        {
                            running = FALSE;
                            CheckMenuItem(MainhMenu, ID_FILE_PAUSE, MF_CHECKED);
                        }
                        else
                        {
                            running = TRUE;
                            SetEvent(hRunEvent);
							AcquireDevices(MainhWnd);
                            CheckMenuItem(MainhMenu, ID_FILE_PAUSE, MF_UNCHECKED);
                        }
                    }
                    return 0;
                    
                case ID_FILE_EXIT:
                    PostQuitMessage(0);
                    return 0;

                case ID_OPTIONS_VIDEO:
                    DoVideoOptions();
                    break;

                case ID_OPTIONS_AUDIO:
                    DoAudioOptions();
                    break;

				case ID_OPTIONS_CONT1:
					cont_to_cfg = 0;
					DoConfigureControllers();
					break;

				case ID_OPTIONS_CONT2:
					cont_to_cfg = 1;
					DoConfigureControllers();
					break;

				case ID_OPTIONS_CONT3:
					cont_to_cfg = 2;
					DoConfigureControllers();
					break;

				case ID_OPTIONS_CONT4:
					cont_to_cfg = 3;
					DoConfigureControllers();
					break;

                case ID_OPTIONS_REG_FILETYPES:
                    DoRegisterFileTypes();
                    break;

                case ID_DP_CONTROL:
                    break;

                case ID_DP_OPEN:
                    Audio_LoadFile();
                    break;

                case ID_DP_PLAY:
                    Audio_PlayFile();
                    break;

                case ID_DP_PAUSE:
                    if (audiofile_paused)
                    {
                        audiofile_paused = FALSE;
                        Audio_UnpauseFile();
                        CheckMenuItem(MainhMenu, ID_DP_PAUSE, MF_UNCHECKED);
                    }
                    else
                    {
                        audiofile_paused = TRUE;
                        Audio_PauseFile();
                        CheckMenuItem(MainhMenu, ID_DP_PAUSE, MF_CHECKED);
                    }
                    break;

                case ID_DP_STOP:
                    Audio_StopFile();
                    break;

                case ID_DP_MIX_N64:
                    Audio_SetMix(0);
                    break;

                case ID_DP_MIX_DP:
                    Audio_SetMix(1);
                    break;

                case ID_DP_MIX_BOTH:
                    Audio_SetMix(2);
                    break;

                case ID_DEBUG_ENTERDEBUGGER:
#ifdef _DEBUG
                    if (!inDebugger)
                    {
						UnAcquireDevices();

                        running    = FALSE;
                        inDebugger = TRUE;
                        EnableMenuItem(MainhMenu, ID_DEBUG_ENTERDEBUGGER, MF_GRAYED);
                        EnableMenuItem(MainhMenu, ID_DEBUG_CLOSEDEBUGGER, MF_ENABLED);

                        debugproc = MakeProcInstance((FARPROC)DebuggerProc, MainhInst);
                        debughWnd = CreateDialog(MainhInst, MAKEINTRESOURCE(IDD_DEBUG), NULL, debugproc);

                        rspdebugproc = MakeProcInstance((FARPROC)RSPDebuggerProc, MainhInst);
                        rspdebughWnd = CreateDialog(MainhInst, MAKEINTRESOURCE(IDD_DEBUG), NULL, rspdebugproc);

                        rspregisterproc = MakeProcInstance((FARPROC)RSPRegisterProc, MainhInst);
                        rspregisterhWnd = CreateDialog(MainhInst, MAKEINTRESOURCE(IDD_RSP_REGISTERS), NULL, rspregisterproc);

                        registerproc = MakeProcInstance((FARPROC)RegisterProc, MainhInst);
                        registerhWnd = CreateDialog(MainhInst, MAKEINTRESOURCE(IDD_REGISTERS), NULL, registerproc);

                        memoryproc = MakeProcInstance((FARPROC)MemoryProc, MainhInst);
                        memoryhWnd = CreateDialog(MainhInst, MAKEINTRESOURCE(IDD_MEMORY), NULL, memoryproc);

						callstackproc = MakeProcInstance((FARPROC)CallStackProc, MainhInst);
                        callstackhWnd = CreateDialog(MainhInst, MAKEINTRESOURCE(IDD_CALLSTACK), NULL, callstackproc);

						RestoreDebuggerPositions();
                        RefreshDebugAll();
                    }
#endif // _DEBUG
                    return 0;

                case ID_DEBUG_CLOSEDEBUGGER:
#ifdef _DEBUG
                    if (inDebugger)
                    {
						SaveDebuggerPositions();

                        if (loaded)
                        {
                            running = TRUE;
                            SetEvent(hRunEvent);
							AcquireDevices(MainhWnd);
                        }

                        inDebugger = FALSE;
                        EnableMenuItem(MainhMenu, ID_DEBUG_ENTERDEBUGGER, MF_ENABLED);
                        EnableMenuItem(MainhMenu, ID_DEBUG_CLOSEDEBUGGER, MF_GRAYED);

						if (rspdebughWnd)    DestroyWindow(rspdebughWnd);
						if (rspregisterhWnd) DestroyWindow(rspregisterhWnd);
                        if (debughWnd)       DestroyWindow(debughWnd);
                        if (registerhWnd)    DestroyWindow(registerhWnd);
                        if (memoryhWnd)      DestroyWindow(memoryhWnd);
						if (callstackhWnd)   DestroyWindow(callstackhWnd);
                        
						FreeProcInstance(rspdebugproc);
						FreeProcInstance(rspregisterproc);
                        FreeProcInstance(debugproc);
                        FreeProcInstance(registerproc);
                        FreeProcInstance(memoryproc);
						FreeProcInstance(callstackproc);
                    }
#endif // _DEBUG
                    return 0;

				case ID_DEBUG_REFRESH:
#ifdef _DEBUG
					if (inDebugger) RefreshDebugAll();
#endif // _DEBUG
					return 0;

                case ID_DEBUG_STOP_ON_UNH:
                    if (stop_on_unhandled)
                    {
                        stop_on_unhandled = FALSE;
                        CheckMenuItem(MainhMenu, ID_DEBUG_STOP_ON_UNH, MF_UNCHECKED);
                    }
                    else
                    {
                        stop_on_unhandled = TRUE;
                        CheckMenuItem(MainhMenu, ID_DEBUG_STOP_ON_UNH, MF_CHECKED);
                    }
                    return 0;

                case ID_DEBUG_LOG_MMIO_REG:
                    if (log_mmio_reg)
                    {
                        log_mmio_reg = FALSE;
                        CheckMenuItem(MainhMenu, ID_DEBUG_LOG_MMIO_REG, MF_UNCHECKED);
                    }
                    else
                    {
                        log_mmio_reg = TRUE;
                        CheckMenuItem(MainhMenu, ID_DEBUG_LOG_MMIO_REG, MF_CHECKED);
                    }
                    return 0;

                case ID_HELP_ABOUT:
                    ShowAboutBox();
                    return 0;
            }
            break;
    }

    return DefWindowProc(hWnd, message, wParam, lParam);
}

// Root registry keys:
// -------------------
// HKEY_CLASSES_ROOT\.v64
// HKEY_CLASSES_ROOT\.u64
// HKEY_CLASSES_ROOT\.n64
// HKEY_CLASSES_ROOT\.bin
// HKEY_CLASSES_ROOT\DoctorV64Image
// HKEY_CLASSES_ROOT\CompiledN64Binary
//
// HKEY_USERS\.Default\Software\Project UnReality
// HKEY_CURRENT_USER\Software\Project UnReality

void Register_FileType(char *ext, char *ID, char *fullID)
{
    HKEY curr, icon, shell, open, command;
    DWORD foo;
    char buf[256];

    // Register extension.  [HKEY_CLASSES_ROOT\<ID>]
    RegCreateKeyEx(HKEY_CLASSES_ROOT, ext, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &curr, &foo);
    RegSetValueEx(curr, NULL, 0, REG_SZ, (const uchar *)ID, strlen(ID) + 1);
    RegCloseKey(curr);

    // Register default icon and shell open command.  [HKEY_CLASSES_ROOT\<fullID>]
    RegCreateKeyEx(HKEY_CLASSES_ROOT, ID, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &curr, &foo);
    RegSetValueEx(curr, NULL, 0, REG_SZ, (const uchar *)fullID, strlen(fullID) + 1);

    sprintf(buf, "%s,0", argv0);
    RegCreateKeyEx(curr, "DefaultIcon", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &icon, &foo);

	// [HKEY_CLASSES_ROOT\<fullID>\DefaultIcon\<buf>]
    RegSetValueEx(icon, NULL, 0, REG_SZ, (const uchar *)buf, strlen(buf) + 1);
    RegCloseKey(icon);

    sprintf(buf, "%s %%1", argv0);
    RegCreateKeyEx(curr, "shell", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &shell, &foo);
    RegCreateKeyEx(shell, "open", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &open, &foo);
    RegCreateKeyEx(open, "command", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &command, &foo);

	// [HKEY_CLASSES_ROOT\<fullID>\shell\open\command\<buf>]
    RegSetValueEx(command, NULL, 0, REG_SZ, (const uchar *)buf, strlen(buf) + 1);
    RegCloseKey(command);
    RegCloseKey(open);
    RegCloseKey(shell);
    RegCloseKey(curr);
}

void Register_FileTypes(bool t_v64, bool t_u64, bool t_n64, bool t_bin)
{
    if (t_v64) Register_FileType(".v64", "DoctorV64Image", "Doctor V64 Image");
    if (t_u64) Register_FileType(".u64", "DoctorV64Image", "Doctor V64 Image");
    if (t_n64) Register_FileType(".n64", "DoctorV64Image", "Doctor V64 Image");
    if (t_bin) Register_FileType(".bin", "CompiledN64Binary", "Compiled N64 Binary");
}

void Check_FileType(char *ext, char *cmp_ID, bool *cmp_ty)
{
    HKEY curr;
    DWORD foo, len;
    char buf[256];

    *cmp_ty = FALSE;

    if (RegOpenKeyEx(HKEY_CLASSES_ROOT, ext, 0, KEY_ALL_ACCESS, &curr) != ERROR_SUCCESS)
        return;

    len = 256;
    if (RegQueryValueEx(curr, "", NULL, &foo, (uchar *)buf, &len) != ERROR_SUCCESS)
    {
        RegCloseKey(curr);
        return;
    }

    if (!strcmp(buf, cmp_ID))
        *cmp_ty = TRUE;
}

void Check_FileTypes(bool *t_v64, bool *t_u64, bool *t_n64, bool *t_bin)
{
    Check_FileType(".v64", "DoctorV64Image", t_v64);
    Check_FileType(".u64", "DoctorV64Image", t_u64);
    Check_FileType(".n64", "DoctorV64Image", t_n64);
    Check_FileType(".bin", "CompiledN64Binary", t_bin);
}

void Save_Config(void)
{
    HKEY def, soft, unreal, curr;
    DWORD foo;

    // Create config block for HKEY_USERS.
    RegOpenKeyEx(HKEY_USERS, ".Default", 0, KEY_ALL_ACCESS, &def);
    RegOpenKeyEx(def, "Software", 0, KEY_ALL_ACCESS, &soft);
    RegCreateKeyEx(soft, "Project UnReality", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &unreal, &foo);
    RegCreateKeyEx(unreal, UNREAL_VERSION, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &curr, &foo);
    RegSetValueEx(curr, "Config", 0, REG_BINARY, (const uchar *)&cfg, sizeof(cfg));
    RegCloseKey(curr);
    RegCloseKey(unreal);
    RegCloseKey(soft);
    RegCloseKey(def);

    // Create config block for HKEY_CURRENT_USER.
    RegOpenKeyEx(HKEY_CURRENT_USER, "Software", 0, KEY_ALL_ACCESS, &soft);
    RegCreateKeyEx(soft, "Project UnReality", 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &unreal, &foo);
    RegCreateKeyEx(unreal, UNREAL_VERSION, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &curr, &foo);
    RegSetValueEx(curr, "Config", 0, REG_BINARY, (const uchar *)&cfg, sizeof(cfg));
    RegCloseKey(curr);
    RegCloseKey(unreal);
    RegCloseKey(soft);
}

void Create_Config(void)
{
	int t, x;

    // Just create a default structure then save it.
    cfg.Windowed   = TRUE;
    cfg.Resizeable = FALSE;
    cfg.DDrawDev   = 0;
	cfg.FPSLock    = FALSE;
	cfg.ShowFPS    = FALSE;
	cfg.Upscale320 = FALSE;
	memset(cfg.LastPath, 0, 256);

#ifdef _DEBUG
	cfg.DebugXpos = 0;
	cfg.DebugYpos = 0;
	cfg.RegXpos = 16;
	cfg.RegYpos = 16;
	cfg.MemXpos = 32;
	cfg.MemYpos = 32;
	cfg.CallXpos = 48;
	cfg.CallYpos = 48;
	cfg.RSPDebugXpos = 64;
	cfg.RSPDebugYpos = 64;
	cfg.RSPRegXpos = 80;
	cfg.RSPRegYpos = 80;
#endif // _DEBUG

	// Setup default values for controller 1.
	cfg.Cont[0].Controller_Active = TRUE;

	for (x = 0; x < MAX_BUTTONS; x++)
		cfg.Cont[0].Type[x] = TYPE_KEYBOARD;

	cfg.Cont[0].Val[0] = 0x9D;
	cfg.Cont[0].Val[1] = 0x36;
	cfg.Cont[0].Val[2] = 0x35;
	cfg.Cont[0].Val[3] = 0x1A;
	cfg.Cont[0].Val[4] = 0x1B;
	cfg.Cont[0].Val[5] = 0x1C;
	cfg.Cont[0].Val[6] = 0x17;
	cfg.Cont[0].Val[7] = 0x24;
	cfg.Cont[0].Val[8] = 0x26;
	cfg.Cont[0].Val[9] = 0x25;
	cfg.Cont[0].Val[10] = 0xC8;
	cfg.Cont[0].Val[11] = 0xCB;
	cfg.Cont[0].Val[12] = 0xCD;
	cfg.Cont[0].Val[13] = 0xD0;

	cfg.Cont[0].Type_Analog = TYPE_MOUSE_MOVE;
	cfg.Cont[0].Analog_Input_Digital = FALSE;

	cfg.Cont[0].Toggle_Active = FALSE;
	cfg.Cont[0].Type_Toggle = TYPE_DISABLED;
	cfg.Cont[0].Val_Toggle = 0x00;

	// Zap controllers 2-4.
	for (t = 1; t < 4; t++)
	{
		cfg.Cont[t].Controller_Active = FALSE;

		for (x = 0; x < MAX_BUTTONS; x++)
		{
			cfg.Cont[t].Type[x] = TYPE_DISABLED;
			cfg.Cont[t].Val[x]  = 0x00;
		}

		cfg.Cont[t].Type_Analog = TYPE_DISABLED;
		cfg.Cont[t].Analog_Input_Digital = FALSE;

		cfg.Cont[t].Toggle_Active = FALSE;
		cfg.Cont[t].Type_Toggle = TYPE_DISABLED;
		cfg.Cont[t].Val_Toggle = 0x00;
	}

    // Setup default audio settings.
    Audio_Autodetect();
    cfg.DisableAudio     = FALSE;
    cfg.ForceSoftwareMix = TRUE;
    cfg.ThreadMix        = FALSE;

    cfg.SoundHighQ      = TRUE;
    cfg.SoundInterp     = TRUE;
    cfg.SoundPriority   = 1;
    cfg.SoundEmuCubic   = FALSE;
    cfg.SoundEmuXTCPlay = FALSE;
    cfg.SoundMixN64     = 2;
	memset(cfg.LastSoundPath, 0, 256);

    Save_Config();
}

void Read_Config(void)
{
    HKEY def, soft, unreal, curr;
    DWORD foo, len;

    RegOpenKeyEx(HKEY_USERS, ".Default", 0, KEY_ALL_ACCESS, &def);
    RegOpenKeyEx(def, "Software", 0, KEY_ALL_ACCESS, &soft);
    if (RegOpenKeyEx(soft, "Project UnReality", 0, KEY_ALL_ACCESS, &unreal) != ERROR_SUCCESS)
    {
        // Key not found, create default config.
        Create_Config();
        RegCloseKey(soft);
        RegCloseKey(def);
        return;
    }

    if (RegOpenKeyEx(unreal, UNREAL_VERSION, 0, KEY_ALL_ACCESS, &curr) != ERROR_SUCCESS)
    {
        // Key not found for this version, create default config.
        Create_Config();
        RegCloseKey(unreal);
        RegCloseKey(soft);
        RegCloseKey(def);
        return;
    }

    // Key found, so read configuration data.
    len = sizeof(cfg);
    if (RegQueryValueEx(curr, "Config", NULL, &foo, (uchar *)&cfg, &len) != ERROR_SUCCESS)
        Create_Config();

    RegCloseKey(curr);
    RegCloseKey(unreal);
    RegCloseKey(soft);
    RegCloseKey(def);
}

void Get_Argv0(LPSTR cmdline, uchar *buf)
{
    char *ptr = (char *)cmdline;
    uchar *dptr = buf;

    // Search backwards until we get to a ':' or a '\\'.
    for (;;)
    {
        if (*ptr == ':' || (*ptr == '\\' && *(ptr - 1) == '\\'))
        {
            // Go back one more.
            ptr--;

            // Now copy until we hit a space or a NULL.
            for (;;)
            {
                if (*ptr == ' ' || *ptr == 0)
                {
                    *dptr = 0;
                    return;
                }

                *dptr++ = *ptr++;
            }
        }

        ptr--;
    }
}

int Eval_EmulationException(LPEXCEPTION_POINTERS xcpt)
{
	if (xcpt->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
	{
		// Copy the exception info and execute the handler.
		xp.ExceptionRecord = &xr;
		xp.ContextRecord = &cr;

		memcpy(&xr, xcpt->ExceptionRecord, sizeof(xr));
		memcpy(&cr, xcpt->ContextRecord, sizeof(cr));
		return EXCEPTION_EXECUTE_HANDLER;
	}

	// Not handled...
    return EXCEPTION_CONTINUE_SEARCH;
}

#define BASE_ADDR_UNKNOWN		0xDEADBEEF			// Always wanted to use this... :)

DWORD EmulationThreadFunc(LPDWORD lpdwParam)
{
	ulong base_addr;
	char buf[256];
	int ex_ty, opcode;

	InitMainEmulation();

	while (!want_to_exit)
	{
		__try
		{
			MainEmulation();
		}
		__except (Eval_EmulationException(GetExceptionInformation()))
		{
			// Try to determine if this was an access to the memory map by examining ESI and seeing if it's within bounds of
			// the N64Memory -> N64Memory + 512Mb address.

			if ((xp.ContextRecord->Esi >= (ulong)N64Memory) && (xp.ContextRecord->Esi <= (ulong)((char *)N64Memory + 0x20000000)))
				base_addr = (ulong)xp.ContextRecord->Esi - (ulong)N64Memory;
			else
				base_addr = BASE_ADDR_UNKNOWN;

sprintf(buf, "address=%08X  PC=%08X", base_addr, r4reg.PC);
MessageBox(NULL, buf, "Debug Exception", MB_OK);

			// Check to see if this was a fetch (to read an opcode from memory).
			if ((r4reg.PC & 0x1FFFFFFF) == base_addr)
			{
				ex_ty = EXCEPTION_TLB_LOAD;
			}
			else
			{
				// Check to see if the opcode was a load or store operation.
				opcode = Read8(r4reg.PC) >> 2;
				if (((opcode >= 0x20) && (opcode <= 0x27)) || ((opcode >= 0x30) && (opcode <= 0x37)) || (opcode == 0x1A) || (opcode == 0x1B))
				{
					ex_ty = EXCEPTION_TLB_LOAD;
				}
				else
				{
					// Check if it's a store opcode...
					if (((opcode >= 0x28) && (opcode <= 0x2F)) || ((opcode >= 0x38) && (opcode <= 0x3F)))
					{
						ex_ty = EXCEPTION_TLB_STORE;
					}
					else
					{
						sprintf(buf, "Access to unknown memory! PC=%08X, addr=%08X", r4reg.PC, base_addr);
						MessageBox(NULL, buf, "Fatal Error", MB_OK);
						want_to_exit = TRUE;
					}
				}
			}

			if (!want_to_exit)
			{
				// Slap in the virtual address into BadVPN2...
				c0reg.Context &= 0xFF800000;
				c0reg.Context |= (base_addr >> 12) << 4;

				// Generate the exception and return back to emu-land...
				GenerateException(ex_ty);
			}
		}
	};

    return 0;
}

void StartEmulationThread(void)
{
	DWORD ThreadID;

    // Make sure we don't start up another one!
    if (emu_thread_running) return;

    // Initialize audio.
    Audio_Reset();

    // Initialize input thread.
	if (Input_StartThread() == FALSE)
		MessageBox(MainhWnd, "Unable to initialize input thread.  Hmm... this is very bad.", NULL, MB_OK);

	// Reset controller data.
	ResetControllers();

	// Reset the CFB.
    want_to_exit = FALSE;
	CFB_Reset();
    Clear();
    Flip();
    CFB_BitDepth = 8;

	// Initialize the heap and add initial vertical retrace event to the heap.
	InitEventHeap();
	EventHeapInsert(c0reg.Count + VERTICAL_RETRACE_TIME, VerticalRetraceInterrupt);

    // Run run run!
    running = FALSE;
    hEmuThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)EmulationThreadFunc, 0, CREATE_SUSPENDED, &ThreadID);

#ifdef FAST_PUR
    SetThreadPriority(hEmuThread, THREAD_PRIORITY_TIME_CRITICAL);
#endif

    // Flag the thread as running.
    emu_thread_running = TRUE;
}

void TerminateEmulationThread(void)
{
    // Make sure one's already running before we try to destroy anything.
    if (!emu_thread_running) return;

    // Shut down the emulation thread.
    want_to_exit = TRUE;
    running = FALSE;
    SetEvent(hRunEvent);

    // Wait until thread stops...
    WaitForSingleObject(hEmuThread, INFINITE);

	// Cleanup.
    CloseHandle(hEmuThread);
    hEmuThread = NULL;

    // Set flag...
	emu_thread_running = FALSE;

	// Stop the RSP.
	RSP->RSP_Stop();

    // Kill off the input thread.
	Input_StopThread();

    // Shut off audio.
    Audio_Stop();
}

void InitVariables(void)
{
    int t;

	N64Memory = NULL;
	CART1DOM = NULL;
    RDRAM = NULL;
    RDRAM_TEMP_MEM = NULL;
    SP_DATA_MEM = NULL;
    SP_INST_MEM = NULL;
	SP_REGS = NULL;
	SP_PC = NULL;
	SP_IMEM_BIST = NULL;
	DP_CTRL_REGS = NULL;
	DP_SPAN_REGS = NULL;
	MIPS_REGS = NULL;
	VIDEO_REGS = NULL;
	AUDIO_REGS = NULL;
	PERIF_REGS = NULL;
	RDRAM_INTF_REGS = NULL;
	SERIAL_REGS = NULL;
    PIF_ROM = NULL;
    PIF_RAM = NULL;
	LEO = NULL;

    running = FALSE;
    want_to_exit = FALSE;

	FUNC_MemCopy          = (ulong)MemCopy;
	FUNC_MemReadFail      = (ulong)MemReadFail;
	FUNC_MemWriteFail     = (ulong)MemWriteFail;
	FUNC_UnknownOpcode    = (ulong)UnknownOpcode;
	FUNC_COP_Unusable     = (ulong)COP_Unusable;
	FUNC_COP0_Unknown     = (ulong)COP0_Unknown;
	FUNC_COP1_Unknown     = (ulong)COP1_Unknown;
	FUNC_COP2_Unknown     = (ulong)COP2_Unknown;
	FUNC_UnhandledOpcode  = (ulong)Unhandled_Opcode;

	FUNC_RegenIntr     = (ulong)RegenInterrupts;
	FUNC_GenArithOflow = (ulong)GenerateArithmeticOverflow;
	FUNC_GenTrapExcept = (ulong)GenerateTrapException;

	EventFUNC_Compare = (ulong)EventAdd_Compare;
	EventFUNC_PI      = (ulong)EventAdd_PI;

    for (t = 0; t < 16; t++)
        AudioDevList.name[t] = NULL;

    cur_aud_num = 0;
}

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR cmdline, int nCmdShow)
{
    MSG msg;
    bool done;
    char *ptr, buf[128];
    DWORD dwStyle;

    // Begin basic initialization...
    MainhInst = hInstance;
    Get_Argv0(cmdline, argv0);
    Read_Config();
    InitCommonControls();
    InitVariables();

    // Create main window and menu bar...
	sprintf(buf, "Project UnReality %s", UNREAL_VERSION);
    MainhWnd = CreateMainWindow(640, 480, MainhInst, nCmdShow, buf);
    MainhMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU1));
    SetMenu(MainhWnd, MainhMenu);

#ifndef _DEBUG
	// Disable debugger if this is not a debug build.
	EnableMenuItem(MainhMenu, ID_DEBUG_ENTERDEBUGGER, MF_GRAYED);
#endif

	// Initialize DirectInput...
	if (InitDirectInput() == FALSE)
	{
		MessageBox(NULL, "DirectInput Initialization Failed!  (Install DirectX5?)", NULL, MB_OK);
		return 0;
	}

	// Initialize video/CFB.
    if (CFB_Init() == FALSE)
	{
		MessageBox(NULL, "DirectDraw Initialization Failed!  (Install DirectX5?)", NULL, MB_OK);
		return 0;
	}

    // Initialize audio.
    if ((Audio_Init() == FALSE) && (cfg.DisableAudio == FALSE))
    {
        MessageBox(NULL, "Audio Initialization Failed!  Audio will be disabled.", NULL, MB_OK);
        Audio_Disable = TRUE;
    }

    // Display our pretty logo...
    LoadPal(UNREAL_PAL);
    UnpackPCX(&buf_title, UNREAL_XSIZE, UNREAL_YSIZE, UNREAL_IMAGE);
    loaded_images = TRUE;

    // Invalidate the windows's rectangle to send a WM_PAINT and show the image.
    InvalidateRect(MainhWnd, NULL, FALSE);
    UpdateWindow(MainhWnd);

    // Turn off resizing...
    dwStyle  = GetWindowStyle(MainhWnd);
    dwStyle &= ~WS_THICKFRAME;
    SetWindowLong(MainhWnd, GWL_STYLE, dwStyle);

	// Allocate the huge 512Mb virtual memory chunk.
	N64Memory = VirtualAlloc(NULL, 0x20000000, MEM_RESERVE, PAGE_NOACCESS);
	if (N64Memory == NULL)
	{
		MessageBox(MainhWnd, "Unable to allocate flat memory space, Project UnReality cannot continue!", "Fatal Error", MB_OK);
		return 0;
	}

	// And allocate 16Mb for the Cartridge 1 domain.
	CART1DOM = VirtualAlloc((char *)N64Memory + CART1DOM_BASE, CART1DOM_SIZE, MEM_COMMIT, PAGE_READWRITE);
	if (CART1DOM == NULL)
	{
		MessageBox(MainhWnd, "Unable to allocate ROM space, Project UnReality cannot continue!", "Fatal Error", MB_OK);
		return 0;
	}

	// Create the RSP now that memory is available...
	RSP = new RSP_Processor;

    // If a file on the command line was passed to us, go ahead and load it.
    ptr = strchr(cmdline, ' ');
    if (ptr) *ptr = 0;

    if (cmdline[0] != 0) 
        LoadROM(cmdline);

#ifdef FAST_PUR
    // Raise our priority.
    HANDLE hSelf = OpenProcess(PROCESS_SET_INFORMATION, TRUE, GetCurrentProcessId());
    if (hSelf != NULL) SetPriorityClass(hSelf, REALTIME_PRIORITY_CLASS);
    CloseHandle(hSelf);
#endif

    // Setup an event to have us wait until we should begin execution...
    hRunEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

	// ...and one for debugger synchronization.
	hDebugEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

    // Begin main message loop...
    done = FALSE;

    while (!done)
    {
        if (!GetMessage(&msg, NULL, 0, 0))
        {
            done = TRUE;
        }
        else
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

	// Stop input thread.
	Input_StopThread();

    // Shut down emulation thread...
	TerminateEmulationThread();

	// Kill the RSP.
	delete RSP;

	// Unacquire emulation-related devices.
	UnAcquireDevices();

    // Drop audio.
    Audio_Deinit();

    // Cleanup...
    VirtualFree(CART1DOM, CART1DOM_SIZE, MEM_DECOMMIT);
	VirtualFree(N64Memory, 0, MEM_RELEASE);
	N64Memory = NULL;

    FreeCOD_MAP();
    CloseHandle(hRunEvent);
	CloseHandle(hDebugEvent);
	DeinitDirectInput();
	CFB_Deinit();
    return 0;
}
