//
// (C) 2004 Mike Brent aka Tursi aka HarmlessLion.com
// This software is provided AS-IS. No warranty
// express or implied is provided.
//
// This notice defines the entire license for this code.
// All rights not explicity granted here are reserved by the
// author.
//
// You may redistribute this software provided the original
// archive is UNCHANGED and a link back to my web page,
// http://harmlesslion.com, is provided as the author's site.
// It is acceptable to link directly to a subpage at harmlesslion.com
// provided that page offers a URL for that purpose
//
// Source code, if available, is provided for educational purposes
// only. You are welcome to read it, learn from it, mock
// it, and hack it up - for your own use only.
//
// Please contact me before distributing derived works or
// ports so that we may work out terms. I don't mind people
// using my code but it's been outright stolen before. In all
// cases the code must maintain credit to the original author(s).
//
// -COMMERCIAL USE- Contact me first. I didn't make
// any money off it - why should you? ;) If you just learned
// something from this, then go ahead. If you just pinched
// a routine or two, let me know, I'll probably just ask
// for credit. If you want to derive a commercial tool
// or use large portions, we need to talk. ;)
//
// If this, itself, is a derived work from someone else's code,
// then their original copyrights and licenses are left intact
// and in full force.
//
// http://harmlesslion.com - visit the web page for contact info
//
//*****************************************************
//* Ami 99 - TI Emulator for Win32					  *
//* by M.Brent                                        *
//* Win32 WindowProc                                  *
//*****************************************************

//#define WIN32_LEAN_AND_MEAN
//#define _WIN32_WINNT 0x0400

#include <stdio.h>
#include <windows.h>
#include <malloc.h>
//#include <commctrl.h>
//#include <commdlg.h>

#include "resource.h"
#include "tiemul.h"

//extern HDC tmpDC;
extern int timer9901;										// 9901 interrupt timer

extern int nSystem;
extern int nCartGroup;
extern int nCart;
extern int keyboard;
extern bool fKeyEverPressed;
extern int nSampleIdx[3];
extern bool fSampleShouldLoop[3];
extern void GenerateToneBuffer(int idx);
extern int max_cpf, oldmax;
// needed for configuration
extern char AVIFileName[256];
extern int drawspeed, speedup_float, simulate_int, fJoy, joy1mode, joy2mode;
extern int slowdown_keyboard;
//extern HINSTANCE hInstance;						// global program instance
extern int TVFiltersAvailable;
extern int TVScanLines;

//#include <mmsystem.h>
#include <dsound.h>
extern LPDIRECTSOUNDBUFFER voice[3];						// Voice buffers
extern LPDIRECTSOUNDBUFFER noise[8];						// Noise buffers
extern int nCycleCount;
const char *pCurrentHelpMsg=NULL;
HWND hKBMap=NULL;
HWND hTVDlg=NULL;

static LONG nOldVolume[11]={ -10000, -10000, -10000, -10000, -10000, -10000, -10000, -10000, -10000, -10000, -10000};

/////////////////////////////////////////////////////////////////////////
// Window handler
/////////////////////////////////////////////////////////////////////////
#if 0
LONG FAR PASCAL myproc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	// winuser.h has the VK_key key defines
	// also fill in the key[] array for on/off
    
	PAINTSTRUCT ps;
    HDC hDC;
	char szTemp[1024];
	float height;
	int i1;
	RECT myrect, myrect2;

	if (myWnd == hwnd) {	// Main TI window
		switch(msg) {
		case WM_INITMENUPOPUP:
			if (IsClipboardFormatAvailable(CF_TEXT)) {
				EnableMenuItem(GetMenu(myWnd), ID_EDITPASTE, MF_ENABLED | MF_BYCOMMAND);
				
			} else {
				EnableMenuItem(GetMenu(myWnd), ID_EDITPASTE, MF_GRAYED | MF_BYCOMMAND);
			}
			break;

		case WM_DISPLAYCHANGE:
			if (NULL != tmpDC) {
				DeleteDC(tmpDC);
				tmpDC=CreateCompatibleDC(NULL);
			}
			break;
		
		case WM_PAINT:
			hDC = BeginPaint(hwnd, &ps);
			GetClientRect(myWnd, &myrect);
			if (StretchMode==0) {
				FillRect(hDC, &myrect, (HBRUSH)(COLOR_MENU+1));
			}
			SetEvent(BlitEvent);
			EndPaint(hwnd, &ps);
			break;

		case WM_DESTROY:
			quitflag=1;
			PostQuitMessage(0);
			break;
		
		case WM_KEYDOWN:
			key[wParam]=1;
			fKeyEverPressed=true;
			break;

		case WM_KEYUP:
			key[wParam]=0;
			break;

		case WM_SYSCHAR:
			// Don't remove this check, even if we need no ALT keys - otherwise all FCTN keys on the TI ding ;)
			// Fullscreen toggle - Alt-Enter
			if ((wParam==VK_RETURN)&&((lParam&0x8000)==0)) {
				if (3 == StretchMode) {
					StretchMode=2;
					PostMessage(hwnd, WM_COMMAND, ID_VIDEO_STRETCHMODE_NONE+StretchMode, 1);
				} else {
					if ((2 == StretchMode) && (0 != FullScreenMode)) {
						StretchMode=3;
						PostMessage(hwnd, WM_COMMAND, ID_VIDEO_STRETCHMODE_DXFULL_320X240X8+FullScreenMode-1, 1);
					}
				}
			}
			break;

		case WM_SYSKEYDOWN:				// returns from ALT and ALT+KEY (I use as FCTN)
			// some system keys we want Windows to process, namely F4 (close)
			if ((wParam != VK_F4) & (wParam != VK_RETURN))
			{	
				key[wParam]=1;
			}
			else
			{
				switch (wParam)
				{
				case VK_F4:
					quitflag=1;
					PostQuitMessage(0);
					break;
				
				default:
					return(DefWindowProc(hwnd, msg, wParam, lParam));
				}
			}
			break;

		case WM_SYSKEYUP:
			key[wParam]=0;
			break;

		case WM_COMMAND:
			// Check for dynamic ones first, so we don't need a huge switch
			if ((wParam >= ID_SYSTEM_0) && (wParam < ID_SYSTEM_0+100)) {
				// user requested to change system
				int ret;
				if ((!lParam)&&(fKeyEverPressed)) {
					ret=MessageBox(hwnd, "This will reset the emulator - are you sure?", "Change System Type", MB_YESNO|MB_ICONQUESTION);
				} else {
					ret=IDYES;
				}
				if (IDYES == ret) {
					nSystem=wParam-ID_SYSTEM_0;
					for (int idx=0; idx<100; idx++) {
						if (idx == nSystem) {
							CheckMenuItem(GetMenu(myWnd), ID_SYSTEM_0+idx, MF_CHECKED);
						} else {
							CheckMenuItem(GetMenu(myWnd), ID_SYSTEM_0+idx, MF_UNCHECKED);
						}
					} 
					// Special case for 99/4
					if (nSystem == 0) {
						keyboard=0;	// 99/4 layout
						MessageBox(myWnd, "Note: 99/4 may be slow to start!", "Compatibility Notes", MB_ICONINFORMATION|MB_OK);
					} else {
						keyboard=1;	// 99/4A layout
					}
					readroms();
					
					SendMessage(hwnd, WM_COMMAND, ID_FILE_RESET, 0);
				}
			}
			if ((wParam >= ID_APP_0) && (wParam < ID_APP_0+100)) {
				// user requested to change cartridge (apps)
				int ret;
				if (fKeyEverPressed) {
					ret=MessageBox(hwnd, "This will reset the emulator - are you sure?", "Change Cartridge", MB_YESNO|MB_ICONQUESTION);
				} else {
					ret=IDYES;
				}
				if (IDYES == ret) {
					nCartGroup=0;
					nCart=wParam-ID_APP_0;
					for (int idx=0; idx<100; idx++) {
						if (idx == nCart) {
							CheckMenuItem(GetMenu(myWnd), ID_APP_0+idx, MF_CHECKED);
						} else {
							CheckMenuItem(GetMenu(myWnd), ID_APP_0+idx, MF_UNCHECKED);
						}
					}
					for (idx=0; idx<100; idx++) {
						CheckMenuItem(GetMenu(myWnd), ID_GAME_0+idx, MF_UNCHECKED);
					}
					for (idx=0; idx<100; idx++) {
						CheckMenuItem(GetMenu(myWnd), ID_USER_0+idx, MF_UNCHECKED);
					}
					readroms();

					if (NULL != pCurrentHelpMsg) {
						SetWindowText(myWnd, "Classic99 - See Help->Known Issues for this cart");
					} else {
						SetWindowText(myWnd, "Classic99");
					}

					SendMessage(hwnd, WM_COMMAND, ID_FILE_RESET, 0);
				}
			}
			if ((wParam >= ID_GAME_0) && (wParam < ID_GAME_0+100)) {
				// user requested to change cartridge (games)
				int ret;
				if (fKeyEverPressed) {
					ret=MessageBox(hwnd, "This will reset the emulator - are you sure?", "Change Cartridge", MB_YESNO|MB_ICONQUESTION);
				} else {
					ret=IDYES;
				}
				if (IDYES == ret) {
					nCartGroup=1;
					nCart=wParam-ID_GAME_0;
					for (int idx=0; idx<100; idx++) {
						if (idx == nCart) {
							CheckMenuItem(GetMenu(myWnd), ID_GAME_0+idx, MF_CHECKED);
						} else {
							CheckMenuItem(GetMenu(myWnd), ID_GAME_0+idx, MF_UNCHECKED);
						}
					}
					for (idx=0; idx<100; idx++) {
						CheckMenuItem(GetMenu(myWnd), ID_APP_0+idx, MF_UNCHECKED);
					}
					for (idx=0; idx<100; idx++) {
						CheckMenuItem(GetMenu(myWnd), ID_USER_0+idx, MF_UNCHECKED);
					}
					readroms();
					
					if (NULL != pCurrentHelpMsg) {
						SetWindowText(myWnd, "Classic99 - See Help->Known Issues for this cart");
					} else {
						SetWindowText(myWnd, "Classic99");
					}

					SendMessage(hwnd, WM_COMMAND, ID_FILE_RESET, 0);
				}
			}
			if ((wParam >= ID_USER_0) && (wParam < ID_USER_0+100)) {
				// user requested to change cartridge (user)
				int ret;
				if (fKeyEverPressed) {
					ret=MessageBox(hwnd, "This will reset the emulator - are you sure?", "Change Cartridge", MB_YESNO|MB_ICONQUESTION);
				} else {
					ret=IDYES;
				}
				if (IDYES == ret) {
					nCartGroup=2;
					nCart=wParam-ID_USER_0;
					for (int idx=0; idx<100; idx++) {
						if (idx == nCart) {
							CheckMenuItem(GetMenu(myWnd), ID_USER_0+idx, MF_CHECKED);
						} else {
							CheckMenuItem(GetMenu(myWnd), ID_USER_0+idx, MF_UNCHECKED);
						}
					}
					for (idx=0; idx<100; idx++) {
						CheckMenuItem(GetMenu(myWnd), ID_GAME_0+idx, MF_UNCHECKED);
					}
					for (idx=0; idx<100; idx++) {
						CheckMenuItem(GetMenu(myWnd), ID_APP_0+idx, MF_UNCHECKED);
					}
					readroms();

					if (NULL != pCurrentHelpMsg) {
						SetWindowText(myWnd, "Classic99 - See Help->Known Issues for this cart");
					} else {
						SetWindowText(myWnd, "Classic99");
					}

					SendMessage(hwnd, WM_COMMAND, ID_FILE_RESET, 0);
				}
			}
			// Any others?
			switch (wParam)
			{
			case ID_HELP_ABOUT:
				sprintf(szTemp, "Classic99 %s\n"\
								"By Mike Brent (Tursi)\n"\
								"(C)1994-2006\n\n"\
								"ROM data included under license from Texas Instruments.\n\n"\
								"Thanks to TI, Roland Meier, Jeff Brown, Frank\n"\
								"Palazolo and Ralph Nebet for assistance.\n\n"\
								"Contains additional code by:\n"\
								"MESS Team - Speech, with thanks to\n"\
								"Ralph Nebet for speech ROM help.\n"\
								"John Butler - 9900 Disasm\n"\
								"Derek Liauw Kie Fa - 2xSaI Renderer\n"\
								"2xSaI code from the SNES9x project\n\n"\
								"Shay Green for the TV Filter\n\n"\
								"Keyboard map by Ron Reuter - www.mainbyte.com\n\n"\
								"tursi@harmlesslion.com\n"\
								"http://harmlesslion.com/software/classic99", 
						VERSION);
				MessageBox(myWnd, szTemp, "Classic99 About", MB_OK);
				return 0;
				break;

			case ID_HELP_KBMAP: 
				{
					if (NULL == hKBMap) {
						// create a modeless dialog to show the keyboard map
						hKBMap=CreateDialog(NULL, MAKEINTRESOURCE(IDD_KBMAP), hwnd, KBMapProc);
						ShowWindow(hKBMap, SW_SHOW);
					}
				}
				break;

			case ID_HELP_KNOWNISSUES:
				if (NULL == pCurrentHelpMsg) {
					pCurrentHelpMsg="There are no known issues with the currently selected cartridge.";
				}
				MessageBox(myWnd, pCurrentHelpMsg, "Compatibility Notes", MB_ICONINFORMATION|MB_OK);
				break;

			case ID_FILE_RESET:
				ST=(ST&0xfff0);							// disable interrupts
				memset(CRU, 1, 4096);					// reset 9901
				timer9901=0;
				wrword(0x83c4,0);						// Console bug work around, make sure no user int is active
				WP=romword(0x0000);						// Reset = BLWP @>0000
				wrword(WP+26,0xffff);
				wrword(WP+28,PC);
				wrword(WP+30,ST);
				PC=romword(0x0002);
				grmaccess=0;			// No GROM Access yet
				pcodeaccess=0;
				nCurrentDSR=-1;
				memset(nDSRBank, 0, sizeof(nDSRBank));
				vdpaccess=0;			// No VDP address writes yet 
				interrupt_needed=0;		// No interrupt missed yet
				fKeyEverPressed=false;	// No key pressed yet (to disable the warning on cart change)
				if (max_cpf == 0) {
					max_cpf=oldmax;		// unpause!
				}
				nCycleCount=26;		// not that it's a big deal, but that's how long reset takes ;)
				break;

			case ID_FILE_QUIT:
				quitflag=1;
				PostQuitMessage(0);
				break;
			
			case ID_VIDEO_50HZ:
				if (lParam != 1) {
					hzRate=(hzRate==60)?50:60;
				}

				if (hzRate == 60) {
					CheckMenuItem(GetMenu(myWnd), ID_VIDEO_50HZ, MF_UNCHECKED);
				} else {
					CheckMenuItem(GetMenu(myWnd), ID_VIDEO_50HZ, MF_CHECKED);
				}
				break;

			case ID_VIDEO_STARTRECORDING:
				if (Recording == 0)
				{
					debug_write("Starting AVI recording");
					if (0==InitAvi())
					{
						SetWindowText(myWnd, "Classic99 - Recording AVI");
						Recording=1;
						RecordFrame=0;
					}
				}
				break;
			
			case ID_VIDEO_STOPRECORDING:
				if (Recording)
				{
					debug_write("Stoping AVI recording");
					SetWindowText(myWnd, "Classic99");
					CloseAVI();
					Recording=0;
				}
				break;

			case ID_VIDEO_MAINTAINASPECT:
				if (1 != lParam) {
					MaintainAspect=MaintainAspect?0:1;
				}
				if (MaintainAspect)
				{
					CheckMenuItem(GetMenu(myWnd), ID_VIDEO_MAINTAINASPECT, MF_CHECKED);
				}
				else
				{
					CheckMenuItem(GetMenu(myWnd), ID_VIDEO_MAINTAINASPECT, MF_UNCHECKED);
				}
				break;

			case ID_OPTIONS_PAUSEINACTIVE:
				if (1 != lParam) {
					PauseInactive=PauseInactive?0:1;
				}
				if (PauseInactive) {
					CheckMenuItem(GetMenu(myWnd), ID_OPTIONS_PAUSEINACTIVE, MF_CHECKED);
				} else {
					CheckMenuItem(GetMenu(myWnd), ID_OPTIONS_PAUSEINACTIVE, MF_UNCHECKED);
				}
				break;

			case ID_OPTIONS_SPEECHENABLED:
				if (1 != lParam) {
					SpeechEnabled=SpeechEnabled?0:1;
				}
				if (SpeechEnabled) {
					CheckMenuItem(GetMenu(myWnd), ID_OPTIONS_SPEECHENABLED, MF_CHECKED);
				} else {
					CheckMenuItem(GetMenu(myWnd), ID_OPTIONS_SPEECHENABLED, MF_UNCHECKED);
				}
				break;


			case ID_OPTIONS_CPUTHROTTLING:
				if (1 != lParam) {
					CPUThrottle=CPUThrottle?0:1;
				}
				if (CPUThrottle) {
					CheckMenuItem(GetMenu(myWnd), ID_OPTIONS_CPUTHROTTLING, MF_CHECKED);
				} else {
					CheckMenuItem(GetMenu(myWnd), ID_OPTIONS_CPUTHROTTLING, MF_UNCHECKED);
				}
				break;

			case ID_OPTIONS_AUDIO: 
				{
					// Silence during the dialog
					for (int idx=0; idx<3; idx++) {
						voice[idx]->SetVolume(DSBVOLUME_MIN);
					}

					// Create a dialog to reconfigure audio voices
					if (DialogBox(NULL, MAKEINTRESOURCE(IDD_AUDIO), hwnd, AudioBoxProc) == IDOK) {
						for (int idx=0; idx<3; idx++) {
							GenerateToneBuffer(idx);
							if (voice[idx]->Play(0, 0, fSampleShouldLoop[idx]?DSBPLAY_LOOPING:0) != DS_OK) {
								debug_write("Voice DID NOT START");
							}
							voice[idx]->SetVolume(DSBVOLUME_MIN);
						}
					}
				}
				break;

			case ID_OPTIONS_OPTIONS:
				{
					// Silence during the dialog
					for (int idx=0; idx<3; idx++) {
						voice[idx]->SetVolume(DSBVOLUME_MIN);
					}

					// Create a dialog to reconfigure generic options
					DialogBox(NULL, MAKEINTRESOURCE(IDD_OPTIONS), hwnd, OptionsBoxProc);
					// It handles the OK/Cancel operation in OptionsBoxProc
				}
				break;

			case ID_OPTIONS_TV:
				// If filters aren't available, tell the user
				if (!TVFiltersAvailable) {
					MessageBox(hwnd, "TV Filter DLL is not available.", "Classic99", MB_OK);
					break;
				}
				// If we aren't in TV mode, switch to it and come back here
				if (4 != FilterMode) {
					PostMessage(hwnd, WM_COMMAND, ID_VIDEO_FILTERMODE_TVMODE, 0);
					PostMessage(hwnd, WM_COMMAND, ID_OPTIONS_TV, 0);
					break;
				}
				// If the TV dialog doesn't already exist, exist it :)
				if (NULL == hTVDlg) {
					// create a modeless dialog to show the TV controls dialog
					hTVDlg=CreateDialog(NULL, MAKEINTRESOURCE(IDD_TVOPTIONS), hwnd, TVBoxProc);
					ShowWindow(hTVDlg, SW_SHOW);
				}
				break;

			case ID_EDITPASTE:
				if (NULL != PasteString) {
					Beep(550,100);
					break;
				}
				if (OpenClipboard(myWnd)) {
					HANDLE data;

					data=GetClipboardData(CF_TEXT);

					if (data) {
						PasteString=(char*)malloc(strlen((char*)data)+1);
						strcpy(PasteString, (char*)data);
						PasteIndex=PasteString;
					}

					CloseClipboard();
				}
				break;

			case ID_VIDEO_STRETCHMODE_NONE:		// This mode is a fallback, it must not fail and must not loop back
				StretchMode=0;
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_STRETCHMODE_NONE, MF_CHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_STRETCHMODE_DIB, MF_UNCHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_STRETCHMODE_DX, MF_UNCHECKED);
				InvalidateRect(myWnd, NULL, false);
				break;

			case ID_VIDEO_STRETCHMODE_DIB:
				StretchMode=1;
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_STRETCHMODE_NONE, MF_UNCHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_STRETCHMODE_DIB, MF_CHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_STRETCHMODE_DX, MF_UNCHECKED);
				InvalidateRect(myWnd, NULL, false);
				break;

			case ID_VIDEO_STRETCHMODE_DX:
				StretchMode=2;
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_STRETCHMODE_NONE, MF_UNCHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_STRETCHMODE_DIB, MF_UNCHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_STRETCHMODE_DX, MF_CHECKED);
				takedownDirectDraw();
				SetupDirectDraw(0);
				if (2 != StretchMode) {
					myproc(hwnd, WM_COMMAND, ID_VIDEO_STRETCHMODE_NONE, 0);
				} else {
					InvalidateRect(myWnd, NULL, false);
				}
				break;

			case ID_VIDEO_STRETCHMODE_DXFULL_320X240X8:
			case ID_VIDEO_STRETCHMODE_DXFULL_320X240X16:
			case ID_VIDEO_STRETCHMODE_DXFULL_320X240X24:
			case ID_VIDEO_STRETCHMODE_DXFULL_320X200X8:
			case ID_VIDEO_STRETCHMODE_DXFULL_640X480X8:
			case ID_VIDEO_STRETCHMODE_DXFULL_640X480X16:
			case ID_VIDEO_STRETCHMODE_DXFULL_640X480X24:
			case ID_VIDEO_STRETCHMODE_DXFULL_800X600X16:
			case ID_VIDEO_STRETCHMODE_DXFULL_800X600X24:
				StretchMode=3;
				FullScreenMode=wParam-ID_VIDEO_STRETCHMODE_DXFULL_320X240X8+1;
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_STRETCHMODE_NONE, MF_UNCHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_STRETCHMODE_DIB, MF_UNCHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_STRETCHMODE_DX, MF_UNCHECKED);
				{for (DWORD idx=ID_VIDEO_STRETCHMODE_DXFULL_320X240X8; idx<ID_VIDEO_STRETCHMODE_DXFULL_800X600X24; idx++) {
					if (idx==wParam) {
						CheckMenuItem(GetMenu(myWnd), wParam, MF_CHECKED);
					} else {
						CheckMenuItem(GetMenu(myWnd), wParam, MF_UNCHECKED);
					}
				}}
				takedownDirectDraw();
				SetupDirectDraw(FullScreenMode);
				if (3 != StretchMode) {
					myproc(hwnd, WM_COMMAND, ID_VIDEO_STRETCHMODE_NONE, 0);
				} else {
					InvalidateRect(myWnd, NULL, false);
				}
				break;

			case ID_VIDEO_FILTERMODE_NONE:
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_NONE, MF_CHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_2XSAI, MF_UNCHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_SUPER2XSAI, MF_UNCHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_SUPEREAGLE, MF_UNCHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_TVMODE, MF_UNCHECKED);
				if (FilterMode != 0) {
					ResizeBackBuffer(256+16, 192+16);
				}
				FilterMode=0;
				InvalidateRect(myWnd, NULL, false);
				break;

			case ID_VIDEO_FILTERMODE_2XSAI:
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_NONE, MF_UNCHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_2XSAI, MF_CHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_SUPER2XSAI, MF_UNCHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_SUPEREAGLE, MF_UNCHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_TVMODE, MF_UNCHECKED);
				if (FilterMode == 0) {
					ResizeBackBuffer(512+32,384+29);
				}
				FilterMode=1;
				InvalidateRect(myWnd, NULL, false);
				break;

			case ID_VIDEO_FILTERMODE_SUPER2XSAI:
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_NONE, MF_UNCHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_2XSAI, MF_UNCHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_SUPER2XSAI, MF_CHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_SUPEREAGLE, MF_UNCHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_TVMODE, MF_UNCHECKED);
				if (FilterMode == 0) {
					ResizeBackBuffer(512+32,384+29);
				}
				FilterMode=2;
				InvalidateRect(myWnd, NULL, false);
				break;

			case ID_VIDEO_FILTERMODE_SUPEREAGLE:
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_NONE, MF_UNCHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_2XSAI, MF_UNCHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_SUPER2XSAI, MF_UNCHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_SUPEREAGLE, MF_CHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_TVMODE, MF_UNCHECKED);
				if (FilterMode == 0) {
					ResizeBackBuffer(512+32,384+29);
				}
				FilterMode=3;
				InvalidateRect(myWnd, NULL, false);
				break;

			case ID_VIDEO_FILTERMODE_TVMODE:
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_NONE, MF_UNCHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_2XSAI, MF_UNCHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_SUPER2XSAI, MF_UNCHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_SUPEREAGLE, MF_UNCHECKED);
				CheckMenuItem(GetMenu(myWnd), ID_VIDEO_FILTERMODE_TVMODE, MF_CHECKED);
				if (FilterMode == 0) {
					ResizeBackBuffer(512+32,384+29);
				}
				FilterMode=4;
				InvalidateRect(myWnd, NULL, false);
				break;

			default:
				return(DefWindowProc(hwnd, msg, wParam, lParam));

			}

		case WM_SIZE: {
				HDC myDC;
				
				if (MaintainAspect)
				{
					GetWindowRect(myWnd, &myrect);
					GetClientRect(myWnd, &myrect2);
					i1=((myrect.right - myrect.left)-(myrect2.right - myrect2.left))/2;
					myrect.left += i1;
					myrect.right = myrect.left + myrect2.right;
					myrect.bottom -= i1;
					myrect.top = myrect.bottom - myrect2.bottom;
					
					height=(float)((myrect.right - myrect.left) * 0.75);
					height-=(myrect.bottom - myrect.top);

					if (height)
					{
						GetWindowRect(myWnd, &myrect);
						myrect.bottom+=(long)height;
						MoveWindow(myWnd, myrect.left, myrect.top, myrect.right-myrect.left, myrect.bottom-myrect.top, true);
					}
				}

				if (0 == StretchMode) {
					GetClientRect(myWnd, &myrect);
					myDC=GetDC(myWnd);
					FillRect(myDC, &myrect, (HBRUSH)(COLOR_MENU+1));
					ReleaseDC(myWnd, myDC);
				}
				}
				break;

		case WM_MOUSEACTIVATE:
				SetEvent(BlitEvent);
				return(DefWindowProc(hwnd, msg, wParam, lParam));
				break;

		case WM_LBUTTONUP:
		case WM_RBUTTONUP:
				if (3 == StretchMode) {
					StretchMode=2;
					takedownDirectDraw();
					SetupDirectDraw(false);
				}
				break;

		case WM_SETFOCUS: 
			if (PauseInactive) {
				// Re-enable sounds
				for (int idx=0; idx<3; idx++) {
					voice[idx]->SetVolume(nOldVolume[idx]);
				}
				for (idx=0; idx<8; idx++) {
					noise[idx]->SetVolume(nOldVolume[idx+3]);
				}
			}
			break;

		case WM_KILLFOCUS:
			// Disable sounds, cache current levels
			if (PauseInactive) {
				for (int idx=0; idx<3; idx++) {
					voice[idx]->GetVolume(&nOldVolume[idx]);
					voice[idx]->SetVolume(DSBVOLUME_MIN);
				}
				for (idx=0; idx<8; idx++) {
					noise[idx]->GetVolume(&nOldVolume[idx+3]);
					noise[idx]->SetVolume(DSBVOLUME_MIN);
				}
			}
			break;

		default:
			return(DefWindowProc(hwnd, msg, wParam, lParam));
		}
		return 0;
	}

	if ((hwnd==regWnd)||(hwnd==memWnd)||(hwnd==asmWnd)||(hwnd==dbgWnd)) {
		switch (msg) {
		case WM_DESTROY:
			if (hwnd==regWnd) regWnd=NULL;
			if (hwnd==memWnd) memWnd=NULL;
			if (hwnd==asmWnd) asmWnd=NULL;
			if (hwnd==dbgWnd) dbgWnd=NULL;
			break;

		default:
			return(DefWindowProc(hwnd, msg, wParam, lParam));
		}
		return 0;
	}

	return(DefWindowProc(hwnd, msg, wParam, lParam));
}


BOOL CALLBACK AudioBoxProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	// tricky
	// IDC_RADIO2 -11 is line 1
	// IDC_RADIO12-21 is line 2
	// IDC_RADIO22-31 is line 3
	int idx;

    switch (uMsg) 
    { 
        case WM_COMMAND: 
            switch (LOWORD(wParam)) 
            { 
                case IDOK: 
					// Read new values into nSampleIdx[0..2]
					for (idx=IDC_RADIO2; idx<=IDC_RADIO11; idx++) {
						if (BST_CHECKED == SendDlgItemMessage(hwnd, idx, BM_GETCHECK, 0, 0)) {
							nSampleIdx[0]=idx-IDC_RADIO2;
							break;
						}
					}
					for (idx=IDC_RADIO12; idx<=IDC_RADIO21; idx++) {
						if (BST_CHECKED == SendDlgItemMessage(hwnd, idx, BM_GETCHECK, 0, 0)) {
							nSampleIdx[1]=idx-IDC_RADIO12;
							break;
						}
					}
					for (idx=IDC_RADIO22; idx<=IDC_RADIO31; idx++) {
						if (BST_CHECKED == SendDlgItemMessage(hwnd, idx, BM_GETCHECK, 0, 0)) {
							nSampleIdx[2]=idx-IDC_RADIO22;
							break;
						}
					}
                    // Fall through. 
                 case IDCANCEL: 
                    EndDialog(hwnd, wParam); 
                    return TRUE; 

				 case IDC_DEFAULT:
					// set the values to default. (square wave)
					CheckRadioButton(hwnd, IDC_RADIO2, IDC_RADIO11, IDC_RADIO2);
					CheckRadioButton(hwnd, IDC_RADIO12, IDC_RADIO21, IDC_RADIO12);
					CheckRadioButton(hwnd, IDC_RADIO22, IDC_RADIO31, IDC_RADIO22);
					return TRUE;
            } 
			break;

		case WM_INITDIALOG:
			SendDlgItemMessage(hwnd, IDC_RADIO2+nSampleIdx[0], BM_SETCHECK, BST_CHECKED, NULL);
			SendDlgItemMessage(hwnd, IDC_RADIO12+nSampleIdx[1], BM_SETCHECK, BST_CHECKED, NULL);
			SendDlgItemMessage(hwnd, IDC_RADIO22+nSampleIdx[2], BM_SETCHECK, BST_CHECKED, NULL);
			return TRUE;

    } 
    return FALSE; 
} 

BOOL CALLBACK OptionsBoxProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) 
    { 
        case WM_COMMAND: 
            switch (LOWORD(wParam)) 
            { 
                case IDOK: 
					// Read new values from dialog
					SendDlgItemMessage(hwnd, IDC_AVIFILENAME, WM_GETTEXT, 256, (LPARAM)AVIFileName);
					CPUThrottle=IsDlgButtonChecked(hwnd, IDC_CHKTHROTTLE)?1:0;
					max_cpf=DEFAULT_CPF*SendDlgItemMessage(hwnd, IDC_SLDCPU, TBM_GETPOS, 0, 0)/10;
					drawspeed=SendDlgItemMessage(hwnd, IDC_SLDFRAMESKIP, TBM_GETPOS, 0, 0);
					speedup_float=IsDlgButtonChecked(hwnd, IDC_CHKFLOAT)?1:0;
					simulate_int=IsDlgButtonChecked(hwnd, IDC_CHKINT)?1:0;
					slowdown_keyboard=IsDlgButtonChecked(hwnd, IDC_CHKSLOWKEY)?1:0;
					fJoy=IsDlgButtonChecked(hwnd, IDC_CHKJOYST)?1:0;
					if (IsDlgButtonChecked(hwnd, IDC_JOY1KEY))  joy1mode=0;
					if (IsDlgButtonChecked(hwnd, IDC_JOY1JOY1)) joy1mode=1;
					if (IsDlgButtonChecked(hwnd, IDC_JOY1JOY2)) joy1mode=2;
					if (IsDlgButtonChecked(hwnd, IDC_JOY2KEY))  joy2mode=0;
					if (IsDlgButtonChecked(hwnd, IDC_JOY2JOY1)) joy2mode=1;
					if (IsDlgButtonChecked(hwnd, IDC_JOY2JOY2)) joy2mode=2;

					// Special - tell the CPU Throttle menu item the new state
					PostMessage(myWnd, WM_COMMAND, ID_OPTIONS_CPUTHROTTLING, 1);
                    // Fall through. 
                 case IDCANCEL: 
                    EndDialog(hwnd, wParam); 
                    return TRUE; 

				 case IDC_BROWSEAVI:
					 // Browse for new AVI filename
					 {
						OPENFILENAME ofn;
						char buf[256];

						memset(&ofn, 0, sizeof(OPENFILENAME));
						ofn.lStructSize=sizeof(OPENFILENAME);
						ofn.hwndOwner=hwnd;
						ofn.lpstrFilter="AVI Files\0*.avi\0\0";
						SendDlgItemMessage(hwnd, IDC_AVIFILENAME, WM_GETTEXT, 256, (LPARAM)buf);
						ofn.lpstrFile=buf;
						ofn.nMaxFile=256;
						ofn.Flags=OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT|OFN_PATHMUSTEXIST;

						if (GetSaveFileName(&ofn)) {
						 SendDlgItemMessage(hwnd, IDC_AVIFILENAME, WM_SETTEXT, 0, (LPARAM)ofn.lpstrFile);
						}
					 }

					 return TRUE;
            } 
			break;

		case WM_INITDIALOG:
			// load the controls with current values
			SendDlgItemMessage(hwnd, IDC_AVIFILENAME, WM_SETTEXT, 0, (LPARAM)AVIFileName);
			SendDlgItemMessage(hwnd, IDC_CHKTHROTTLE, BM_SETCHECK, CPUThrottle?BST_CHECKED:BST_UNCHECKED, 0);
			SendDlgItemMessage(hwnd, IDC_SLDCPU, TBM_SETRANGE, TRUE, MAKELONG(1,20));
			SendDlgItemMessage(hwnd, IDC_SLDCPU, TBM_SETPOS, TRUE, (max_cpf*10)/DEFAULT_CPF);
			SendDlgItemMessage(hwnd, IDC_SLDFRAMESKIP, TBM_SETRANGE, TRUE, MAKELONG(0,10));
			SendDlgItemMessage(hwnd, IDC_SLDFRAMESKIP, TBM_SETPOS, TRUE, drawspeed);
			SendDlgItemMessage(hwnd, IDC_CHKFLOAT, BM_SETCHECK, speedup_float?BST_CHECKED:BST_UNCHECKED, 0);
			SendDlgItemMessage(hwnd, IDC_CHKINT, BM_SETCHECK, simulate_int?BST_CHECKED:BST_UNCHECKED, 0);
			SendDlgItemMessage(hwnd, IDC_CHKJOYST, BM_SETCHECK, fJoy?BST_CHECKED:BST_UNCHECKED, 0);
			SendDlgItemMessage(hwnd, IDC_CHKSLOWKEY, BM_SETCHECK, slowdown_keyboard?BST_CHECKED:BST_UNCHECKED, 0);
			CheckRadioButton(hwnd, IDC_JOY1KEY, IDC_JOY1JOY2, IDC_JOY1KEY+joy1mode);
			CheckRadioButton(hwnd, IDC_JOY2KEY, IDC_JOY2JOY2, IDC_JOY2KEY+joy2mode);
			return TRUE;

    } 
    return FALSE; 
} 

BOOL CALLBACK KBMapProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	// only tricky part is loading the kb image resource
	static HBITMAP hBmp=NULL;
	HWND hWnd=NULL;

    switch (uMsg) 
    { 
        case WM_COMMAND: 
            switch (LOWORD(wParam)) 
            { 
                case IDOK: 
                    // Fall through. 
                 case IDCANCEL: 
                    EndDialog(hwnd, wParam); 
					if (NULL != hBmp) {
						DeleteObject(hBmp);
						hBmp=NULL;
					}
					hKBMap=NULL;
                    return TRUE; 
            } 
			break;

		case WM_INITDIALOG:
			// load the bitmap into the frame
			hWnd=GetDlgItem(hwnd, IDC_IMAGE);
			if (NULL != hWnd) {
				hBmp=LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_KBMAP));
				if (NULL != hBmp) {
					SendMessage(hWnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBmp);
				}
			}
			return TRUE;
    } 
    return FALSE; 
} 

// External references
BOOL CALLBACK TVBoxProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	static double hue=0, sat=0, cont=0, bright=0, sharp=0;
	double thue, tsat, tcont, tbright, tsharp, tmp;

    switch (uMsg) 
    { 
        case WM_COMMAND: 
            switch (LOWORD(wParam)) 
            { 
                case IDOK: 
					TVScanLines=IsDlgButtonChecked(hwnd, IDC_SCANLINES)?1:0;

					// Read the values one more time
					tmp=SendDlgItemMessage(hwnd, IDC_HUE, TBM_GETPOS, 0, 0);
					thue=(tmp-100)/100.0;
					tmp=SendDlgItemMessage(hwnd, IDC_SAT, TBM_GETPOS, 0, 0);
					tsat=(tmp-100)/100.0;
					tmp=SendDlgItemMessage(hwnd, IDC_CONT, TBM_GETPOS, 0, 0);
					tcont=(tmp-100)/100.0;
					tmp=SendDlgItemMessage(hwnd, IDC_BRIGHT, TBM_GETPOS, 0, 0);
					tbright=(tmp-100)/100.0;
					tmp=SendDlgItemMessage(hwnd, IDC_SHARP, TBM_GETPOS, 0, 0);
					tsharp=(tmp-100)/100.0;

					SetTVValues(thue, tsat, tcont, tbright, tsharp);
					InvalidateRect(myWnd, NULL, false);

                    EndDialog(hwnd, wParam); 
					hTVDlg=NULL;
                    return TRUE; 

				case IDCANCEL: 
					// set back to cached values (note: no cancel button on dialog today!)
					SetTVValues(hue, sat, cont, bright, sharp);
                    EndDialog(hwnd, wParam); 
					hTVDlg=NULL;
                    return TRUE; 

				 case IDC_RESET:
					SetTVValues(0, 0, 0, 0, 0);
					InvalidateRect(myWnd, NULL, false);
					
					SendDlgItemMessage(hwnd, IDC_HUE, TBM_SETPOS, TRUE, 100);
					SendDlgItemMessage(hwnd, IDC_SAT, TBM_SETPOS, TRUE, 100);
					SendDlgItemMessage(hwnd, IDC_CONT, TBM_SETPOS, TRUE, 100);
					SendDlgItemMessage(hwnd, IDC_BRIGHT, TBM_SETPOS, TRUE, 100);
					SendDlgItemMessage(hwnd, IDC_SHARP, TBM_SETPOS, TRUE, 100);
					return TRUE;

				 case IDC_SCANLINES:
					TVScanLines=IsDlgButtonChecked(hwnd, IDC_SCANLINES)?1:0;
					InvalidateRect(myWnd, NULL, false);
					return TRUE;
            } 
			break;

		case WM_VSCROLL:
			if (NULL != lParam) {
				switch (LOWORD(wParam)) {
					case SB_THUMBPOSITION:
					case SB_THUMBTRACK:
						// High word contains absolute position
						tmp=(HIWORD(wParam)-100)/100.0;
						GetTVValues(&thue, &tsat, &tcont, &tbright, &tsharp);
						if (lParam == (long)GetDlgItem(hwnd, IDC_HUE)) {
							thue=tmp;
						}
						if (lParam == (long)GetDlgItem(hwnd, IDC_SAT)) {
							tsat=tmp;
						}
						if (lParam == (long)GetDlgItem(hwnd, IDC_CONT)) {
							tcont=tmp;
						}
						if (lParam == (long)GetDlgItem(hwnd, IDC_BRIGHT)) {
							tbright=tmp;
						}
						if (lParam == (long)GetDlgItem(hwnd, IDC_SHARP)) {
							tsharp=tmp;
						}
						SetTVValues(thue, tsat, tcont, tbright, tsharp);
						InvalidateRect(myWnd, NULL, false);
						break;

					case SB_BOTTOM:
					case SB_TOP:
					case SB_LINEDOWN:
					case SB_LINEUP:
					case SB_PAGEDOWN:
					case SB_PAGEUP:
					case SB_ENDSCROLL:
						// Need to query for the position (mostly keyboard interface)
						tmp=SendMessage((HWND)lParam, TBM_GETPOS, 0, 0);
						tmp=(tmp-100)/100.0;
						GetTVValues(&thue, &tsat, &tcont, &tbright, &tsharp);
						if (lParam == (long)GetDlgItem(hwnd, IDC_HUE)) {
							thue=tmp;
						}
						if (lParam == (long)GetDlgItem(hwnd, IDC_SAT)) {
							tsat=tmp;
						}
						if (lParam == (long)GetDlgItem(hwnd, IDC_CONT)) {
							tcont=tmp;
						}
						if (lParam == (long)GetDlgItem(hwnd, IDC_BRIGHT)) {
							tbright=tmp;
						}
						if (lParam == (long)GetDlgItem(hwnd, IDC_SHARP)) {
							tsharp=tmp;
						}
						SetTVValues(thue, tsat, tcont, tbright, tsharp);
						InvalidateRect(myWnd, NULL, false);
						break;
				}
			}
			return 0;

		case WM_INITDIALOG:
			// cache the current values in case we cancel
			GetTVValues(&hue, &sat, &cont, &bright, &sharp);

			// load the controls with current values
			SendDlgItemMessage(hwnd, IDC_HUE, TBM_SETRANGE, TRUE, MAKELONG(0,200));
			SendDlgItemMessage(hwnd, IDC_HUE, TBM_SETPOS, TRUE, (int)((hue+1.0)*100));

			SendDlgItemMessage(hwnd, IDC_SAT, TBM_SETRANGE, TRUE, MAKELONG(0,200));
			SendDlgItemMessage(hwnd, IDC_SAT, TBM_SETPOS, TRUE, (int)((sat+1.0)*100));

			SendDlgItemMessage(hwnd, IDC_CONT, TBM_SETRANGE, TRUE, MAKELONG(0,200));
			SendDlgItemMessage(hwnd, IDC_CONT, TBM_SETPOS, TRUE, (int)((cont+1.0)*100));

			SendDlgItemMessage(hwnd, IDC_BRIGHT, TBM_SETRANGE, TRUE, MAKELONG(0,200));
			SendDlgItemMessage(hwnd, IDC_BRIGHT, TBM_SETPOS, TRUE, (int)((bright+1.0)*100));

			SendDlgItemMessage(hwnd, IDC_SHARP, TBM_SETRANGE, TRUE, MAKELONG(0,200));
			SendDlgItemMessage(hwnd, IDC_SHARP, TBM_SETPOS, TRUE, (int)((sharp+1.0)*100));

			SendDlgItemMessage(hwnd, IDC_SCANLINES, BM_SETCHECK, TVScanLines?BST_CHECKED:BST_UNCHECKED, 0);

			return TRUE;

    } 
    return FALSE; 
} 
#endif
