shithub: minivmac

ref: ea61a2a8e8601d5b732d2c13257413ad4f864b85
dir: /src/OSGLUWIN.c/

View raw version
/*
	OSGLUWIN.c

	Copyright (C) 2009 Philip Cummins, Weston Pawlowski,
	Bradford L. Barrett, Paul C. Pratt, Fabio Concas

	You can redistribute this file and/or modify it under the terms
	of version 2 of the GNU General Public License as published by
	the Free Software Foundation.  You should have received a copy
	of the license along with this file; see the file COPYING.

	This file is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	license for more details.
*/

/*
	Operating System GLUe for microsoft WINdows

	All operating system dependent code for the
	Microsoft Windows platform should go here.

	This code is descended from Weston Pawlowski's Windows
	port of vMac, by Philip Cummins.
	Adapted by Fabio Concas to run on Pocket PC 2003 devices.

	The main entry point '_tWinMain' is at the end of this file.
*/

#include "CNFGRAPI.h"
#include "SYSDEPNS.h"
#include "ENDIANAC.h"

#include "MYOSGLUE.h"


/* --- adapting to API/ABI version differences --- */

#ifdef UNICODE
#define MyUseUni 1
#else
#define MyUseUni 0
#endif

#ifdef _WIN32_WCE
#define UseWinCE 1
#else
#define UseWinCE 0
#endif


#define My_CSIDL_APPDATA 0x001a

typedef BOOL (WINAPI *SHGetSpecialFolderPathProcPtr) (
	HWND hwndOwner,
	LPTSTR lpszPath,
	int nFolder,
	BOOL fCreate
);
LOCALVAR SHGetSpecialFolderPathProcPtr MySHGetSpecialFolderPath = NULL;
LOCALVAR blnr DidSHGetSpecialFolderPath = falseblnr;

LOCALFUNC blnr HaveMySHGetSpecialFolderPath(void)
{
	if (! DidSHGetSpecialFolderPath) {
		HMODULE hLibModule = LoadLibrary(TEXT("shell32.dll"));
		if (NULL != hLibModule) {
			MySHGetSpecialFolderPath =
				(SHGetSpecialFolderPathProcPtr)
				GetProcAddress(hLibModule,
#if MyUseUni
					TEXT("SHGetSpecialFolderPathW")
#else
					TEXT("SHGetSpecialFolderPathA")
#endif
				);
			/* FreeLibrary(hLibModule); */
		}
		DidSHGetSpecialFolderPath = trueblnr;
	}
	return (MySHGetSpecialFolderPath != NULL);
}


/* --- end of adapting to API/ABI version differences --- */

#include "STRCONST.h"

#if MyUseUni
#define NeedCell2UnicodeMap 1
#else
#define NeedCell2WinAsciiMap 1
#endif

#include "INTLCHAR.h"


LOCALPROC NativeStrFromCStr(LPTSTR r, char *s, blnr AddEllipsis)
{
	ui3b ps[ClStrMaxLength];
	int i;
	int L;

	ClStrFromSubstCStr(&L, ps, s);

	for (i = 0; i < L; ++i) {
		r[i] = (TCHAR)
#if MyUseUni
			Cell2UnicodeMap[ps[i]];
#else
			Cell2WinAsciiMap[ps[i]];
#endif
	}

	if (AddEllipsis) {
#if MyUseUni
		r[L] = 0x2026;
		++L;
#else
		r[L] = '.';
		++L;
		r[L] = '.';
		++L;
		r[L] = '.';
		++L;
#endif
	}

	r[L] = 0;
}

LOCALFUNC LPTSTR FindLastTerm(LPTSTR s, TCHAR delim)
{
	TCHAR c;
	LPTSTR p0 = s;
	LPTSTR p = (LPTSTR)nullpr;

	while ((c = *p0++) != (TCHAR)('\0')) {
		if (c == delim) {
			p = p0;
		}
	}

	return p;
}

LOCALVAR HINSTANCE AppInstance;

LOCALFUNC blnr GetAppDir(LPTSTR pathName)
/* be sure at least _MAX_PATH long! */
{
	if (GetModuleFileName(AppInstance, pathName, _MAX_PATH) == 0) {
		/* MacMsg("error", "GetModuleFileName failed", falseblnr); */
	} else {
		LPTSTR p = FindLastTerm(pathName,
			(TCHAR)('\\'));
		if (p == nullpr) {
			/* MacMsg("error", "strrchr failed", falseblnr); */
		} else {
			*--p = (TCHAR)('\0');
			return trueblnr;
		}
	}
	return falseblnr;
}

/* --- sending debugging info to file --- */

#if dbglog_HAVE

LOCALVAR HANDLE dbglog_File = INVALID_HANDLE_VALUE;

LOCALFUNC blnr dbglog_open0(void)
{
	TCHAR pathName[_MAX_PATH];
	TCHAR Child0[] = TEXT("\\dbglog.txt");
	size_t newlen;

	if (GetAppDir(pathName)) {
		newlen = _tcslen(pathName) + _tcslen(Child0);
		if (newlen + 1 < _MAX_PATH) {
			_tcscat(pathName, Child0);

			dbglog_File = CreateFile(
				pathName, /* pointer to name of the file */
				GENERIC_READ + GENERIC_WRITE,
					/* access (read-write) mode */
				0, /* share mode */
				NULL, /* pointer to security descriptor */
				OPEN_ALWAYS, /* how to create */
				FILE_ATTRIBUTE_NORMAL, /* file attributes */
				NULL /* handle to file with attributes to copy */
			);
			if (INVALID_HANDLE_VALUE == dbglog_File) {
				/* report error (how?) */
			} else if (SetFilePointer(
				dbglog_File, /* handle of file */
				0, /* number of bytes to move file pointer */
				nullpr,
					/* address of high-order word of distance to move */
				FILE_BEGIN /* how to move */
				) != 0)
			{
				/* report error (how?) */
			}
		}
	}

	return (INVALID_HANDLE_VALUE != dbglog_File);
}

LOCALPROC dbglog_write0(char *s, uimr L)
{
	DWORD BytesWritten;

	if (INVALID_HANDLE_VALUE != dbglog_File) {
		if (! WriteFile(dbglog_File, /* handle of file to read */
			(LPVOID)s, /* address of buffer that receives data */
			(DWORD)L, /* number of bytes to read */
			&BytesWritten, /* address of number of bytes read */
			nullpr) /* address of structure for data */
			|| ((ui5b)BytesWritten != L))
		{
			/* report error (how?) */
		}
	}
}

LOCALPROC dbglog_close0(void)
{
	if (INVALID_HANDLE_VALUE != dbglog_File) {
		if (! SetEndOfFile(dbglog_File)) {
			/* report error (how?) */
		}
		(void) CloseHandle(dbglog_File);
		dbglog_File = INVALID_HANDLE_VALUE;
	}
}

#endif

#include "COMOSGLU.h"

#ifndef InstallFileIcons
#define InstallFileIcons 0
#endif

/* Resource Ids */

#define IDI_VMAC      256
#if InstallFileIcons
#define IDI_ROM       257
#define IDI_DISK      258
#endif

/* --- some simple utilities --- */

#define TestBit(i, p) (((unsigned long)(i) & PowOf2(p)) != 0)

GLOBALOSGLUPROC MyMoveBytes(anyp srcPtr, anyp destPtr, si5b byteCount)
{
/*
	must work even if blocks overlap in memory
*/
	(void) memcpy((char *)destPtr, (char *)srcPtr, byteCount);
}

/* --- Parameter buffers --- */

#if IncludePbufs
LOCALVAR HGLOBAL PbufDat[NumPbufs];
#endif

#if IncludePbufs
LOCALFUNC tMacErr PbufNewFromHandle(HGLOBAL h, ui5b count, tPbuf *r)
{
	tPbuf i;
	tMacErr err;

	if (! FirstFreePbuf(&i)) {
		(void) GlobalFree(h);
		err = mnvm_miscErr;
	} else {
		*r = i;
		PbufDat[i] = h;
		PbufNewNotify(i, count);
		err = mnvm_noErr;
	}

	return err;
}
#endif

#if IncludePbufs
GLOBALOSGLUFUNC tMacErr PbufNew(ui5b count, tPbuf *r)
{
	HGLOBAL h;
	tMacErr err = mnvm_miscErr;

	h = GlobalAlloc(GMEM_DDESHARE | GMEM_ZEROINIT, count);
	if (h != NULL) {
		/* need to clear h */
		err = PbufNewFromHandle(h, count, r);
	}

	return err;
}
#endif

#if IncludePbufs
GLOBALOSGLUPROC PbufDispose(tPbuf i)
{
	(void) GlobalFree(PbufDat[i]);
	PbufDisposeNotify(i);
}
#endif

#if IncludePbufs
LOCALPROC UnInitPbufs(void)
{
	tPbuf i;

	for (i = 0; i < NumPbufs; ++i) {
		if (PbufIsAllocated(i)) {
			PbufDispose(i);
		}
	}
}
#endif

#if IncludePbufs
#define PbufHaveLock 1
#endif

#if IncludePbufs
LOCALFUNC ui3p PbufLock(tPbuf i)
{
	HGLOBAL h = PbufDat[i];
	return (ui3p)GlobalLock(h);
}
#endif

#if IncludePbufs
LOCALPROC PbufUnlock(tPbuf i)
{
	(void) GlobalUnlock(PbufDat[i]);
}
#endif

#if IncludePbufs
GLOBALOSGLUPROC PbufTransfer(ui3p Buffer,
	tPbuf i, ui5r offset, ui5r count, blnr IsWrite)
{
	HGLOBAL h = PbufDat[i];
	ui3p p0 = GlobalLock(h);
	if (p0 != NULL) {
		void *p = p0 + offset;
		if (IsWrite) {
			(void) memcpy(p, Buffer, count);
		} else {
			(void) memcpy(Buffer, p, count);
		}
	}
	(void) GlobalUnlock(h);
}
#endif

/* --- control mode and internationalization --- */

#include "CONTROLM.h"

/* --- main window info --- */

LOCALVAR HWND MainWnd = NULL;

LOCALVAR int WndX;
LOCALVAR int WndY;

#if UseWinCE
LOCALVAR short oldOrientation;
LOCALVAR unsigned long oldDisplayOrientation;
#endif

#if VarFullScreen
LOCALVAR blnr UseFullScreen = (WantInitFullScreen != 0);
#endif

#if EnableMagnify
LOCALVAR blnr UseMagnify = (WantInitMagnify != 0);
#endif

#if MayFullScreen
LOCALVAR short hOffset;
LOCALVAR short vOffset;
#endif

/* cursor hiding */

LOCALVAR blnr HaveCursorHidden = falseblnr;
LOCALVAR blnr WantCursorHidden = falseblnr;

LOCALPROC ForceShowCursor(void)
{
	if (HaveCursorHidden) {
		HaveCursorHidden = falseblnr;
		(void) ShowCursor(TRUE);
		SetCursor(LoadCursor(NULL, IDC_ARROW));
	}
}

/* cursor moving */

LOCALFUNC blnr MyMoveMouse(si4b h, si4b v)
{
	POINT NewMousePos;
	ui5b difftime;
	blnr IsOk;
	DWORD StartTime = GetTickCount();
	LONG x = h;
	LONG y = v;

#if VarFullScreen
	if (UseFullScreen)
#endif
#if MayFullScreen
	{
		x -= ViewHStart;
		y -= ViewVStart;
	}
#endif

#if EnableMagnify
	if (UseMagnify) {
		x *= MyWindowScale;
		y *= MyWindowScale;
	}
#endif

#if VarFullScreen
	if (UseFullScreen)
#endif
#if MayFullScreen
	{
		x += hOffset;
		y += vOffset;
	}
#endif

	x += WndX;
	y += WndY;

	do {
		(void) SetCursorPos(x, y);
		if (! GetCursorPos(&NewMousePos)) {
			IsOk = falseblnr;
		} else {
			IsOk = (x == NewMousePos.x) && (y == NewMousePos.y);
		}
		difftime = (ui5b)(GetTickCount() - StartTime);
	} while ((! IsOk) && (difftime < 100));
	return IsOk;
}

#if EnableFSMouseMotion
LOCALPROC StartSaveMouseMotion(void)
{
	if (! HaveMouseMotion) {
		if (MyMoveMouse(ViewHStart + (ViewHSize / 2),
				ViewVStart + (ViewVSize / 2)))
		{
			SavedMouseH = ViewHStart + (ViewHSize / 2);
			SavedMouseV = ViewVStart + (ViewVSize / 2);
			HaveMouseMotion = trueblnr;
		}
	}
}
#endif

#if EnableFSMouseMotion
LOCALPROC StopSaveMouseMotion(void)
{
	if (HaveMouseMotion) {
		(void) MyMoveMouse(CurMouseH, CurMouseV);
		HaveMouseMotion = falseblnr;
	}
}
#endif

LOCALVAR blnr MyMouseCaptured = falseblnr;

LOCALPROC MyMouseCaptureSet(blnr v)
{
	if (v != MyMouseCaptured) {
		if (v) {
			(void) SetCapture(MainWnd);
		} else {
			(void) ReleaseCapture();
		}
		MyMouseCaptured = v;
	}
}

LOCALPROC SetCurMouseButton(blnr v)
{
	MyMouseButtonSet(v);
	MyMouseCaptureSet(v);
}

/* keyboard */

/* these constants weren't in the header files I have */
#define myVK_Subtract 0xBD
#define myVK_Equal 0xBB
#define myVK_BackSlash 0xDC
#define myVK_Comma 0xBC
#define myVK_Period 0xBE
#define myVK_Slash 0xBF
#define myVK_SemiColon 0xBA
#define myVK_SingleQuote 0xDE
#define myVK_LeftBracket 0xDB
#define myVK_RightBracket 0xDD
#define myVK_Grave 0xC0

/* some new ones, need to check if in all header versions */
#define myVK_PRIOR 0x21
#define myVK_NEXT 0x22
#define myVK_END 0x23
#define myVK_HOME 0x24
#define myVK_INSERT 0x2D
#define myVK_DELETE 0x2E
#define myVK_HELP 0x2F
#define myVK_SCROLL 0x91
#define myVK_SNAPSHOT 0x2C
#define myVK_PAUSE 0x13
#define myVK_CLEAR 0x0C

#define myVK_OEM_8 0xDF
#define myVK_OEM_102 0xE2

#ifndef ItnlKyBdFix
#define ItnlKyBdFix 0
#endif

#if ItnlKyBdFix
LOCALVAR ui3b MyVkMapA[256];
#endif

#if ItnlKyBdFix
LOCALPROC MyVkSwapZY(void)
{
	MyVkMapA['Z'] = 'Y';
	MyVkMapA['Y'] = 'Z';
}
#endif

#if ItnlKyBdFix
LOCALPROC MyVkSwapGraveQuote(void)
{
	MyVkMapA[myVK_Grave] = myVK_SingleQuote;
	MyVkMapA[myVK_SingleQuote] = myVK_Grave;
}
#endif

#if ItnlKyBdFix
LOCALPROC MyVkSwapSlashSubtract(void)
{
	MyVkMapA[myVK_Slash] = myVK_Subtract;
	MyVkMapA[myVK_Subtract] = myVK_Slash;
}
#endif

#if ItnlKyBdFix
LOCALPROC MyVkSwapAQZWGraveQuote(void)
{
	MyVkSwapGraveQuote();
	MyVkMapA['A'] = 'Q';
	MyVkMapA['Q'] = 'A';
	MyVkMapA['Z'] = 'W';
	MyVkMapA['W'] = 'Z';
}
#endif

#if ItnlKyBdFix
LOCALPROC MyVkMapBelgian(void)
{
	MyVkSwapAQZWGraveQuote();
	MyVkMapA['M'] = myVK_SemiColon;
	MyVkMapA[myVK_SemiColon] = myVK_RightBracket;
	MyVkMapA[myVK_RightBracket] = myVK_LeftBracket;
	MyVkMapA[myVK_LeftBracket] = myVK_Subtract;
	MyVkMapA[myVK_Subtract] = myVK_Equal;
	MyVkMapA[myVK_Equal] = myVK_Slash;
	MyVkMapA[myVK_Slash] = myVK_Period;
	MyVkMapA[myVK_Period] = myVK_Comma;
	MyVkMapA[myVK_Comma] = 'M';
}
#endif

#if ItnlKyBdFix
LOCALPROC MyVkMapSwiss(void)
{
	MyVkSwapZY();
	MyVkMapA[myVK_OEM_8] = myVK_BackSlash;
	MyVkMapA[myVK_BackSlash] = myVK_SingleQuote;
	MyVkMapA[myVK_SingleQuote] = myVK_SemiColon;
	MyVkMapA[myVK_SemiColon] = myVK_LeftBracket;
	MyVkMapA[myVK_LeftBracket] = myVK_Subtract;
	MyVkMapA[myVK_Subtract] = myVK_Slash;
	MyVkMapA[myVK_Slash] = myVK_Grave;
	MyVkMapA[myVK_Grave] = myVK_RightBracket;
	MyVkMapA[myVK_RightBracket] = myVK_Equal;
}
#endif

#if ItnlKyBdFix
LOCALPROC MyVkMapDanish(void)
{
	MyVkMapA[myVK_Equal] = myVK_Subtract;
	MyVkMapA[myVK_Subtract] = myVK_Slash;
	MyVkMapA[myVK_Slash] = myVK_BackSlash;
	MyVkMapA[myVK_BackSlash] = myVK_Grave;
	MyVkMapA[myVK_Grave] = myVK_SemiColon;
	MyVkMapA[myVK_SemiColon] = myVK_RightBracket;
	MyVkMapA[myVK_RightBracket] = myVK_LeftBracket;
	MyVkMapA[myVK_LeftBracket] = myVK_Equal;
}
#endif

#if ItnlKyBdFix
LOCALPROC MyVkMapBritish(void)
{
	MyVkMapA[myVK_OEM_8] = myVK_Grave;
	MyVkMapA[myVK_Grave] = myVK_SingleQuote;
	MyVkMapA[myVK_SingleQuote] = myVK_BackSlash;
	MyVkMapA[myVK_BackSlash] = myVK_OEM_102;
}
#endif

#if ItnlKyBdFix
LOCALPROC MyVkMapSpanish(void)
{
	MyVkMapA[myVK_SemiColon] = myVK_LeftBracket;
	MyVkMapA[myVK_LeftBracket] = myVK_Subtract;
	MyVkMapA[myVK_Subtract] = myVK_Slash;
	MyVkMapA[myVK_Slash] = myVK_BackSlash;
	MyVkMapA[myVK_BackSlash] = myVK_Grave;
	MyVkMapA[myVK_Grave] = myVK_SemiColon;

	MyVkMapA[myVK_RightBracket] = myVK_Equal;
	MyVkMapA[myVK_Equal] = myVK_RightBracket;
}
#endif

#if ItnlKyBdFix
LOCALPROC MyVkMapDutch(void)
{
	MyVkSwapGraveQuote();
	MyVkMapA[myVK_SemiColon] = myVK_RightBracket;
	MyVkMapA[myVK_RightBracket] = myVK_LeftBracket;
	MyVkMapA[myVK_LeftBracket] = myVK_Subtract;
	MyVkMapA[myVK_Subtract] = myVK_Slash;
	MyVkMapA[myVK_Slash] = myVK_Equal;
	MyVkMapA[myVK_Equal] = myVK_SemiColon;
}
#endif

#if ItnlKyBdFix
LOCALPROC MyVkMapGreekIBM(void)
{
	MyVkSwapSlashSubtract();
	MyVkMapA[myVK_LeftBracket] = myVK_Equal;
	MyVkMapA[myVK_Equal] = myVK_LeftBracket;
}
#endif

#if ItnlKyBdFix
LOCALPROC MyVkMapFrench(void)
{
	MyVkSwapAQZWGraveQuote();
	MyVkMapA['M'] = myVK_SemiColon;
	MyVkMapA[myVK_SemiColon] = myVK_RightBracket;
	MyVkMapA[myVK_RightBracket] = myVK_LeftBracket;
	MyVkMapA[myVK_LeftBracket] = myVK_Subtract;
	MyVkMapA[myVK_Comma] = 'M';
	MyVkMapA[myVK_Period] = myVK_Comma;
	MyVkMapA[myVK_Slash] = myVK_Period;
	MyVkMapA[myVK_OEM_8] = myVK_Slash;
}
#endif

#if ItnlKyBdFix
LOCALPROC MyVkMapGerman(void)
{
	MyVkSwapZY();
	MyVkMapSpanish();
}
#endif

#if ItnlKyBdFix
LOCALPROC MyVkMapBosnian(void)
{
	MyVkSwapZY();
	/* not in Windows 95 */
	MyVkSwapSlashSubtract();
}
#endif

#if ItnlKyBdFix
LOCALPROC MyVkMapBulgarian(void)
{
	MyVkMapA[myVK_OEM_8] = myVK_Comma;
	MyVkMapA[myVK_Comma] = 'Q';
	MyVkMapA['Q'] = myVK_Period;
	MyVkMapA[myVK_Period] = myVK_Equal;
}
#endif

#if ItnlKyBdFix
LOCALPROC MyVkMapFromLayout(uimr sv)
{
	int i;

	for (i = 0; i < 256; ++i) {
		MyVkMapA[i] = i;
	}

	switch (sv) {
		case 0x00000409:
			/* United States 101 */
			break;
		case 0x0000041c:
			/* Albanian; */
			MyVkSwapZY();
			break;
		case 0x0000042B:
			/* Armenian Eastern; */
			MyVkMapDutch();
			break;
		case 0x0001042B:
			/* Armenian Western; */
			MyVkMapDutch();
			break;
		case 0x0000042C:
			/* not in Windows 95 */
			/* Azeri Latin */
			MyVkMapBritish();
			break;
		case 0x0001080C:
			/* Belgian (comma) */
			MyVkMapBelgian();
			break;
		case 0x0000080c:
			/* Belgian French */
			MyVkMapBelgian();
			break;
		case 0x00000813:
			/* not in Windows 95 */
			/* Belgian (period); */
			MyVkMapBelgian();
			break;
		case 0x0000141A:
			/* not in Windows 95 */
			/* Bosnian */
			MyVkMapBosnian();
			break;
		case 0x00000809:
			/* British / United Kingdom */
			MyVkMapBritish();
			break;
		case 0x00000452:
			/* not in Windows 95 */
			/* United Kingdom Extended */
			MyVkMapBritish();
			break;
		case 0x00000402:
			/* Bulgarian */
			/* not same in Windows 95 */
			MyVkMapBulgarian();
			break;
		case 0x00030402:
			/* Bulgarian */
			MyVkMapBulgarian();
			break;
		case 0x00020402:
			/* Bulgarian (Phonetic) */
			MyVkMapBosnian();
			break;
		case 0x00001009:
			/* Canadian Multilingual */
			/* not in Windows 95 */
			MyVkSwapGraveQuote();
			break;
		case 0x00011009:
			/* Canadian Standard */
			MyVkSwapGraveQuote();
			break;
		case 0x0000041a:
			/* Croatian */
			MyVkMapBosnian();
			break;
		case 0x00000405:
			/* Czech */
			MyVkMapBosnian();
#if 0
			/* but Windows 7 gives */
			MyVkSwapZY();
			MyVkMapA[myVK_Equal] = myVK_Subtract;
			MyVkMapA[myVK_Subtract] = myVK_Slash;
			MyVkMapA[myVK_Slash] = myVK_Equal;
#endif
			break;
		case 0x00020405:
			/* Czech (Programmers) */
			/* only in Windows 95 */
			/* MyVkSwapZY(); */
			break;
		case 0x00010405:
			/* Czech (Qwerty) */
			/* only in Windows 95 */
			/* MyVkSwapZY(); */
			break;
		case 0x00000406:
			/* Danish */
			MyVkMapDanish();
			break;
		case 0x00000413:
			/* Dutch */
			MyVkMapDutch();
			break;
		case 0x00000425:
			/* Estonian */
			MyVkMapA[myVK_Grave] = myVK_LeftBracket;
			MyVkMapA[myVK_LeftBracket] = myVK_RightBracket;
			MyVkMapA[myVK_RightBracket] = myVK_Slash;
			MyVkMapA[myVK_Slash] = myVK_SingleQuote;
			MyVkMapA[myVK_SingleQuote] = myVK_Grave;
			/* only in Windows 95 ? */
			/* MyVkMapA[VK_DECIMAL] = VK_DELETE; */
			break;
		case 0x00000438:
			/* Faeroe Islands */
			MyVkMapDanish();
			break;
		case 0x0000040b:
			/* Finnish */
			MyVkMapDanish();
			break;
		case 0x0001083B:
			/* not in Windows 95 */
			/* Finnish with Sami */
			MyVkMapDanish();
			break;
		case 0x0000040c:
			/* v = kMyKbdFrench; */
			/* French */
			MyVkMapFrench();
			break;
		case 0x00000c0c:
			/* French Canadian */
			MyVkSwapGraveQuote();
			break;
		case 0x00011809:
			/* not in Windows 95 */
			/* Gaelic */
			MyVkMapBritish();
			break;
		case 0x00010407:
			/* German (IBM) */
			MyVkMapGerman();
			break;
		case 0x00000407:
			/* German (Standard) */
			MyVkMapGerman();
			break;
		case 0x00010408:
			/* Greek IBM 220 */
			/* not in Windows 95 */
			MyVkMapGreekIBM();
			break;
		case 0x00030408:
			/* Greek IBM 319 */
			/* not in Windows 95 */
			MyVkMapGreekIBM();
			break;
		case 0x00020408:
			/* Greek Latin IBM 220 */
			/* not in Windows 95 */
			MyVkSwapSlashSubtract();
			break;
		case 0x00040408:
			/* Greek Latin IBM 319 */
			/* not in Windows 95 */
			MyVkSwapSlashSubtract();
			break;
		case 0x0000040e:
			/* Hungarian */
			MyVkMapBosnian();
			MyVkMapA[myVK_Grave] = '0';
			MyVkMapA['0'] = myVK_Grave;
			break;
		case 0x0001040E:
			/* Hungarian (101 Keys) */
			MyVkMapA[myVK_Grave] = '0';
			MyVkMapA['0'] = myVK_Grave;
			break;
		case 0x0000040f:
			/* Icelandic */
			MyVkMapDanish();
			break;
		case 0x00001809:
			/* Irish */
			MyVkMapBritish();
			break;
		case 0x00000410:
			/* Italian */
			MyVkMapSpanish();
			break;
		case 0x00010410:
			/* Italian 142 */
			MyVkMapSpanish();
			break;
		case 0x0000080a:
			/* Latin American */
			MyVkMapSpanish();
			break;
		case 0x0000046E:
			/* Luxembourgish */
			MyVkMapSwiss();
			break;
		case 0x00000414:
			/* Norwegian */
			MyVkMapDanish();
			break;
		case 0x0000043B:
			/* Norwegian with Sami */
			MyVkMapDanish();
			break;
		case 0x00010415:
			/* Polish (214) */
			MyVkSwapZY();
			/* not in windows 95 */
			MyVkMapA[myVK_Equal] = myVK_Subtract;
			MyVkMapA[myVK_Subtract] = myVK_Slash;
			MyVkMapA[myVK_Slash] = myVK_Equal;
			break;
		case 0x00010416:
			/* Porguguese (Brazilian ABNT2) */
			/* MyVkMapA[myVK_OEM_8] = ??; */
			/* MyVkMapA[VK_SEPARATOR] = ??; */
			break;
		case 0x00000816:
			/* Porguguese (Standard) */
			MyVkMapA[myVK_SemiColon] = myVK_RightBracket;
			MyVkMapA[myVK_RightBracket] = myVK_Equal;
			MyVkMapA[myVK_Equal] = myVK_LeftBracket;
			MyVkMapA[myVK_LeftBracket] = myVK_Subtract;
			MyVkMapA[myVK_Subtract] = myVK_Slash;
			MyVkMapA[myVK_Slash] = myVK_BackSlash;
			MyVkMapA[myVK_BackSlash] = myVK_Grave;
			MyVkMapA[myVK_Grave] = myVK_SemiColon;
			break;
		case 0x00000418:
			/* Romanian (Legacy) */
			MyVkSwapZY();
			/* only in Windows 95 */
			/* MyVkSwapSlashSubtract(); */
			break;
		case 0x0002083B:
			/* Sami Extended Finland-Sweden */
			MyVkMapDanish();
			break;
		case 0x0001043B:
			/* Sami Extended Norway */
			MyVkMapDanish();
			break;
		case 0x00010C1A:
			/* in Windows 95 */
			/* Serbian (Latin) */
			MyVkSwapZY();
			break;
		case 0x0000081A:
			/* not in Windows 95 */
			/* Serbian (Latin) */
			MyVkMapBosnian();
			break;
		case 0x0000041b:
			/* Slovak */
			MyVkMapBosnian();
			/* not in Windows 95 */
			MyVkMapA[myVK_OEM_8] = myVK_Equal;
			break;
		case 0x00000424:
			/* Slovenian */
			MyVkMapBosnian();
			break;
		case 0x0000040A:
			/* Spanish, not windows 95 */
			MyVkMapSpanish();
			break;
		case 0x0001040A:
			/* Spanish Variation, not windows 95 */
			MyVkMapA[myVK_OEM_8] = myVK_Slash;
			MyVkMapA[myVK_Slash] = myVK_BackSlash;
			MyVkMapA[myVK_BackSlash] = myVK_Grave;
			MyVkMapA[myVK_Grave] = myVK_SemiColon;
			MyVkMapA[myVK_SemiColon] = myVK_RightBracket;
			MyVkMapA[myVK_RightBracket] = myVK_LeftBracket;
			MyVkMapA[myVK_LeftBracket] = myVK_Equal;
			break;
		case 0x00000c0a:
			/* kMyKbdSpanish; */
			/* Spanish Modern, windows 95 */
			MyVkMapSpanish();
			break;
		case 0x00000403:
			/* Spanish Traditional */
			MyVkMapSpanish();
			break;
		case 0x0000041d:
			/* Swedish */
			MyVkMapDanish();
			break;
		case 0x0000083B:
			/* not in windows 95 */
			/* Swedish with Sami */
			MyVkMapDanish();
			break;
		case 0x0000100c:
			/* Swiss French */
			MyVkMapSwiss();
			break;
		case 0x00000807:
			/* Swiss German */
			MyVkMapSwiss();
			break;
		case 0x0000085D:
			/* Inuktitut Latin */
			/* in windows 7, not XP */
			MyVkMapBritish();
			break;
		case 0x0001045D:
			/* Inuktitut - Naqittaut */
			MyVkMapBritish();
			break;
		case 0x0000046F:
			/* Greenlandic */
			MyVkMapDanish();
			break;
		case 0x00020427:
			/* Lithuanian Standard */
			MyVkMapDanish();
			break;
		case 0x0000042f:
			/* Macedonian (FYROM) - Standard */
			MyVkMapBosnian();
			break;
		case 0x0000042E:
			/* Sorbian Standard (Legacy) */
			MyVkMapGerman();
			break;
		case 0x0001042E:
			/* Sorbian Extended */
			MyVkMapGerman();
			break;
		case 0x0002042E:
			/* Sorbian Standard */
			MyVkMapGerman();
			break;
		case 0x00000488:
			/* Wolof */
			MyVkMapFrench();
			break;
		case 0x0000041f:
			/* Turkish (Q type) */
			/* windows 95 */
			/* MyVkMapA[myVK_Equal] = myVK_Subtract; */
			/* MyVkMapA[myVK_Subtract] = myVK_Equal; */
			/* not windows 95 */
			MyVkMapA[myVK_OEM_8] = myVK_Subtract;
			MyVkMapA[myVK_Subtract] = myVK_Equal;

			MyVkMapA[myVK_Comma] = myVK_BackSlash;
			MyVkMapA[myVK_BackSlash] = myVK_Period;
			MyVkMapA[myVK_Period] = myVK_Slash;
			MyVkMapA[myVK_Slash] = myVK_Comma;
			break;
		case 0x00010409:
			/* United States Dvorak */
			MyVkMapA[myVK_LeftBracket] = myVK_Subtract;
			MyVkMapA[myVK_RightBracket] = myVK_Equal;
			MyVkMapA[myVK_SingleQuote] = 'Q';
			MyVkMapA[myVK_Comma] = 'W';
			MyVkMapA[myVK_Period] = 'E';
			MyVkMapA['P'] = 'R';
			MyVkMapA['Y'] = 'T';
			MyVkMapA['F'] = 'Y';
			MyVkMapA['G'] = 'U';
			MyVkMapA['C'] = 'I';
			MyVkMapA['R'] = 'O';
			MyVkMapA['L'] = 'P';
			MyVkMapA[myVK_Slash] = myVK_LeftBracket;
			MyVkMapA[myVK_Equal] = myVK_RightBracket;
			MyVkMapA['O'] = 'S';
			MyVkMapA['E'] = 'D';
			MyVkMapA['U'] = 'F';
			MyVkMapA['I'] = 'G';
			MyVkMapA['D'] = 'H';
			MyVkMapA['H'] = 'J';
			MyVkMapA['T'] = 'K';
			MyVkMapA['N'] = 'L';
			MyVkMapA['S'] = myVK_SemiColon;
			MyVkMapA[myVK_Subtract] = myVK_SingleQuote;
			MyVkMapA[myVK_SemiColon] = 'Z';
			MyVkMapA['Q'] = 'X';
			MyVkMapA['J'] = 'C';
			MyVkMapA['K'] = 'V';
			MyVkMapA['X'] = 'B';
			MyVkMapA['B'] = 'N';
			MyVkMapA['W'] = myVK_Comma;
			MyVkMapA['V'] = myVK_Period;
			MyVkMapA['Z'] = myVK_Slash;
			break;
#if 0
		/* too complicated, don't bother with */
		case 0x0x00000426:
			/* Latvian */
			MyVkMapA['F'] = myVK_Equal;
			MyVkMapA['G'] = 'W';
			MyVkMapA['J'] = 'E';
			MyVkMapA['M'] = 'T';
			MyVkMapA['V'] = 'Y';
			MyVkMapA['N'] = 'U';
			MyVkMapA['Z'] = 'I';
			MyVkMapA['W'] = 'O';
			MyVkMapA['X'] = 'P';
			MyVkMapA['Y'] = myVK_LeftBracket;
			MyVkMapA['H'] = myVK_RightBracket;
			MyVkMapA[myVK_SemiColon] = 'A';
			MyVkMapA['U'] = 'S';
			MyVkMapA['S'] = 'D';
			MyVkMapA['I'] = 'F';
			MyVkMapA['L'] = 'G';
			MyVkMapA['D'] = 'H';
			MyVkMapA['A'] = 'J';
			MyVkMapA['T'] = 'K';
			MyVkMapA['E'] = 'L';
			MyVkMapA['C'] = myVK_SemiColon;
			MyVkMapA[myVK_LeftBracket] = 'Z';
			MyVkMapA['B'] = 'X';
			MyVkMapA[myVK_RightBracket] = 'C';
			MyVkMapA['K'] = 'V';
			MyVkMapA['P'] = 'B';
			MyVkMapA['O'] = 'N';
			MyVkMapA[myVK_OEM_8] = 'M';
			break;
		case 0x0001041F:
			/* Turkish (F type) */

			MyVkMapA[myVK_Equal] = myVK_Subtract;
			MyVkMapA[myVK_Subtract] = myVK_Equal;
			MyVkMapA['F'] = 'Q';
			MyVkMapA['G'] = 'W';
			MyVkMapA[myVK_SemiColon] = 'E';
			MyVkMapA['I'] = 'R';
			MyVkMapA['O'] = 'T';
			MyVkMapA['D'] = 'Y';
			MyVkMapA['R'] = 'U';
			MyVkMapA['N'] = 'I';
			MyVkMapA['H'] = 'O';
			MyVkMapA['Q'] = myVK_LeftBracket;
			MyVkMapA['W'] = myVK_RightBracket;
			MyVkMapA['U'] = 'A';
			MyVkMapA[myVK_LeftBracket] = 'S';
			MyVkMapA['E'] = 'D';
			MyVkMapA['A'] = 'F';
			MyVkMapA[myVK_RightBracket] = 'G';
			MyVkMapA['T'] = 'H';
			MyVkMapA['K'] = 'J';
			MyVkMapA['M'] = 'K';
			MyVkMapA['Y'] = myVK_SemiColon;
			MyVkMapA['X'] = myVK_BackSlash;
			MyVkMapA['J'] = 'Z';
			MyVkMapA[myVK_BackSlash] = 'X';
			MyVkMapA['V'] = 'C';
			MyVkMapA['C'] = 'V';
			MyVkMapA[myVK_Slash] = 'B';
			MyVkMapA['Z'] = 'N';
			MyVkMapA['S'] = 'M';
			MyVkMapA['B'] = myVK_Comma;
			MyVkMapA[myVK_Comma] = myVK_Slash;
			break;
		case 0x00030409:
			/* United States LH Dvorak */
			MyVkMapA[myVK_LeftBracket] = '1';
			MyVkMapA[myVK_RightBracket] = '2';
			MyVkMapA[myVK_Slash] = '3';
			MyVkMapA['P'] = '4';
			MyVkMapA['F'] = '5';
			MyVkMapA['M'] = '6';
			MyVkMapA['L'] = '7';
			MyVkMapA['J'] = '8';
			MyVkMapA['4'] = '9';
			MyVkMapA['3'] = '0';
			MyVkMapA['2'] = myVK_Subtract;
			MyVkMapA['1'] = myVK_Equal;
			MyVkMapA[myVK_SemiColon] = 'Q';
			MyVkMapA['Q'] = 'W';
			MyVkMapA['B'] = 'E';
			MyVkMapA['Y'] = 'R';
			MyVkMapA['U'] = 'T';
			MyVkMapA['R'] = 'Y';
			MyVkMapA['S'] = 'U';
			MyVkMapA['O'] = 'I';
			MyVkMapA[myVK_Period] = 'O';
			MyVkMapA['6'] = 'P';
			MyVkMapA['5'] = myVK_LeftBracket;
			MyVkMapA[myVK_Equal] = myVK_RightBracket;
			MyVkMapA[myVK_Subtract] = 'A';
			MyVkMapA['K'] = 'S';
			MyVkMapA['C'] = 'D';
			MyVkMapA['D'] = 'F';
			MyVkMapA['T'] = 'G';
			MyVkMapA['E'] = 'J';
			MyVkMapA['A'] = 'K';
			MyVkMapA['Z'] = 'L';
			MyVkMapA['8'] = myVK_SemiColon;
			MyVkMapA['7'] = myVK_SingleQuote;
			MyVkMapA[myVK_SingleQuote] = 'Z';
			MyVkMapA['G'] = 'C';
			MyVkMapA['W'] = 'B';
			MyVkMapA['I'] = 'M';
			MyVkMapA['0'] = myVK_Period;
			MyVkMapA['9'] = myVK_Slash;
			break;
		case 0x00040409:
			/* United States RH Dvorak */
			MyVkMapA['J'] = '5';
			MyVkMapA['L'] = '6';
			MyVkMapA['M'] = '7';
			MyVkMapA['F'] = '8';
			MyVkMapA['P'] = '9';
			MyVkMapA[myVK_Slash] = '0';
			MyVkMapA[myVK_LeftBracket] = myVK_Subtract;
			MyVkMapA[myVK_RightBracket] = myVK_Equal;
			MyVkMapA['5'] = 'Q';
			MyVkMapA['6'] = 'W';
			MyVkMapA['Q'] = 'E';
			MyVkMapA[myVK_Period] = 'R';
			MyVkMapA['O'] = 'T';
			MyVkMapA['R'] = 'Y';
			MyVkMapA['S'] = 'U';
			MyVkMapA['U'] = 'I';
			MyVkMapA['Y'] = 'O';
			MyVkMapA['B'] = 'P';
			MyVkMapA[myVK_SemiColon] = myVK_LeftBracket;
			MyVkMapA[myVK_Equal] = myVK_RightBracket;
			MyVkMapA['7'] = 'A';
			MyVkMapA['8'] = 'S';
			MyVkMapA['Z'] = 'D';
			MyVkMapA['A'] = 'F';
			MyVkMapA['E'] = 'G';
			MyVkMapA['T'] = 'J';
			MyVkMapA['D'] = 'K';
			MyVkMapA['C'] = 'L';
			MyVkMapA['K'] = myVK_SemiColon;
			MyVkMapA[myVK_Subtract] = myVK_SingleQuote;
			MyVkMapA['9'] = 'Z';
			MyVkMapA['0'] = 'X';
			MyVkMapA['X'] = 'C';
			MyVkMapA[myVK_Comma] = 'V';
			MyVkMapA['I'] = 'B';
			MyVkMapA['W'] = 'M';
			MyVkMapA['V'] = myVK_Comma;
			MyVkMapA['G'] = myVK_Period;
			MyVkMapA[myVK_SingleQuote] = myVK_Slash;
			break;
#endif
#if 0
		case 0x0000082C:
			/* not in Windows 95 */
			/* Azeri Cyrillic */
			break;
		case 0x00000423:
			/* Belarusian */
			break;
		case 0x00000445:
			/* not in Windows 95 */
			/* Bengali */
			break;
		case 0x00010445:
			/* not in Windows 95 */
			/* Bengali (Inscript) */
			break;
		case 0x0000201A:
			/* not in Windows 95 */
			/* Bosnian Cyrillic*/
			break;
		case 0x00010402:
			/* Bulgarian Latin */
#if 0 /* Only in Windows 95 */
			MyVkMapA['J'] = 'Q';
			MyVkMapA['C'] = 'W';
			MyVkMapA['U'] = 'E';
			MyVkMapA['K'] = 'R';
			MyVkMapA['E'] = 'T';
			MyVkMapA['N'] = 'Y';
			MyVkMapA['G'] = 'U';
			MyVkMapA[myVK_SemiColon] = 'I';
			MyVkMapA[myVK_OEM_102] = 'O';
			MyVkMapA['Z'] = 'P';
			MyVkMapA['H'] = myVK_LeftBracket;
			MyVkMapA['F'] = 'A';
			MyVkMapA['Y'] = 'S';
			MyVkMapA['W'] = 'D';
			MyVkMapA['A'] = 'F';
			MyVkMapA['P'] = 'G';
			MyVkMapA['R'] = 'H';
			MyVkMapA['O'] = 'J';
			MyVkMapA['L'] = 'K';
			MyVkMapA['D'] = 'L';
			MyVkMapA['V'] = myVK_SemiColon;
			MyVkMapA[myVK_LeftBracket] = 'Z';
			MyVkMapA['S'] = 'X';
			MyVkMapA['M'] = 'C';
			MyVkMapA['I'] = 'V';
			MyVkMapA['T'] = 'B';
			MyVkMapA['X'] = 'N';
			MyVkMapA['B'] = 'M';
			MyVkMapA['Q'] = myVK_OEM_102;
#endif
			break;
		case 0x00000408:
			/* Greek */
			break;
		case 0x00050408:
			/* Greek Latin */
			break;
		case 0x00060408:
			/* Greek Polytonic */
			break;
		case 0x0000043F:
			/* Kazakh */
			break;
		case 0x00000440:
			/* Kyrgyz Cyrillic */
			break;
		case 0x00010426:
			/* Latvian Latin */
			break;
		case 0x00010427:
			/* Lithuanian */
			break;
		case 0x00000427:
			/* Lithuanian (IBM) */
			break;
		case 0x0000044C:
			/* Malayalam */
			break;
		case 0x0000042f:
			/* Macedonian (FYROM) */
			break;
		case 0x0000043A:
			/* Maltese 47-key */
			break;
		case 0x0001043A:
			/* Maltese 48-key */
			break;
		case 0x00000481:
			/* Maori */
			break;
		case 0x00000450:
			/* Mongolian Cyrillic */
			break;
		case 0x00000461:
			/* Nepali */
			break;
		case 0x00000463:
			/* Pashto */
			break;
		case 0x00000415:
			/* Polish (Programmers) */
			break;
		case 0x00000416:
			/* Porguguese (Brazilian standard) */
			break;
		case 0x00000419:
			/* Russian */
			break;
		case 0x00010419:
			/* Russian (Typewriter) */
			break;
		case 0x00000c1a:
			/* Serbian */
			break;
		case 0x0001041B:
			/* Slovak (Qwerty) */
			break;
		case 0x00000444:
			/* Tatar */
			break;
		case 0x00000422:
			/* Ukrainian */
			break;
		case 0x00020409:
			/* United States International */
			break;
		case 0x00000843:
			/* Uzbek Cyrillic */
			break;
		case 0x00010418:
			/* Romanian (Standard) */
			break;
		case 0x00020418:
			/* Romanian (Programmers) */
			break;
		case 0x00000401:
			/* Arabic (101) */
			break;
		case 0x00010401:
			/* Arabic (102) */
			break;
		case 0x0000044D:
			/* Assamese - INSCRIPT */
			break;
		case 0x0000046D:
			/* Bashkir */
			break;
		case 0x00040402:
			/* Bulgarian (Phonetic Traditional) */
			break;
		case 0x00000404:
			/* Chinese (Traditional) */
			break;
		case 0x00000804:
			/* Chinese (Simplified) */
			break;
		case 0x00000C04:
			/* Chinese (Traditional, Hong Kong S.A.R.) */
			break;
		case 0x00001004:
			/* Chinese (Simplified, Singapore) */
			break;
		case 0x00001404:
			/* Chinese (Traditional, Macao S.A.R.) */
			break;
		case 0x0000040D:
			/* Hebrew */
			break;
		case 0x00000447:
			/* Gujarati */
			break;
		case 0x00000468:
			/* Hausa */
			break;
		case 0x00010439:
			/* Hindi Traditional */
			break;
		case 0x00000439:
			/* Devanagari - INSCRIPT */
			break;
		case 0x00000465:
			/* Divehi Phonetic */
			break;
		case 0x00010465:
			/* Divehi Typewriter */
			break;
		case 0x00000437:
			/* Georgian */
			break;
		case 0x00010437:
			/* Georgian (QWERTY) */
			break;
		case 0x00020437:
			/* Georgian (Ergonomic) */
			break;
		case 0x00000470:
			/* Igbo */
			break;
		case 0x00000411:
			/* Japanese */
			/* MyVkMapA[??] = ??; */
			break;
		case 0x00000412:
			/* Korean */
			/* MyVkMapA[VK_ZOOM] = ??; */
			/* MyVkMapA[VK_HELP] = VK_ZOOM; */
			/* MyVkMapA[??] = VK_HELP; */
			/* MyVkMapA[??] = ??; */
			break;
		case 0x0000044B:
			/* Kannada */
			break;
		case 0x00000453:
			/* Khmer */
			break;
		case 0x00000454:
			/* Lao */
			break;
		case 0x00000448:
			/* Oriya */
			break;
		case 0x0000044E:
			/* Marathi */
			break;
		case 0x00000850:
			/* Mongolian (Mongolian Script) */
			break;
		case 0x00000429:
			/* Persion */
			break;
		case 0x00000446:
			/* Punjabi */
			break;
		case 0x0000046C:
			/* Sesotho sa Leboa */
			break;
		case 0x00000432:
			/* Setswana */
			break;
		case 0x0000045B:
			/* Sinhala */
			break;
		case 0x0001045B:
			/* Sinhala - Wij 9 */
			break;
		case 0x0000045A:
			/* Syriac */
			break;
		case 0x0001045A:
			/* Syriac Phonetic */
			break;
		case 0x00000428:
			/* Tajik */
			break;
		case 0x00000449:
			/* Tamil */
			break;
		case 0x0000044A:
			/* Telugu */
			break;
		case 0x0000041E:
			/* Thai Kedmanee */
			break;
		case 0x0001041E:
			/* Thai Pattachote */
			break;
		case 0x0002041E:
			/* Thai Kedmanee (non-ShiftLock) */
			break;
		case 0x0003041E:
			/* Thai Pattachote (non-ShiftLock) */
			break;
		case 0x00000451:
			/* Tibetan (PRC) */
			break;
		case 0x00000442:
			/* Turkmen */
			break;
		case 0x00020422:
			/* Ukrainian (Enhanced) */
			break;
		case 0x00000420:
			/* Urdu */
			break;
		case 0x00050409:
			/* US English Table for IBM Arabic 238_L */
			break;
		case 0x00000480:
			/* Uyghur (Legacy) */
			break;
		case 0x00010480:
			/* Uyghur */
			break;
		case 0x0000042A:
			/* Vietnamese */
			break;
		case 0x00000485:
			/* Yakut */
			break;
		case 0x0000046A:
			/* Yoruba */
			break;
#endif
	}
}
#endif

#if ItnlKyBdFix
LOCALVAR uimr CurKyBdLytNm = 0;
#endif

#if ItnlKyBdFix
LOCALFUNC blnr tStrIsHex(TCHAR *s, int n, uimr *r)
{
	short i;
	TCHAR c1;
	TCHAR *p = s;
	uimr v = 0;

	for (i = n; --i >= 0; ) {
		v <<= 4;
		c1 = *p++;
		if ((c1 >= '0') && (c1 <= '9')) {
			v += c1 - '0';
		} else if ((c1 >= 'A') && (c1 <= 'F')) {
			v += c1 - ('A' - 10);
		} else if ((c1 >= 'a') && (c1 <= 'f')) {
			v += c1 - ('a' - 10);
		} else {
			return falseblnr;
		}
	}

	*r = v;
	return trueblnr;
}
#endif

#if ItnlKyBdFix
LOCALFUNC blnr MyGetKeyboardLayoutHex(uimr *r)
{
	TCHAR s[KL_NAMELENGTH];
	blnr IsOk = falseblnr;

	if (! GetKeyboardLayoutName(s)) {
		/* ReportWinLastError(); */
	} else {
		size_t n = _tcslen(s);

		if (8 != n) {
			/* fail */
		} else {
			IsOk = tStrIsHex(s, n, r);
		}
	}

	return IsOk;
}
#endif

#if ItnlKyBdFix && ! UseWinCE
LOCALPROC MyCheckKeyboardLayout(void)
{
	uimr sv;

	if (! MyGetKeyboardLayoutHex(&sv)) {
	} else if (sv == CurKyBdLytNm) {
		/* no change */
	} else {
		CurKyBdLytNm = sv;

		MyVkMapFromLayout(sv);
	}
}
#endif

#if ItnlKyBdFix
LOCALPROC MyInitCheckKeyboardLayout(void)
{
	uimr sv;

	if (! MyGetKeyboardLayoutHex(&sv)) {
		sv = 0x00000409;
	}

	CurKyBdLytNm = sv;

	MyVkMapFromLayout(sv);
}
#endif

LOCALVAR ui3b WinKey2Mac[256];

LOCALPROC AssignOneMacKey(ui3b WinKey, ui3r MacKey)
{
	WinKey2Mac[WinKey] = MacKey;
}

LOCALFUNC blnr InitWinKey2Mac(void)
{
	int i;

	for (i = 0; i < 256; ++i) {
		WinKey2Mac[i] = MKC_None;
	}

	AssignOneMacKey('A', MKC_A);
	AssignOneMacKey('S', MKC_S);
	AssignOneMacKey('D', MKC_D);
	AssignOneMacKey('F', MKC_F);
	AssignOneMacKey('H', MKC_H);
	AssignOneMacKey('G', MKC_G);
	AssignOneMacKey('Z', MKC_Z);
	AssignOneMacKey('X', MKC_X);
	AssignOneMacKey('C', MKC_C);
	AssignOneMacKey('V', MKC_V);
	AssignOneMacKey('B', MKC_B);
	AssignOneMacKey('Q', MKC_Q);
	AssignOneMacKey('W', MKC_W);
	AssignOneMacKey('E', MKC_E);
	AssignOneMacKey('R', MKC_R);
	AssignOneMacKey('Y', MKC_Y);
	AssignOneMacKey('T', MKC_T);
	AssignOneMacKey('1', MKC_1);
	AssignOneMacKey('2', MKC_2);
	AssignOneMacKey('3', MKC_3);
	AssignOneMacKey('4', MKC_4);
	AssignOneMacKey('6', MKC_6);
	AssignOneMacKey('5', MKC_5);
	AssignOneMacKey(myVK_Equal, MKC_Equal);
	AssignOneMacKey('9', MKC_9);
	AssignOneMacKey('7', MKC_7);
	AssignOneMacKey(myVK_Subtract, MKC_Minus);
	AssignOneMacKey('8', MKC_8);
	AssignOneMacKey('0', MKC_0);
	AssignOneMacKey(myVK_RightBracket, MKC_RightBracket);
	AssignOneMacKey('O', MKC_O);
	AssignOneMacKey('U', MKC_U);
	AssignOneMacKey(myVK_LeftBracket, MKC_LeftBracket);
	AssignOneMacKey('I', MKC_I);
	AssignOneMacKey('P', MKC_P);
	AssignOneMacKey(VK_RETURN, MKC_Return);
	AssignOneMacKey('L', MKC_L);
	AssignOneMacKey('J', MKC_J);
	AssignOneMacKey(myVK_SingleQuote, MKC_SingleQuote);
	AssignOneMacKey('K', MKC_K);
	AssignOneMacKey(myVK_SemiColon, MKC_SemiColon);
	AssignOneMacKey(myVK_BackSlash, MKC_formac_BackSlash);
	AssignOneMacKey(myVK_Comma, MKC_Comma);
	AssignOneMacKey(myVK_Slash, MKC_formac_Slash);
	AssignOneMacKey('N', MKC_N);
	AssignOneMacKey('M', MKC_M);
	AssignOneMacKey(myVK_Period, MKC_Period);

	AssignOneMacKey(VK_TAB, MKC_Tab);
	AssignOneMacKey(VK_SPACE, MKC_Space);
	AssignOneMacKey(myVK_Grave, MKC_formac_Grave);
	AssignOneMacKey(VK_BACK, MKC_BackSpace);
	AssignOneMacKey(VK_ESCAPE, MKC_formac_Escape);

	AssignOneMacKey(VK_MENU, MKC_formac_Command);

	AssignOneMacKey(VK_LMENU, MKC_formac_Command);

	AssignOneMacKey(VK_RMENU, MKC_formac_RCommand);

	AssignOneMacKey(VK_SHIFT, MKC_formac_Shift);
	AssignOneMacKey(VK_LSHIFT, MKC_formac_Shift);
	AssignOneMacKey(VK_RSHIFT, MKC_formac_RShift);

	AssignOneMacKey(VK_CAPITAL, MKC_formac_CapsLock);

	AssignOneMacKey(VK_APPS, MKC_formac_ROption);

	AssignOneMacKey(VK_LWIN, MKC_formac_Option);

	AssignOneMacKey(VK_RWIN, MKC_formac_ROption);

	AssignOneMacKey(VK_CONTROL, MKC_formac_Control);

	AssignOneMacKey(VK_LCONTROL, MKC_formac_Control);

	AssignOneMacKey(VK_RCONTROL, MKC_formac_RControl);

	AssignOneMacKey(VK_F1, MKC_formac_F1);
	AssignOneMacKey(VK_F2, MKC_formac_F2);
	AssignOneMacKey(VK_F3, MKC_formac_F3);
	AssignOneMacKey(VK_F4, MKC_formac_F4);
	AssignOneMacKey(VK_F5, MKC_formac_F5);
	AssignOneMacKey(VK_F6, MKC_F6);
	AssignOneMacKey(VK_F7, MKC_F7);
	AssignOneMacKey(VK_F8, MKC_F8);
	AssignOneMacKey(VK_F9, MKC_F9);
	AssignOneMacKey(VK_F10, MKC_F10);
	AssignOneMacKey(VK_F11, MKC_F11);
	AssignOneMacKey(VK_F12, MKC_F12);

	AssignOneMacKey(VK_DECIMAL, MKC_Decimal);
	AssignOneMacKey(VK_DELETE, MKC_Decimal);
	/* AssignOneMacKey(VK_RIGHT, 0x42); */
	AssignOneMacKey(VK_MULTIPLY, MKC_KPMultiply);
	AssignOneMacKey(VK_ADD, MKC_KPAdd);
	/* AssignOneMacKey(VK_LEFT, 0x46); */
	AssignOneMacKey(VK_NUMLOCK, MKC_Clear);

	/* AssignOneMacKey(VK_DOWN, 0x48); */
	AssignOneMacKey(VK_DIVIDE, MKC_KPDevide);
	/* AssignOneMacKey(VK_RETURN, MKC_formac_Enter); */
	/* AssignOneMacKey(VK_UP, 0x4D); */
	AssignOneMacKey(VK_DIVIDE, MKC_KPDevide);
	AssignOneMacKey(VK_SUBTRACT, MKC_KPSubtract);

	AssignOneMacKey(VK_SEPARATOR, MKC_KPEqual);
	AssignOneMacKey(VK_NUMPAD0, MKC_KP0);
	AssignOneMacKey(VK_NUMPAD1, MKC_KP1);
	AssignOneMacKey(VK_NUMPAD2, MKC_KP2);
	AssignOneMacKey(VK_NUMPAD3, MKC_KP3);
	AssignOneMacKey(VK_NUMPAD4, MKC_KP4);
	AssignOneMacKey(VK_NUMPAD5, MKC_KP5);

	AssignOneMacKey(VK_NUMPAD6, MKC_KP6);
	AssignOneMacKey(VK_NUMPAD7, MKC_KP7);
	AssignOneMacKey(VK_NUMPAD8, MKC_KP8);
	AssignOneMacKey(VK_NUMPAD9, MKC_KP9);

	AssignOneMacKey(VK_LEFT, MKC_Left);
	AssignOneMacKey(VK_RIGHT, MKC_Right);
	AssignOneMacKey(VK_DOWN, MKC_Down);
	AssignOneMacKey(VK_UP, MKC_Up);

	AssignOneMacKey(myVK_PRIOR, MKC_formac_PageUp);
	AssignOneMacKey(myVK_NEXT, MKC_formac_PageDown);
	AssignOneMacKey(myVK_END, MKC_formac_End);
	AssignOneMacKey(myVK_HOME, MKC_formac_Home);
	AssignOneMacKey(myVK_INSERT, MKC_formac_Help);
	AssignOneMacKey(myVK_DELETE, MKC_formac_ForwardDel);
	AssignOneMacKey(myVK_HELP, MKC_formac_Help);
	AssignOneMacKey(myVK_SNAPSHOT, MKC_Print);
	AssignOneMacKey(myVK_SCROLL, MKC_ScrollLock);
	AssignOneMacKey(myVK_PAUSE, MKC_Pause);

	AssignOneMacKey(myVK_OEM_102, MKC_AngleBracket);

	InitKeyCodes();

#if ItnlKyBdFix
	MyInitCheckKeyboardLayout();
#endif

	return trueblnr;
}

LOCALPROC DoKeyCode(int i, blnr down)
{
	ui3r key = WinKey2Mac[
#if ItnlKyBdFix
		MyVkMapA[i]
#else
		i
#endif
		];
	if (MKC_None != key) {
		Keyboard_UpdateKeyMap2(key, down);
	}
}

#ifndef EnableGrabSpecialKeys
#if UseWinCE
#define EnableGrabSpecialKeys 0
#else
#define EnableGrabSpecialKeys (MayFullScreen && GrabKeysFullScreen)
#endif
#endif /* EnableGrabSpecialKeys */

#if EnableGrabSpecialKeys
LOCALVAR blnr HaveSetSysParam = falseblnr;
#endif

LOCALPROC CheckTheCapsLock(void)
{
	DoKeyCode(VK_CAPITAL, (GetKeyState(VK_CAPITAL) & 1) != 0);
}

#if EnableGrabSpecialKeys
LOCALVAR blnr VK_LWIN_pressed = falseblnr;
LOCALVAR blnr VK_RWIN_pressed = falseblnr;
#endif

#if EnableGrabSpecialKeys
LOCALPROC CheckForLostKeyUps(void)
{
	if (HaveSetSysParam) {
		/* check for lost key ups */
		if (VK_LWIN_pressed) {
			if ((GetAsyncKeyState(VK_LWIN) & 0x8000) == 0) {
				DoKeyCode(VK_LWIN, falseblnr);
				VK_LWIN_pressed = falseblnr;
			}
		}
		if (VK_RWIN_pressed) {
			if ((GetAsyncKeyState(VK_RWIN) & 0x8000) == 0) {
				DoKeyCode(VK_RWIN, falseblnr);
				VK_RWIN_pressed = falseblnr;
			}
		}
	}
}
#endif

LOCALPROC DoVKcode0(int i, blnr down)
{
#if EnableGrabSpecialKeys
	if (HaveSetSysParam) {
		/* will need to check for lost key ups */
		if (VK_LWIN == i) {
			VK_LWIN_pressed = down;
		} else if (VK_RWIN == i) {
			VK_RWIN_pressed = down;
		}
	}
#endif
	DoKeyCode(i, down);
}

LOCALPROC DoVKcode(int i, ui3r flags, blnr down)
{
	switch (i) {
#if MKC_formac_Control != MKC_formac_RControl
		case VK_CONTROL:
			Keyboard_UpdateKeyMap2(TestBit(flags, 0)
				? MKC_formac_RControl : MKC_formac_Control,
				down);
			break;
#endif
#if MKC_formac_RCommand != MKC_formac_Command
		case VK_MENU:
			Keyboard_UpdateKeyMap2(TestBit(flags, 0)
				? MKC_formac_RCommand : MKC_formac_Command,
				down);
			break;
#endif
		case VK_RETURN:
			Keyboard_UpdateKeyMap2(TestBit(flags, 0)
				? MKC_formac_Enter : MKC_Return,
				down);
			break;
		case myVK_HOME:
			Keyboard_UpdateKeyMap2(TestBit(flags, 0)
				? MKC_formac_Home : MKC_KP7,
				down);
			break;
		case VK_UP:
			Keyboard_UpdateKeyMap2(TestBit(flags, 0)
				? MKC_Up : MKC_KP8,
				down);
			break;
		case myVK_PRIOR:
			Keyboard_UpdateKeyMap2(TestBit(flags, 0)
				? MKC_formac_PageUp : MKC_KP9,
				down);
			break;
		case VK_LEFT:
			Keyboard_UpdateKeyMap2(TestBit(flags, 0)
				? MKC_Left : MKC_KP4,
				down);
			break;
		case myVK_CLEAR:
			Keyboard_UpdateKeyMap2(TestBit(flags, 0)
				? MKC_Clear : MKC_KP5,
				down);
			break;
		case VK_RIGHT:
			Keyboard_UpdateKeyMap2(TestBit(flags, 0)
				? MKC_Right : MKC_KP6,
				down);
			break;
		case myVK_END:
			Keyboard_UpdateKeyMap2(TestBit(flags, 0)
				? MKC_formac_End : MKC_KP1,
				down);
			break;
		case VK_DOWN:
			Keyboard_UpdateKeyMap2(TestBit(flags, 0)
				? MKC_Down : MKC_KP2,
				down);
			break;
		case myVK_NEXT:
			Keyboard_UpdateKeyMap2(TestBit(flags, 0)
				? MKC_formac_PageDown : MKC_KP3,
				down);
			break;
		case myVK_INSERT:
			Keyboard_UpdateKeyMap2(TestBit(flags, 0)
				? MKC_formac_Help : MKC_KP0,
				down);
			break;
		case myVK_DELETE:
			Keyboard_UpdateKeyMap2(TestBit(flags, 0)
				? MKC_formac_ForwardDel : MKC_Decimal,
				down);
			break;
		case VK_CAPITAL:
			CheckTheCapsLock();
			break;
		default:
			if ((i >= 0) && (i < 256)) {
				DoVKcode0(i, down);
			}
			break;
	}
}

LOCALVAR blnr WantCmdOptOnReconnect = falseblnr;

LOCALPROC ReconnectKeyCodes3(void)
{
	int i;

	CheckTheCapsLock();

	if (WantCmdOptOnReconnect) {
		WantCmdOptOnReconnect = falseblnr;

		for (i = 0; i < 256; ++i) {
			if ((GetKeyState(i) & 0x8000) != 0) {
				if ((VK_CAPITAL != i)
					&& (VK_RETURN != i))
				{
					DoVKcode0(i, trueblnr);
				}
			}
		}
	}
}

LOCALPROC DisconnectKeyCodes3(void)
{
	DisconnectKeyCodes2();
	SetCurMouseButton(falseblnr);
}

#if EnableGrabSpecialKeys
static HHOOK hKeyHook = NULL;
#endif

#if EnableGrabSpecialKeys
typedef struct {
	DWORD   vkCode;
	DWORD   scanCode;
	DWORD   flags;
	DWORD   time;
	DWORD   dwExtraInfo;
} My_KBDLLHOOKSTRUCT;
#endif

#if EnableGrabSpecialKeys
LRESULT CALLBACK LowLevelKeyboardProc(
	int nCode,     /* hook code */
	WPARAM wParam, /* message identifier */
	LPARAM lParam  /* pointer to structure with message data */
);
#endif

#if EnableGrabSpecialKeys
LRESULT CALLBACK LowLevelKeyboardProc(
	int nCode,     /* hook code */
	WPARAM wParam, /* message identifier */
	LPARAM lParam  /* pointer to structure with message data */
)
{
	if (nCode == HC_ACTION) {
		My_KBDLLHOOKSTRUCT *p = (My_KBDLLHOOKSTRUCT *)lParam;
		if (p->vkCode != VK_CAPITAL) {
			switch (wParam) {
				case WM_KEYDOWN:
				case WM_SYSKEYDOWN:
					DoVKcode(p->vkCode, p->flags, trueblnr);
					return 1;
					break;
				case WM_KEYUP:
				case WM_SYSKEYUP:
					DoVKcode(p->vkCode, p->flags, falseblnr);
					return 1;
					break;
			}
		}
	}
	return CallNextHookEx(hKeyHook, /* handle to current hook */
		nCode, /* hook code passed to hook procedure */
		wParam, /* value passed to hook procedure */
		lParam /* value passed to hook procedure */
	);

}
#endif

#if EnableGrabSpecialKeys
#define My_WH_KEYBOARD_LL 13
#define My_SPI_SETSCREENSAVERRUNNING 0x0061
#endif

#if EnableGrabSpecialKeys
LOCALVAR UINT nPreviousState;
#endif

#if EnableGrabSpecialKeys
LOCALPROC GrabSpecialKeys(void)
{
	if ((hKeyHook == NULL) && ! HaveSetSysParam) {
		/* this works on Windows XP */
		hKeyHook = SetWindowsHookEx(
			My_WH_KEYBOARD_LL, /* type of hook to install */
			(HOOKPROC)LowLevelKeyboardProc,
				/* address of hook procedure */
			AppInstance,    /* handle to application instance */
			0   /* identity of thread to install hook for */
		);
		if (hKeyHook == NULL) {
			/* this works on Windows 95/98 */
			SystemParametersInfo(My_SPI_SETSCREENSAVERRUNNING, TRUE,
				&nPreviousState, 0);
			HaveSetSysParam = trueblnr;
		}
	}
}
#endif

#if EnableGrabSpecialKeys
LOCALPROC UnGrabSpecialKeys(void)
{
	if (hKeyHook != NULL) {
		(void) UnhookWindowsHookEx(hKeyHook);
		hKeyHook = NULL;
	}
	if (HaveSetSysParam) {
		SystemParametersInfo(My_SPI_SETSCREENSAVERRUNNING, FALSE,
			&nPreviousState, 0);
		HaveSetSysParam = falseblnr;
	}
}
#endif

/* --- priority --- */

#ifndef EnableChangePriority
#if UseWinCE
#define EnableChangePriority 0
#else
#define EnableChangePriority MayFullScreen
#endif
#endif /* EnableChangePriority */

#if EnableChangePriority
LOCALVAR blnr MyPriorityRaised = falseblnr;
#endif

#if EnableChangePriority
LOCALPROC RaiseMyPriority(void)
{
	if (! MyPriorityRaised) {
		if (! SetPriorityClass(
			GetCurrentProcess(), /* handle to the process */
			HIGH_PRIORITY_CLASS
				/* REALTIME_PRIORITY_CLASS (not, killer) */
				/* priority class value */
			))
		{
			/*
				not recursive:
				MacMsg("SetPriorityClass failed",
					"Sorry, Mini vMac encountered errors"
					" and cannot continue.", trueblnr);
			*/
		}
		MyPriorityRaised = trueblnr;
	}
}
#endif

#if EnableChangePriority
LOCALPROC LowerMyPriority(void)
{
	if (MyPriorityRaised) {
		if (! SetPriorityClass(
			GetCurrentProcess(),        /* handle to the process */
			NORMAL_PRIORITY_CLASS /* priority class value */
			))
		{
			/*
				not recursive:
				MacMsg("SetPriorityClass failed",
					"Sorry, Mini vMac encountered errors"
					" and cannot continue.", trueblnr);
			*/
		}
		MyPriorityRaised = falseblnr;
	}
}
#endif


/* --- time, date, location --- */

#define dbglog_TimeStuff (0 && dbglog_HAVE)

LOCALVAR ui5b TrueEmulatedTime = 0;

#define MyInvTimeDivPow 16
#define MyInvTimeDiv (1 << MyInvTimeDivPow)
#define MyInvTimeDivMask (MyInvTimeDiv - 1)
#define MyInvTimeStep 1089590 /* 1000 / 60.14742 * MyInvTimeDiv */

LOCALVAR DWORD LastTime;

LOCALVAR DWORD NextIntTime;
LOCALVAR ui5b NextFracTime;

LOCALPROC IncrNextTime(void)
{
	NextFracTime += MyInvTimeStep;
	NextIntTime += (NextFracTime >> MyInvTimeDivPow);
	NextFracTime &= MyInvTimeDivMask;
}

LOCALPROC InitNextTime(void)
{
	NextIntTime = LastTime;
	NextFracTime = 0;
	IncrNextTime();
}

LOCALFUNC blnr UpdateTrueEmulatedTime(void)
{
	DWORD LatestTime;
	si5b TimeDiff;

	LatestTime = timeGetTime();
	if (LatestTime != LastTime) {
		LastTime = LatestTime;
		TimeDiff = (LatestTime - NextIntTime);
			/* this should work even when time wraps */
		if (TimeDiff >= 0) {
			if (TimeDiff > 256) {
				/* emulation interrupted, forget it */
				++TrueEmulatedTime;
				InitNextTime();

#if dbglog_TimeStuff
				dbglog_writelnNum("emulation interrupted",
					TrueEmulatedTime);
#endif
			} else {
				do {
					++TrueEmulatedTime;
					IncrNextTime();
					TimeDiff = (LatestTime - NextIntTime);
				} while (TimeDiff >= 0);
			}
			return trueblnr;
		} else if (TimeDiff < -256) {
			/* clock goofed if ever get here, reset */
#if dbglog_TimeStuff
			dbglog_writeln("clock set back");
#endif

			InitNextTime();
		}
	}
	return falseblnr;
}

LOCALVAR ui5b TimeSecBase;
LOCALVAR DWORD TimeMilliBase;

#include "DATE2SEC.h"

LOCALFUNC blnr CheckDateTime(void)
{
	ui5b NewMacDateInSecond;

	NewMacDateInSecond =
		((ui5b)(LastTime - TimeMilliBase)) / 1000 + TimeSecBase;
	if (CurMacDateInSeconds != NewMacDateInSecond) {
		CurMacDateInSeconds = NewMacDateInSecond;

		return trueblnr;
	} else {
		return falseblnr;
	}
}

LOCALFUNC blnr Init60thCheck(void)
{
	SYSTEMTIME s;
#if AutoTimeZone
	TIME_ZONE_INFORMATION r;
	DWORD v;
#endif
	DWORD t;

	GetLocalTime(&s);
	t = timeGetTime();
	TimeSecBase = Date2MacSeconds(s.wSecond, s.wMinute, s.wHour,
		s.wDay, s.wMonth, s.wYear);
	TimeMilliBase = t - s.wMilliseconds;

#if AutoTimeZone
	v = GetTimeZoneInformation(&r);
	if ((v != 0xFFFFFFFF) && (v != TIME_ZONE_ID_UNKNOWN)) {
		si5b dlsBias = (v != TIME_ZONE_ID_DAYLIGHT)
			? r.StandardBias : r.DaylightBias;
		CurMacDelta = (((ui5b)(- (r.Bias + dlsBias) * 60))
			& 0x00FFFFFF)
			| (((v != TIME_ZONE_ID_DAYLIGHT) ? 0 : 0x80)
				<< 24);
	}
#endif

	LastTime = timeGetTime();
	InitNextTime();

	OnTrueTime = TrueEmulatedTime;

	(void) CheckDateTime();

	return trueblnr;
}

#ifndef MyTimeResolution
#define MyTimeResolution 3
#endif
	/*
		Setting MyTimeResolution to 1 seems to drastically slow down
		the clock in Virtual PC 7.0.2 for Mac. Using 3 is more polite
		anyway, and should not cause much observable difference.
	*/

#if (MyTimeResolution != 0)
LOCALVAR blnr HaveSetTimeResolution = falseblnr;
#endif

#if (MyTimeResolution != 0)
LOCALPROC MyTimer_Suspend(void)
{
	if (HaveSetTimeResolution) {
		(void) timeEndPeriod(MyTimeResolution);
		HaveSetTimeResolution = falseblnr;
	}
}
#endif

#if (MyTimeResolution != 0)
LOCALPROC MyTimer_Resume(void)
{
	TIMECAPS tc;

	if (timeGetDevCaps(&tc, sizeof(TIMECAPS))
		== TIMERR_NOERROR)
	{
		if ((MyTimeResolution >= tc.wPeriodMin)
			&& (MyTimeResolution <= tc.wPeriodMax))
		{
			if (timeBeginPeriod(MyTimeResolution)
				== TIMERR_NOERROR)
			{
				HaveSetTimeResolution = trueblnr;
			}
		}
	}
}
#endif

/* --- sound --- */

#if MySoundEnabled


#define kLn2SoundBuffers 4 /* kSoundBuffers must be a power of two */
#define kSoundBuffers (1 << kLn2SoundBuffers)
#define kSoundBuffMask (kSoundBuffers - 1)

#define DesiredMinFilledSoundBuffs 3
	/*
		if too big then sound lags behind emulation.
		if too small then sound will have pauses.
	*/

#define kLnOneBuffLen 9
#define kLnAllBuffLen (kLn2SoundBuffers + kLnOneBuffLen)
#define kOneBuffLen (1UL << kLnOneBuffLen)
#define kAllBuffLen (1UL << kLnAllBuffLen)
#define kLnOneBuffSz (kLnOneBuffLen + kLn2SoundSampSz - 3)
#define kLnAllBuffSz (kLnAllBuffLen + kLn2SoundSampSz - 3)
#define kOneBuffSz (1UL << kLnOneBuffSz)
#define kAllBuffSz (1UL << kLnAllBuffSz)
#define kOneBuffMask (kOneBuffLen - 1)
#define kAllBuffMask (kAllBuffLen - 1)
#define dbhBufferSize (kAllBuffSz + kOneBuffSz)

#define dbglog_SoundStuff (0 && dbglog_HAVE)
#define dbglog_SoundBuffStats (0 && dbglog_HAVE)

LOCALVAR tpSoundSamp TheSoundBuffer = nullpr;
LOCALVAR ui4b ThePlayOffset;
LOCALVAR ui4b TheFillOffset;
LOCALVAR blnr wantplaying;
LOCALVAR ui4b MinFilledSoundBuffs;
LOCALVAR ui4b TheWriteOffset;

#define SOUND_SAMPLERATE /* 22050 */ 22255
	/* = round(7833600 * 2 / 704) */


LOCALPROC FillWithSilence(tpSoundSamp p, int n, trSoundSamp v)
{
	int i;

	for (i = n; --i >= 0; ) {
		*p++ = v;
	}
}


LOCALVAR HWAVEOUT hWaveOut = NULL;
LOCALVAR WAVEHDR whdr[kSoundBuffers];


LOCALPROC MySound_BeginPlaying(void)
{
#if dbglog_SoundStuff
	fprintf(stderr, "MySound_BeginPlaying\n");
#endif
}

LOCALPROC MySound_Start(void)
{
	if (hWaveOut == NULL) {
		WAVEFORMATEX wfex;
		MMRESULT mmr;
		int i;
		tpSoundSamp p;
		WAVEHDR *pwh;

		wfex.wFormatTag = WAVE_FORMAT_PCM;
		wfex.nChannels = 1;
		wfex.nSamplesPerSec = SOUND_SAMPLERATE;
		wfex.nAvgBytesPerSec = SOUND_SAMPLERATE;
#if 3 == kLn2SoundSampSz
		wfex.nBlockAlign = 1;
		wfex.wBitsPerSample = 8;
#elif 4 == kLn2SoundSampSz
		wfex.nBlockAlign = 2;
		wfex.wBitsPerSample = 16;
#else
#error "unsupported audio format"
#endif
		wfex.cbSize = 0;
		mmr = waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfex, 0,
			0 /* (DWORD) AppInstance */, CALLBACK_NULL);
		if (mmr != MMSYSERR_NOERROR) {
			/*
				not recursive:
				MacMsg("waveOutOpen failed",
					"Sorry, Mini vMac encountered errors"
					" and cannot continue.", trueblnr);
			*/
		} else {
			p = TheSoundBuffer;
			pwh = whdr;
			for (i = 0; i < kSoundBuffers; ++i) {
				pwh->lpData = (LPSTR)p;
				pwh->dwBufferLength = kOneBuffSz;
				pwh->dwBytesRecorded = 0;
				pwh->dwUser = 0;
				pwh->dwFlags = 0;
				pwh->dwLoops = 0;
				mmr = waveOutPrepareHeader(hWaveOut, pwh,
					sizeof(WAVEHDR));
				if (mmr != MMSYSERR_NOERROR) {
					/*
						not recursive:
						MacMsg("waveOutPrepareHeader failed",
							"Sorry, Mini vMac encountered errors"
							" and cannot continue.", trueblnr);
					*/
				} else {
					pwh->dwFlags |= WHDR_DONE;
				}
				p += kOneBuffLen;
				++pwh;
			}

			TheFillOffset = 0;
			ThePlayOffset = 0;
			TheWriteOffset = 0;
			MinFilledSoundBuffs = kSoundBuffers;
			wantplaying = falseblnr;
		}
	}
}

LOCALPROC MySound_Stop(void)
{
	MMRESULT mmr;
	int i;

	wantplaying = falseblnr;
	if (hWaveOut != NULL) {
		DWORD StartTime = GetTickCount();
		for (i = 0; i < kSoundBuffers; ++i) {
			while (((whdr[i].dwFlags & WHDR_DONE) == 0)
				&& ((ui5b)(GetTickCount() - StartTime) < 1000))
			{
				Sleep(1);
			}

			mmr = waveOutUnprepareHeader(hWaveOut, &whdr[i],
				sizeof(WAVEHDR));
			if (mmr != MMSYSERR_NOERROR) {
				/*
					not recursive:
					MacMsg("waveOutUnprepareHeader failed",
						"Sorry, Mini vMac encountered errors"
						" and cannot continue.", trueblnr);
				*/
			}
		}

		mmr = waveOutClose(hWaveOut);
		if (mmr != MMSYSERR_NOERROR) {
			/*
				MacMsg("waveOutClose failed",
					"Sorry, Mini vMac encountered errors"
					" and cannot continue.", trueblnr);
			*/
		}
		hWaveOut = NULL;
	}
}

LOCALPROC SoundCheckVeryOften(void)
{
	if ((hWaveOut != NULL) && (wantplaying)) {
label_retry:
		{
			ui4b FilledSoundBuffs;
			ui4b ToPlaySize = TheFillOffset - ThePlayOffset;
			ui4b CurPlayBuffer =
				(ThePlayOffset >> kLnOneBuffLen) & kSoundBuffMask;

			if ((ToPlaySize > kOneBuffLen)
				&& ((whdr[CurPlayBuffer].dwFlags & WHDR_DONE) != 0))
			{
				ThePlayOffset += kOneBuffLen;
				goto label_retry;
			}
			FilledSoundBuffs = ToPlaySize >> kLnOneBuffLen;

			if (FilledSoundBuffs < MinFilledSoundBuffs) {
				MinFilledSoundBuffs = FilledSoundBuffs;
			}

			if (FilledSoundBuffs < 2) {
				MMRESULT mmr;
				ui4b PrevPlayOffset = ThePlayOffset - kOneBuffLen;
				ui4b PrevPlayBuffer =
					(PrevPlayOffset >> kLnOneBuffLen) & kSoundBuffMask;
				ui4b LastPlayedOffset =
					((TheFillOffset >> kLnOneBuffLen) << kLnOneBuffLen)
						- 1;

				FillWithSilence(
					TheSoundBuffer + (PrevPlayOffset & kAllBuffMask),
					kOneBuffLen,
					*(TheSoundBuffer
						+ (LastPlayedOffset & kAllBuffMask)));
				mmr = waveOutWrite(
					hWaveOut, &whdr[PrevPlayBuffer], sizeof(WAVEHDR));
				if (mmr != MMSYSERR_NOERROR) {
					whdr[PrevPlayBuffer].dwFlags |= WHDR_DONE;
					/*
						not recursive:
						MacMsg("waveOutWrite failed",
							"Sorry, Mini vMac encountered errors"
							" and cannot continue.", trueblnr);
					*/
				}
				ThePlayOffset = PrevPlayOffset;
				goto label_retry;
			}
		}
	}
}

#if 4 == kLn2SoundSampSz
LOCALPROC ConvertSoundBlockToNative(tpSoundSamp p)
{
	int i;

	for (i = kOneBuffLen; --i >= 0; ) {
		*p++ -= 0x8000;
	}
}
#else
#define ConvertSoundBlockToNative(p)
#endif

LOCALPROC MySound_FilledBlocks(void)
{
	while (0 != ((TheWriteOffset - TheFillOffset) >> kLnOneBuffLen)) {
		ui4b CurFillBuffer =
			(TheFillOffset >> kLnOneBuffLen) & kSoundBuffMask;
		blnr IsOk = falseblnr;

		ConvertSoundBlockToNative((tpSoundSamp)
			whdr[CurFillBuffer].lpData);

		if (hWaveOut != NULL) {
			MMRESULT mmr = waveOutWrite(hWaveOut,
				&whdr[CurFillBuffer], sizeof(WAVEHDR));
			if (mmr == MMSYSERR_NOERROR) {
				IsOk = trueblnr;
			}
		}

		if (! IsOk) {
			/*
				not recursive:
				MacMsg("waveOutWrite failed",
					"Sorry, Mini vMac encountered errors"
					" and cannot continue.", trueblnr);
			*/
			whdr[CurFillBuffer].dwFlags |= WHDR_DONE;
		}

		TheFillOffset += kOneBuffLen;
	}
}

LOCALPROC MySound_WroteABlock(void)
{
	if (wantplaying) {
		MySound_FilledBlocks();
	} else if (((TheWriteOffset - ThePlayOffset) >> kLnOneBuffLen) < 12)
	{
		/* just wait */
	} else {
		MySound_FilledBlocks();
		wantplaying = trueblnr;
		MySound_BeginPlaying();
	}
}

GLOBALOSGLUPROC MySound_EndWrite(ui4r actL)
{
	TheWriteOffset += actL;

	if (0 == (TheWriteOffset & kOneBuffMask)) {
		/* just finished a block */

		MySound_WroteABlock();
	}
}

GLOBALOSGLUFUNC tpSoundSamp MySound_BeginWrite(ui4r n, ui4r *actL)
{
	ui4b ToFillLen = kAllBuffLen - (TheWriteOffset - ThePlayOffset);
	ui4b WriteBuffContig =
		kOneBuffLen - (TheWriteOffset & kOneBuffMask);

	if (WriteBuffContig < n) {
		n = WriteBuffContig;
	}
	if (ToFillLen < n) {
		/* overwrite previous buffer */
		TheWriteOffset -= kOneBuffLen;
	}

	*actL = n;
	return TheSoundBuffer + (TheWriteOffset & kAllBuffMask);
}

LOCALPROC MySound_SecondNotify(void)
{
	if (hWaveOut != NULL) {
		if (MinFilledSoundBuffs > DesiredMinFilledSoundBuffs) {
#if dbglog_SoundStuff
			dbglog_writeln("MinFilledSoundBuffs too high");
#endif
			IncrNextTime();
		} else if (MinFilledSoundBuffs < DesiredMinFilledSoundBuffs) {
#if dbglog_SoundStuff
			dbglog_writeln("MinFilledSoundBuffs too low");
#endif
			++TrueEmulatedTime;
		}
		MinFilledSoundBuffs = kSoundBuffers;
	}
}

#endif

/* --- overall grab --- */

#if MayFullScreen
LOCALPROC GrabTheMachine(void)
{
#if EnableFSMouseMotion
	StartSaveMouseMotion();
#endif
#if EnableChangePriority
	if ((ui3b) -1 == SpeedValue) {
		RaiseMyPriority();
	}
#endif
#if EnableGrabSpecialKeys
	GrabSpecialKeys();
#endif
}
#endif

#if MayFullScreen
LOCALPROC UnGrabTheMachine(void)
{
#if EnableGrabSpecialKeys
	UnGrabSpecialKeys();
#endif
#if EnableFSMouseMotion
	StopSaveMouseMotion();
#endif
#if EnableChangePriority
	LowerMyPriority();
#endif
}
#endif

#if MayFullScreen
LOCALVAR blnr GrabMachine = falseblnr;
#endif

#if MayFullScreen
LOCALPROC AdjustMachineGrab(void)
{
	if (GrabMachine) {
		if (MainWnd != NULL) {
			GrabTheMachine();
		}
	} else {
		UnGrabTheMachine();
	}
}
#endif

/* --- basic dialogs --- */

LOCALPROC MyBeginDialog(void)
{
	DisconnectKeyCodes3();
#if MayFullScreen
	GrabMachine = falseblnr;
	UnGrabTheMachine();
#endif
	ForceShowCursor();
}

LOCALPROC MyEndDialog(void)
{
	ReconnectKeyCodes3();
}

LOCALPROC CheckSavedMacMsg(void)
{
	if (nullpr != SavedBriefMsg) {
		TCHAR briefMsg0[ClStrMaxLength + 1];
		TCHAR longMsg0[ClStrMaxLength + 1];

		NativeStrFromCStr(briefMsg0, SavedBriefMsg, falseblnr);
		NativeStrFromCStr(longMsg0, SavedLongMsg, falseblnr);

		MessageBox(MainWnd, longMsg0, briefMsg0,
			MB_APPLMODAL | MB_OK | (SavedFatalMsg ? MB_ICONSTOP : 0));

		SavedBriefMsg = nullpr;
	}
}

/* --- main window --- */

enum {
	ID_MENU_NULL = 256,
	ID_FILE_INSERTDISK1,
	ID_FILE_QUIT,
	ID_SPECIAL_MORECOMMANDS,
	ID_HELP_ABOUT,

	kNum_ID_MENU
};


#if (1 == vMacScreenDepth) || (vMacScreenDepth >= 4)
#define EnableScalingBuff 1
#else
#define EnableScalingBuff (1 && EnableMagnify && (MyWindowScale == 2))
#endif

#if EnableScalingBuff
LOCALVAR ui3p ScalingBuff = NULL;
#endif

LOCALVAR HDC MainWndDC = NULL;

LOCALVAR si5b CmdShow;

LOCALVAR TCHAR WndTitle[_MAX_PATH];
LOCALVAR const TCHAR WndClassName[] = TEXT("minivmac");

LOCALVAR blnr gBackgroundFlag = falseblnr;
LOCALVAR blnr gTrueBackgroundFlag = falseblnr;
LOCALVAR blnr CurSpeedStopped = trueblnr;

LOCALPROC GetWndTitle(void)
{
	TCHAR pathName[_MAX_PATH];
	WIN32_FIND_DATA fd;
	blnr IsOk = falseblnr;

	if (GetModuleFileName(AppInstance, pathName, _MAX_PATH) != 0) {
		HANDLE hf = FindFirstFile(pathName, &fd);

		if (hf != INVALID_HANDLE_VALUE) {
			/* get rid of extension, presumably '.exe' */
			LPTSTR p = FindLastTerm(fd.cFileName,
				(TCHAR)('.'));
			if (p != nullpr) {
				*--p = (TCHAR)('\0');
			}

			_tcscpy(WndTitle, fd.cFileName);
			IsOk = trueblnr;
			FindClose(hf);
		}
	}
	if (! IsOk) {
		_tcscpy(WndTitle, TEXT(kStrAppName));
	}
}

LOCALPROC DisposeMainWindow(void)
{
#if UseWinCE
	/* Show the taskbar */
	SHFullScreen(MainWnd, SHFS_SHOWTASKBAR);
#endif

	if (MainWndDC != NULL) {
		ReleaseDC(MainWnd, MainWndDC);
	}
	if (MainWnd != NULL) {
		DestroyWindow(MainWnd);
		MainWnd = NULL; /* so MacMsg will still work */
	}
}

enum {
	kMagStateNormal,
#if EnableMagnify
	kMagStateMagnifgy,
#endif
	kNumMagStates
};

#define kMagStateAuto kNumMagStates

#if MayNotFullScreen
LOCALVAR int CurWinIndx;
LOCALVAR blnr HavePositionWins[kNumMagStates];
LOCALVAR POINT WinPositionWins[kNumMagStates];
#endif

#if MayNotFullScreen
LOCALPROC MyAppendConvertMenuItem(HMENU hMenu,
	UINT uIDNewItem, char *s, blnr AddEllipsis)
{
	TCHAR ts[ClStrMaxLength + 1];

	NativeStrFromCStr(ts, s, AddEllipsis);

	(void) AppendMenu(hMenu, MF_ENABLED + MF_STRING,
		uIDNewItem, ts);
}
#endif

#if MayNotFullScreen
LOCALPROC MyAppendSubmenuConvertName(HMENU hMenu,
	HMENU hSubMenu, char *s)
{
	TCHAR ts[ClStrMaxLength + 1];
	MENUITEMINFO mii;

	NativeStrFromCStr(ts, s, falseblnr);

#if 0
	(void) InsertMenu(hMenu, 0xFFFFFFFF,
		MF_BYPOSITION + MF_POPUP + MF_STRING + MF_ENABLED,
		(UINT)hSubMenu, ts);
#endif

	memset(&mii, 0, sizeof(MENUITEMINFO));
	mii.cbSize = sizeof(MENUITEMINFO);
	mii.fMask = MIIM_TYPE | MIIM_SUBMENU;
	mii.fType = MFT_STRING;
	mii.hSubMenu = hSubMenu;
	mii.dwTypeData = ts;
	mii.cch = (UINT)_tcslen(ts);
	(void) InsertMenuItem(hMenu, (UINT) -1, TRUE,
		&mii);
}
#endif

#ifndef kStrMenuFile_win
#define kStrMenuFile_win kStrMenuFile
#endif

LOCALFUNC blnr ReCreateMainWindow(void)
{
#if MayNotFullScreen
	HMENU m;
	int DfltWndX;
	int DfltWndY;
	int WinIndx;
#endif
	HMENU mb;
	HWND NewMainWindow;
	HDC NewMainWndDC = NULL;
	int ScreenX = GetSystemMetrics(SM_CXSCREEN);
	int ScreenY = GetSystemMetrics(SM_CYSCREEN);
	short NewWindowHeight = vMacScreenHeight;
	short NewWindowWidth = vMacScreenWidth;
	HWND OldMainWindow = MainWnd;
	HDC OldMainWndDC = MainWndDC;
	RECT NewWinR;
	DWORD MyWStyle;
	DWORD MyWExStyle;

#if VarFullScreen
	if (! UseFullScreen)
#endif
#if MayNotFullScreen
	{
		/* save old position */
		if (OldMainWindow != NULL) {
			WinPositionWins[CurWinIndx].x = WndX;
			WinPositionWins[CurWinIndx].y = WndY;
		}
	}
#endif

#if MayNotFullScreen
#if EnableMagnify
	if (WantMagnify) {
		WinIndx = kMagStateMagnifgy;
	} else
#endif
	{
		WinIndx = kMagStateNormal;
	}
#endif

#if EnableMagnify
	if (WantMagnify) {
		NewWindowHeight *= MyWindowScale;
		NewWindowWidth *= MyWindowScale;
	}
#endif

#if VarFullScreen
	if (WantFullScreen)
#endif
#if MayFullScreen
	{
		MyWStyle = WS_VISIBLE | WS_POPUP;
		MyWExStyle = WS_EX_TOPMOST;

		hOffset = (ScreenX - NewWindowWidth) / 2;
		vOffset = (ScreenY - NewWindowHeight) / 2;
		if (hOffset < 0) {
			hOffset = 0;
		}
		if (vOffset < 0) {
			vOffset = 0;
		}

		NewWinR.left = 0;
		NewWinR.top = 0;
		NewWinR.right = ScreenX;
		NewWinR.bottom = ScreenY;
	}
#endif
#if VarFullScreen
	else
#endif
#if MayNotFullScreen
	{
		MyWStyle = WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU
			| WS_MINIMIZEBOX;
		MyWExStyle = WS_EX_ACCEPTFILES;

		DfltWndX = (ScreenX - NewWindowWidth) / 2;
		DfltWndY = (ScreenY - NewWindowHeight) / 2;

		if (DfltWndX < 0) {
			DfltWndX = 0;
		}
		if (DfltWndY < 0) {
			DfltWndY = 0;
		}

		if (! HavePositionWins[WinIndx]) {
			WinPositionWins[WinIndx].x = DfltWndX;
			WinPositionWins[WinIndx].y = DfltWndY;
			HavePositionWins[WinIndx] = trueblnr;
		}

		NewWinR.left = WinPositionWins[WinIndx].x;
		NewWinR.top = WinPositionWins[WinIndx].y;
		NewWinR.right = NewWinR.left + NewWindowWidth;
		NewWinR.bottom = NewWinR.top + NewWindowHeight;

		(void) AdjustWindowRectEx(&NewWinR, MyWStyle, TRUE, MyWExStyle);

		if ((NewWinR.right <= 0)
			|| (NewWinR.left >= ScreenX)
			|| (NewWinR.bottom <= 0)
			|| (NewWinR.top >= ScreenY))
		{
			NewWinR.left = DfltWndX;
			NewWinR.top = DfltWndY;
			NewWinR.right = DfltWndX + NewWindowWidth;
			NewWinR.bottom = DfltWndY + NewWindowHeight;

			(void) AdjustWindowRectEx(&NewWinR,
				MyWStyle, TRUE, MyWExStyle);
		}
	}
#endif

	if ((OldMainWindow == NULL)
#if VarFullScreen
		|| (WantFullScreen != UseFullScreen)
#endif
		)
	{

#if VarFullScreen
		if (WantFullScreen)
#endif
#if MayFullScreen
		{
			mb = NULL;
		}
#endif
#if VarFullScreen
		else
#endif
#if MayNotFullScreen
		{
			mb = CreateMenu();
			if (mb != NULL) {
				m = CreateMenu();
				if (m != NULL) {
					MyAppendConvertMenuItem(m, ID_FILE_INSERTDISK1,
						kStrMenuItemOpen, trueblnr);
					(void) AppendMenu(m, MF_SEPARATOR, 0, NULL);
					MyAppendConvertMenuItem(m, ID_FILE_QUIT,
						kStrMenuItemQuit, falseblnr);
					MyAppendSubmenuConvertName(mb, m, kStrMenuFile_win);
				}
				m = CreateMenu();
				if (m != NULL) {
					MyAppendConvertMenuItem(m, ID_SPECIAL_MORECOMMANDS,
						kStrMenuItemMore, trueblnr);
					MyAppendSubmenuConvertName(mb, m, kStrMenuSpecial);
				}
				m = CreateMenu();
				if (m != NULL) {
					MyAppendConvertMenuItem(m, ID_HELP_ABOUT,
						kStrMenuItemAbout, trueblnr);
					MyAppendSubmenuConvertName(mb, m, kStrMenuHelp);
				}
			}
		}
#endif

		NewMainWindow = CreateWindowEx(
			MyWExStyle,
			WndClassName,
			WndTitle,
			MyWStyle,
			NewWinR.left, NewWinR.top,
			NewWinR.right - NewWinR.left, NewWinR.bottom - NewWinR.top,
			NULL,
			mb,
			AppInstance, NULL);
		if (NewMainWindow == NULL) {
			MacMsg("CreateWindow failed",
				"Sorry, Mini vMac encountered errors"
				" and cannot continue.", trueblnr);
			return falseblnr;
		}

		NewMainWndDC = GetDC(NewMainWindow);
		if (NewMainWndDC == NULL) {
			MacMsg("GetDC failed",
				"Sorry, Mini vMac encountered errors"
				" and cannot continue.", trueblnr);
			DestroyWindow(NewMainWindow);
			return falseblnr;
		}
	} else {
		NewMainWndDC = OldMainWndDC;
		NewMainWindow = OldMainWindow;
		(void) MoveWindow(NewMainWindow, NewWinR.left, NewWinR.top,
			NewWinR.right - NewWinR.left, NewWinR.bottom - NewWinR.top,
			TRUE);
	}

#if 0 != vMacScreenDepth
	ColorModeWorks = trueblnr;
#endif

	{
		POINT p;

		/*
			Find out where the window really went, on
			the off chance that the WM_MOVE message wasn't
			called on CreateWindowEx/MoveWindow, or that
			the window wasn't put where asked for.
		*/
		p.x = 0;
		p.y = 0;
		(void) MapWindowPoints(NewMainWindow, NULL, &p, 1);
		WndX = (si4b)p.x;
		WndY = (si4b)p.y;
	}

#if MayFullScreen
	GrabMachine = falseblnr;
	UnGrabTheMachine();
#endif

#if UseWinCE && 0
	/* Show the taskbar */
	SHFullScreen(MainWnd, SHFS_SHOWTASKBAR);
#endif

#if MayNotFullScreen
	CurWinIndx = WinIndx;
#endif

	MainWnd = NewMainWindow;
	MainWndDC = NewMainWndDC;
	gTrueBackgroundFlag = falseblnr;
#if VarFullScreen
	UseFullScreen = WantFullScreen;
#endif
#if EnableMagnify
	UseMagnify = WantMagnify;
#endif

#if VarFullScreen
	if (UseFullScreen)
#endif
#if MayFullScreen
	{
		ViewHSize = ScreenX;
		ViewVSize = ScreenY;
#if EnableMagnify
		if (UseMagnify) {
			ViewHSize /= MyWindowScale;
			ViewVSize /= MyWindowScale;
		}
#endif
		if (ViewHSize >= vMacScreenWidth) {
			ViewHStart = 0;
			ViewHSize = vMacScreenWidth;
		} else {
			ViewHSize &= ~ 1;
		}
		if (ViewVSize >= vMacScreenHeight) {
			ViewVStart = 0;
			ViewVSize = vMacScreenHeight;
		} else {
			ViewVSize &= ~ 1;
		}
	}
#endif

	if (NewMainWindow != OldMainWindow) {
		ShowWindow(NewMainWindow, SW_SHOW /* CmdShow */);
		if (OldMainWndDC != NULL) {
			ReleaseDC(MainWnd, OldMainWndDC);
		}
		if (OldMainWindow != NULL) {
			/* ShowWindow(OldMainWindow, SW_HIDE); */
			DestroyWindow(OldMainWindow);
		}

		DisconnectKeyCodes3();
			/* since key events per window */
	} else {
		(void) InvalidateRgn(MainWnd, NULL, FALSE);
	}

#if UseWinCE
	/* Create and set logical palette for this window */
	{
		HPALETTE hpal;
		LOGPALETTE *lppal = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) +
			sizeof(PALETTEENTRY) * 2);

		if (! lppal)
		{
			MacMsg("CreateWindow failed",
				"Sorry, Mini vMac encountered errors"
				" and cannot continue.", trueblnr);
			return falseblnr;
		}

		lppal->palNumEntries = 2;
		lppal->palVersion = 0x0300;
		lppal->palPalEntry[0].peRed   = 255;
		lppal->palPalEntry[0].peGreen = 255;
		lppal->palPalEntry[0].peBlue  = 255;
		lppal->palPalEntry[0].peFlags = 0;
		lppal->palPalEntry[1].peRed   = 0;
		lppal->palPalEntry[1].peGreen = 0;
		lppal->palPalEntry[1].peBlue  = 0;
		lppal->palPalEntry[1].peFlags = 0;

		hpal = CreatePalette(lppal);

		if (hpal == NULL) {
			free(lppal);
			MacMsg("CreateWindow failed",
				"Sorry, Mini vMac encountered errors"
				" and cannot continue.", trueblnr);
			return falseblnr;
		}

		if (SelectPalette(MainWndDC, hpal, FALSE) == NULL) {
			free(lppal);
			MacMsg("CreateWindow failed",
				"Sorry, Mini vMac encountered errors"
				" and cannot continue.", trueblnr);
			return falseblnr;
		}

		if (RealizePalette(MainWndDC) == GDI_ERROR) {
			free(lppal);
			MacMsg("CreateWindow failed",
				"Sorry, Mini vMac encountered errors"
				" and cannot continue.", trueblnr);
			return falseblnr;
		}

		free(lppal);
	}
#endif

#if UseWinCE
	/* Hide the taskbar */
	SHFullScreen(MainWnd, SHFS_HIDETASKBAR);
	(void) MoveWindow(MainWnd, 0, 0,
			ScreenX, ScreenY, TRUE);
#endif

	if (HaveCursorHidden) {
		(void) MyMoveMouse(CurMouseH, CurMouseV);
		WantCursorHidden = trueblnr;
	}

	return trueblnr;
}

#if UseWinCE
LOCALFUNC blnr AlreadyRunningCheck(void)
{
	/*
		Adapted from example program from Microsoft eMbedded Visual C++
	*/

	/* If it is already running, then focus on the window */
	HWND hWnd = FindWindow(WndClassName, WndTitle);
	if (hWnd == NULL) {
		return falseblnr;
	} else {
		/*
			Set focus to foremost child window.
			The "| 0x01" is used to bring any owned
			windows to the foreground and activate them.
		*/
		SetForegroundWindow((HWND)((ULONG) hWnd | 0x00000001));
		return trueblnr;
	}
}
#endif

typedef struct BITMAPINFOHEADER256 {
	BITMAPINFOHEADER bmi;
#if (0 != vMacScreenDepth) && (vMacScreenDepth < 4)
	RGBQUAD colors[CLUT_size];
#else
	RGBQUAD colors[2];
#endif
} BITMAPINFOHEADER256;

#if EnableMagnify
#define MyScaledHeight (MyWindowScale * vMacScreenHeight)
#define MyScaledWidth (MyWindowScale * vMacScreenWidth)
#endif

LOCALPROC HaveChangedScreenBuff(si4b top, si4b left,
	si4b bottom, si4b right)
{
	BITMAPINFOHEADER256 bmh;
	ui3b *cdb = GetCurDrawBuff();
	int XDest;
	int YDest;

#if VarFullScreen
	if (UseFullScreen)
#endif
#if MayFullScreen
	{
		if (top < ViewVStart) {
			top = ViewVStart;
		}
		if (left < ViewHStart) {
			left = ViewHStart;
		}
		if (bottom > ViewVStart + ViewVSize) {
			bottom = ViewVStart + ViewVSize;
		}
		if (right > ViewHStart + ViewHSize) {
			right = ViewHStart + ViewHSize;
		}

		if ((top >= bottom) || (left >= right)) {
			goto label_exit;
		}
	}
#endif

	XDest = left;
	YDest = top;

#if VarFullScreen
	if (UseFullScreen)
#endif
#if MayFullScreen
	{
		XDest -= ViewHStart;
		YDest -= ViewVStart;
	}
#endif

#if EnableMagnify
	if (UseMagnify) {
		XDest *= MyWindowScale;
		YDest *= MyWindowScale;
	}
#endif
#if VarFullScreen
	if (UseFullScreen)
#endif
#if MayFullScreen
	{
		XDest += hOffset;
		YDest += vOffset;
	}
#endif

#if 0
	{ /* testing code */
		if (PatBlt(MainWndDC,
			(int)left - 1,
			(int)top - 1,
			(int)right - left + 2,
			(int)bottom - top + 2, PATCOPY)) {
		}
	}
#endif
#if 0 != vMacScreenDepth
	if (UseColorMode) {
		int i;
		int nDestWidth = (right - left);
		int nDestHeight = (bottom - top);
#if 1 == vMacScreenDepth
		ui3b *p
			= ScalingBuff + ((ui5r)vMacScreenWidth / 4) * top;
#elif vMacScreenDepth >= 4
		ui3b *p = ScalingBuff + (ui5r)vMacScreenByteWidth * top;
#else
		ui3b *p = cdb + (ui5r)vMacScreenByteWidth * top;
#endif

		memset(&bmh, 0, sizeof (bmh));
		bmh.bmi.biSize = sizeof(BITMAPINFOHEADER);
		bmh.bmi.biWidth = vMacScreenWidth;
		bmh.bmi.biHeight = - (bottom - top);
		bmh.bmi.biPlanes = 1;
#if 1 == vMacScreenDepth
		bmh.bmi.biBitCount = 4;
#else
		bmh.bmi.biBitCount = (1 << vMacScreenDepth);
#endif
		bmh.bmi.biCompression= BI_RGB;
		bmh.bmi.biSizeImage = 0;
		bmh.bmi.biXPelsPerMeter = 0;
		bmh.bmi.biYPelsPerMeter = 0;
#if 1 == vMacScreenDepth
		bmh.bmi.biClrUsed = 4;
#else
		bmh.bmi.biClrUsed = 0;
#endif
		bmh.bmi.biClrImportant = 0;

#if vMacScreenDepth < 4
		for (i = 0; i < CLUT_size; ++i) {
			bmh.colors[i].rgbRed = CLUT_reds[i] >> 8;
			bmh.colors[i].rgbGreen = CLUT_greens[i] >> 8;
			bmh.colors[i].rgbBlue = CLUT_blues[i] >> 8;
			bmh.colors[i].rgbReserved = 0;
		}
#endif

#if 1 == vMacScreenDepth
		{
			int j;
			ui3b *p1 = (ui3b *)(cdb + (ui5r)vMacScreenByteWidth * top);
			ui4b *p2 = (ui4b *)p;
			for (i = bottom - top; --i >= 0; ) {
				for (j = vMacScreenWidth / 4; --j >= 0; ) {
					ui4r t0 = *p1++;
					*p2 ++
						= ((t0 & 0xC0) >> 2)
						| ((t0 & 0x30) >> 4)
						| ((t0 & 0x0C) << 10)
						| ((t0 & 0x03) << 8);
				}
			}
		}
#elif 4 == vMacScreenDepth
		{
			int j;
			ui4b *p1 = (ui4b *)(cdb + (ui5r)vMacScreenByteWidth * top);
			ui4b *p2 = (ui4b *)p;
			for (i = bottom - top; --i >= 0; ) {
				for (j = vMacScreenWidth; --j >= 0; ) {
					ui4r t0 = *p1++;
					*p2 ++ =
						((t0 & 0xFF00) >> 8) | ((t0 & 0x00FF) << 8);
				}
			}
		}
#elif 5 == vMacScreenDepth
		{
			int j;
			ui5b *p1 = (ui5b *)(cdb + (ui5r)vMacScreenByteWidth * top);
			ui5b *p2 = (ui5b *)p;
			for (i = bottom - top; --i >= 0; ) {
				for (j = vMacScreenWidth; --j >= 0; ) {
					ui5r t0 = *p1++;
					*p2++
						= ((t0 & 0xFF000000) >> 24)
						| ((t0 & 0x00FF0000) >> 8)
						| ((t0 & 0x0000FF00) << 8)
						| ((t0 & 0x000000FF) << 24);
				}
			}
		}
#endif

#if EnableMagnify
		if (UseMagnify) {
			nDestWidth *= MyWindowScale;
			nDestHeight *= MyWindowScale;
		}
#endif

		if (StretchDIBits(
			MainWndDC, /* handle of device context */
			XDest,
				/* x-coordinate of upper-left corner of dest. rect. */
			YDest,
				/* y-coordinate of upper-left corner of dest. rect. */
			nDestWidth, /* dest. rectangle width */
			nDestHeight, /* dest. rectangle height */
			left,
				/* x-coordinate of lower-left corner of source rect. */
			0, /* y-coordinate of lower-left corner of source rect. */
			(right - left), /* source rectangle width */
			(bottom - top), /* source rectangle height */
			(CONST VOID *)p, /* address of array with DIB bits */
			(const struct tagBITMAPINFO *)&bmh,
				/* address of structure with bitmap info. */
			DIB_RGB_COLORS, /* RGB or palette indices */
			SRCCOPY
		) == 0) {
			/* ReportWinLastError(); */
		}
	} else
#endif
	{
		ui3b *p = cdb + (ui5r)vMacScreenMonoByteWidth * top;

		memset(&bmh, 0, sizeof (bmh));
		bmh.bmi.biSize = sizeof(BITMAPINFOHEADER);
		bmh.bmi.biWidth = vMacScreenWidth;
		bmh.bmi.biHeight = - (bottom - top);
		bmh.bmi.biPlanes = 1;
		bmh.bmi.biBitCount = 1;
		bmh.bmi.biCompression= BI_RGB;
		bmh.bmi.biSizeImage = 0;
		bmh.bmi.biXPelsPerMeter = 0;
		bmh.bmi.biYPelsPerMeter = 0;
		bmh.bmi.biClrUsed = 0;
		bmh.bmi.biClrImportant = 0;
#if ! UseWinCE
		bmh.colors[0].rgbRed = 255;
		bmh.colors[0].rgbGreen = 255;
		bmh.colors[0].rgbBlue = 255;
		bmh.colors[0].rgbReserved = 0;
		bmh.colors[1].rgbRed = 0;
		bmh.colors[1].rgbGreen = 0;
		bmh.colors[1].rgbBlue = 0;
		bmh.colors[1].rgbReserved = 0;
#endif

#if EnableMagnify
		if (UseMagnify) {
#if EnableScalingBuff
			if (ScalingBuff != NULL) {
				int i;
				int j;
				int k;
				ui4r left1 = left & (~ 7);
				ui4r right1 = (right + 7) & (~ 7);
				ui4r jn = (right1 - left1) / 8;
				ui3b *p1 =
					cdb + ((left1 + vMacScreenWidth * (ui5r)top) / 8);
				ui3b *p2 = ScalingBuff
					/*
						+ ((left1 + vMacScreenWidth * MyWindowScale
								* (ui5r)top)
							* MyWindowScale / 8)
					*/
					;
				ui3b *p3;
				ui3b t0;
				ui3b t1;
				ui3b t2;
				ui3b m;

				for (i = bottom - top; --i >= 0; ) {
					p3 = p2;
					for (j = jn; --j >= 0; ) {
						t0 = *p1++;
						t1 = t0;
						m = 0x80;
						t2 = 0;
						for (k = 4; --k >= 0; ) {
							t2 |= t1 & m;
							t1 >>= 1;
							m >>= 2;
						}
						*p2++ = t2 | (t2 >> 1);

						t1 = t0 << 4;
						m = 0x80;
						t2 = 0;
						for (k = 4; --k >= 0; ) {
							t2 |= t1 & m;
							t1 >>= 1;
							m >>= 2;
						}
						*p2++ = t2 | (t2 >> 1);
					}
					p1 += vMacScreenWidth / 8 - jn;
					p2 += MyScaledWidth / 8 - (MyWindowScale * jn);
					for (j = MyWindowScale * jn; --j >= 0; ) {
						*p2++ = *p3++;
					}
					p2 += MyScaledWidth / 8 - (MyWindowScale * jn);
				}

				bmh.bmi.biWidth = vMacScreenWidth * MyWindowScale;
				bmh.bmi.biHeight = - ((bottom - top) * MyWindowScale);
				if (SetDIBitsToDevice(
					MainWndDC, /* handle of device context */
					XDest,
						/*
							x-coordinate of upper-left corner
							of dest. rect.
						*/
					YDest,
						/*
							y-coordinate of upper-left corner
							of dest. rect.
						*/
					(right - left) * MyWindowScale,
						/* source rectangle width */
					(bottom - top) * MyWindowScale,
						/* source rectangle height */
					(left & 7) * MyWindowScale,
						/*
							x-coordinate of lower-left corner
							of source rect.
						*/
					0,
						/*
							y-coordinate of lower-left corner
							of source rect.
						*/
					0, /* first scan line in array */
					(bottom - top) * MyWindowScale,
						/* number of scan lines */
					(CONST VOID *)ScalingBuff,
						/* address of array with DIB bits */
					(const struct tagBITMAPINFO *)&bmh,
						/* address of structure with bitmap info. */
#if ! UseWinCE
					DIB_RGB_COLORS /* RGB or palette indices */
#else
					DIB_PAL_COLORS /* palette indices */
#endif
				) == 0) {
					/* ReportWinLastError(); */
				}
			}
#else
			if (StretchDIBits(
				MainWndDC, /* handle of device context */
				XDest,
					/*
						x-coordinate of upper-left corner of dest. rect.
					*/
				YDest,
					/*
						y-coordinate of upper-left corner of dest. rect.
					*/
				(right - left) * MyWindowScale,
					/* dest. rectangle width */
				(bottom - top) * MyWindowScale,
					/* dest. rectangle height */
				left,
					/*
						x-coordinate of lower-left corner
						of source rect.
					*/
				0,
					/*
						y-coordinate of lower-left corner
						of source rect.
					*/
				(right - left), /* source rectangle width */
				(bottom - top), /* source rectangle height */
				(CONST VOID *)p, /* address of array with DIB bits */
				(const struct tagBITMAPINFO *)&bmh,
					/* address of structure with bitmap info. */
#if ! UseWinCE
				DIB_RGB_COLORS, /* RGB or palette indices */
#else
				DIB_PAL_COLORS, /* palette indices */
#endif
				SRCCOPY
			) == 0) {
				/* ReportWinLastError(); */
			}
#endif
		} else
#endif

		{
			if (SetDIBitsToDevice(
				MainWndDC, /* handle of device context */
				XDest,
					/*
						x-coordinate of upper-left corner of dest. rect.
					*/
				YDest,
					/*
						y-coordinate of upper-left corner of dest. rect.
					*/
				(right - left), /* source rectangle width */
				(bottom - top), /* source rectangle height */
				left,
					/*
						x-coordinate of lower-left corner
						of source rect.
					*/
				0,
					/*
						y-coordinate of lower-left corner
						of source rect.
					*/
				0, /* first scan line in array */
				(bottom - top), /* number of scan lines */
				(CONST VOID *)p, /* address of array with DIB bits */
				(const struct tagBITMAPINFO *)&bmh,
					/* address of structure with bitmap info. */
#if ! UseWinCE
				DIB_RGB_COLORS /* RGB or palette indices */
#else
				DIB_PAL_COLORS /* palette indices */
#endif
			) == 0) {
				/* ReportWinLastError(); */
			}
		}
	}

#if MayFullScreen
label_exit:
	;
#endif
}

LOCALPROC Screen_DrawAll(void)
{
	HaveChangedScreenBuff(0, 0, vMacScreenHeight, vMacScreenWidth);
}

LOCALPROC MyDrawChangesAndClear(void)
{
	if (ScreenChangedBottom > ScreenChangedTop) {
		HaveChangedScreenBuff(ScreenChangedTop, ScreenChangedLeft,
			ScreenChangedBottom, ScreenChangedRight);
		ScreenClearChanges();
	}
}

GLOBALOSGLUPROC DoneWithDrawingForTick(void)
{
#if EnableFSMouseMotion
	if (HaveMouseMotion) {
		AutoScrollScreen();
	}
#endif
	MyDrawChangesAndClear();
}

LOCALFUNC blnr InitTheCursor(void)
{
	SetCursor(LoadCursor(NULL, IDC_ARROW));
	return trueblnr;
}

#if EnableFSMouseMotion
LOCALPROC MyMouseConstrain(void)
{
	si4b shiftdh;
	si4b shiftdv;

	if (SavedMouseH < ViewHStart + (ViewHSize / 4)) {
		shiftdh = ViewHSize / 2;
	} else if (SavedMouseH > ViewHStart + ViewHSize - (ViewHSize / 4))
	{
		shiftdh = - ViewHSize / 2;
	} else {
		shiftdh = 0;
	}
	if (SavedMouseV < ViewVStart + (ViewVSize / 4)) {
		shiftdv = ViewVSize / 2;
	} else if (SavedMouseV > ViewVStart + ViewVSize - (ViewVSize / 4))
	{
		shiftdv = - ViewVSize / 2;
	} else {
		shiftdv = 0;
	}
	if ((shiftdh != 0) || (shiftdv != 0)) {
		SavedMouseH += shiftdh;
		SavedMouseV += shiftdv;
		if (! MyMoveMouse(SavedMouseH, SavedMouseV)) {
			HaveMouseMotion = falseblnr;
		}
	}
}
#endif

LOCALPROC MousePositionNotify(LONG NewMousePosx, LONG NewMousePosy)
{
	blnr ShouldHaveCursorHidden = trueblnr;

#if VarFullScreen
	if (UseFullScreen)
#endif
#if MayFullScreen
	{
		NewMousePosx -= hOffset;
		NewMousePosy -= vOffset;
	}
#endif

#if EnableMagnify
	if (UseMagnify) {
		NewMousePosx /= MyWindowScale;
		NewMousePosy /= MyWindowScale;
	}
#endif

#if VarFullScreen
	if (UseFullScreen)
#endif
#if MayFullScreen
	{
		NewMousePosx += ViewHStart;
		NewMousePosy += ViewVStart;
	}
#endif

#if EnableFSMouseMotion
	if (HaveMouseMotion) {
		MyMousePositionSetDelta(NewMousePosx - SavedMouseH,
			NewMousePosy - SavedMouseV);
		SavedMouseH = NewMousePosx;
		SavedMouseV = NewMousePosy;
	} else
#endif
	{
		if (NewMousePosx < 0) {
			NewMousePosx = 0;
			ShouldHaveCursorHidden = falseblnr;
		} else if (NewMousePosx > vMacScreenWidth) {
			NewMousePosx = vMacScreenWidth - 1;
			ShouldHaveCursorHidden = falseblnr;
		}
		if (NewMousePosy < 0) {
			NewMousePosy = 0;
			ShouldHaveCursorHidden = falseblnr;
		} else if (NewMousePosy > vMacScreenHeight) {
			NewMousePosy = vMacScreenHeight - 1;
			ShouldHaveCursorHidden = falseblnr;
		}

#if VarFullScreen
		if (UseFullScreen)
#endif
#if MayFullScreen
		{
			ShouldHaveCursorHidden = trueblnr;
		}
#endif

#if ! UseWinCE
		/* if (ShouldHaveCursorHidden || CurMouseButton) */
		/*
			for a game like arkanoid, would like mouse to still
			move even when outside window in one direction
		*/
#endif
		MyMousePositionSet(NewMousePosx, NewMousePosy);
	}

	WantCursorHidden = ShouldHaveCursorHidden;
}

#if ! UseWinCE
LOCALPROC CheckMouseState(void)
{
	POINT NewMousePos;

	GetCursorPos(&NewMousePos);
	NewMousePos.x -= WndX;
	NewMousePos.y -= WndY;
	MousePositionNotify(NewMousePos.x, NewMousePos.y);
}
#endif

LOCALVAR const ui3b Native2MacRomanTab[] = {
	0xAD, 0xB0, 0xE2, 0xC4, 0xE3, 0xC9, 0xA0, 0xE0,
	0xF6, 0xE4, 0xB6, 0xDC, 0xCE, 0xB2, 0xB3, 0xB7,
	0xB8, 0xD4, 0xD5, 0xD2, 0xD3, 0xA5, 0xD0, 0xD1,
	0xF7, 0xAA, 0xC5, 0xDD, 0xCF, 0xB9, 0xC3, 0xD9,
	0xCA, 0xC1, 0xA2, 0xA3, 0xDB, 0xB4, 0xBA, 0xA4,
	0xAC, 0xA9, 0xBB, 0xC7, 0xC2, 0xBD, 0xA8, 0xF8,
	0xA1, 0xB1, 0xC6, 0xD7, 0xAB, 0xB5, 0xA6, 0xE1,
	0xFC, 0xDA, 0xBC, 0xC8, 0xDE, 0xDF, 0xF0, 0xC0,
	0xCB, 0xE7, 0xE5, 0xCC, 0x80, 0x81, 0xAE, 0x82,
	0xE9, 0x83, 0xE6, 0xE8, 0xED, 0xEA, 0xEB, 0xEC,
	0xF5, 0x84, 0xF1, 0xEE, 0xEF, 0xCD, 0x85, 0xF9,
	0xAF, 0xF4, 0xF2, 0xF3, 0x86, 0xFA, 0xFB, 0xA7,
	0x88, 0x87, 0x89, 0x8B, 0x8A, 0x8C, 0xBE, 0x8D,
	0x8F, 0x8E, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95,
	0xFD, 0x96, 0x98, 0x97, 0x99, 0x9B, 0x9A, 0xD6,
	0xBF, 0x9D, 0x9C, 0x9E, 0x9F, 0xFE, 0xFF, 0xD8
};

#if IncludePbufs
LOCALFUNC tMacErr NativeTextToMacRomanPbuf(HGLOBAL x, tPbuf *r)
{
#if MyUseUni
#define MyUnsignedChar ui4b
#else
#define MyUnsignedChar ui3b
#endif
	HGLOBAL h;
	LPTSTR p1;
	ui5b n;
	MyUnsignedChar v;
	tMacErr err = mnvm_miscErr;

	p1 = GlobalLock(x);
	if (p1 != NULL) {
		n = 0;
		while ((v = *p1++) != 0) {
			if (v != 10) {
				++n;
			}
		}
		(void) GlobalUnlock(x);

		h = GlobalAlloc(GMEM_DDESHARE, n);
		if (h != NULL) {
			p1 = GlobalLock(x);
			if (p1 != NULL) {
				ui3b *p2 = GlobalLock(h);
				if (p2 != NULL) {
					while ((v = (MyUnsignedChar)*p1++) != 0) {
						if (v >= 128) {
							*p2++ = Native2MacRomanTab[v & 0x7F];
								/*
									if MyUseUni, then for gives
									garbage for v > 256.
								*/
						} else if (v != 10) {
							*p2++ = v;
						}
					}

					err = mnvm_noErr;

					(void) GlobalUnlock(h);
				}
				(void) GlobalUnlock(x);
			}

			if (mnvm_noErr != err) {
				(void) GlobalFree(h);
			} else {
				err = PbufNewFromHandle(h, n, r);
			}
		}
	}

	return err;
}
#endif

LOCALVAR const ui3b MacRoman2NativeTab[] = {
	0xC4, 0xC5, 0xC7, 0xC9, 0xD1, 0xD6, 0xDC, 0xE1,
	0xE0, 0xE2, 0xE4, 0xE3, 0xE5, 0xE7, 0xE9, 0xE8,
	0xEA, 0xEB, 0xED, 0xEC, 0xEE, 0xEF, 0xF1, 0xF3,
	0xF2, 0xF4, 0xF6, 0xF5, 0xFA, 0xF9, 0xFB, 0xFC,
	0x86, 0xB0, 0xA2, 0xA3, 0xA7, 0x95, 0xB6, 0xDF,
	0xAE, 0xA9, 0x99, 0xB4, 0xA8, 0x80, 0xC6, 0xD8,
	0x81, 0xB1, 0x8D, 0x8E, 0xA5, 0xB5, 0x8A, 0x8F,
	0x90, 0x9D, 0xA6, 0xAA, 0xBA, 0xAD, 0xE6, 0xF8,
	0xBF, 0xA1, 0xAC, 0x9E, 0x83, 0x9A, 0xB2, 0xAB,
	0xBB, 0x85, 0xA0, 0xC0, 0xC3, 0xD5, 0x8C, 0x9C,
	0x96, 0x97, 0x93, 0x94, 0x91, 0x92, 0xF7, 0xB3,
	0xFF, 0x9F, 0xB9, 0xA4, 0x8B, 0x9B, 0xBC, 0xBD,
	0x87, 0xB7, 0x82, 0x84, 0x89, 0xC2, 0xCA, 0xC1,
	0xCB, 0xC8, 0xCD, 0xCE, 0xCF, 0xCC, 0xD3, 0xD4,
	0xBE, 0xD2, 0xDA, 0xDB, 0xD9, 0xD0, 0x88, 0x98,
	0xAF, 0xD7, 0xDD, 0xDE, 0xB8, 0xF0, 0xFD, 0xFE
};

#if IncludePbufs
LOCALFUNC blnr MacRomanTextToNativeHand(tPbuf Pbuf_no,
	blnr IsFileName, HGLOBAL *r)
{
	HGLOBAL h;
	ui5b i;
	ui5b rn = 0;
	HGLOBAL bh = PbufDat[Pbuf_no];
	ui5b L = PbufSize[Pbuf_no];
	blnr IsOk = falseblnr;

	if (IsFileName) {
		if (L > 255) {
			L = 255;
		}
	} else {
		ui3b *Buffer = (ui3b *)GlobalLock(bh);
		if (Buffer != NULL) {
			for (i = 0; i < L; ++i) {
				if (Buffer[i] == 13) {
					++rn;
				}
			}
			(void) GlobalUnlock(bh);
		}
	}

	h = GlobalAlloc(GMEM_DDESHARE, (L + rn + 1) * sizeof(TCHAR));
	if (h != NULL) {
		ui3b *Buffer = (ui3b *)GlobalLock(bh);
		if (Buffer != NULL) {
			LPTSTR p1 = GlobalLock(h);
			if (p1 != NULL) {
				for (i = 0; i < L; ++i) {
					TCHAR y;
					ui3b x = ((ui3b *)Buffer)[i];
					if (x >= 128) {
						y = (TCHAR)MacRoman2NativeTab[x - 128];
					} else {
						if (IsFileName) {
							if ((x < 32)
								|| ('\\' == x) || ('/' == x)
								|| (':' == x) || ('*' == x)
								|| ('?' == x) || ('"' == x)
								|| ('<' == x) || ('>' == x)
								|| ('|' == x))
							{
								y = (TCHAR)('-');
							} else {
								y = (TCHAR)x;
							}
						} else {
							if (13 == x) {
								*p1++ = (TCHAR)(13);
								y = (TCHAR)(10);
							} else {
								y = (TCHAR)x;
							}
						}
					}
					*p1++ = y;
				}
				*p1++ = (TCHAR) 0; /* null character */

				*r = h;
				IsOk = trueblnr;

				(void) GlobalUnlock(h);
			}
			(void) GlobalUnlock(bh);
		}
		if (! IsOk) {
			(void) GlobalFree(h);
		}
	}

	return IsOk;
}
#endif

#if IncludeHostTextClipExchange
GLOBALOSGLUFUNC tMacErr HTCEexport(tPbuf i)
{
	HGLOBAL h;
	tMacErr err = mnvm_miscErr;

	if (MacRomanTextToNativeHand(i, falseblnr, &h)) {
		if (! OpenClipboard(MainWnd)) {
			/* ReportGetLastError(); */
		} else {
			if (! EmptyClipboard()) {
				/* ReportGetLastError(); */
			}
			if (SetClipboardData(CF_TEXT, h) == NULL) {
				/* ReportGetLastError(); */
			} else {
				err = mnvm_noErr;
			}
			h = NULL;
			if (! CloseClipboard()) {
				/* ReportGetLastError(); */
			}
		}
		if (h != NULL) {
			(void) GlobalFree(h);
		}
	}

	PbufDispose(i);

	return err;
}
#endif

#if IncludeHostTextClipExchange
GLOBALOSGLUFUNC tMacErr HTCEimport(tPbuf *r)
{
	tMacErr err = mnvm_miscErr;

	if (IsClipboardFormatAvailable(CF_TEXT)) {
		if (! OpenClipboard(MainWnd)) {
			/* ReportGetLastError(); */
		} else {
			HGLOBAL h = GetClipboardData(CF_TEXT);
			if (h == NULL) {
				/* ReportGetLastError(); */
			} else {
				err = NativeTextToMacRomanPbuf(h, r);
			}
			if (! CloseClipboard()) {
				/* ReportGetLastError(); */
			}
		}
	}

	return err;
}
#endif

/* --- drives --- */

#define NotAfileRef INVALID_HANDLE_VALUE

LOCALVAR HANDLE Drives[NumDrives]; /* open disk image files */

#define NeedDriveNames (IncludeSonyGetName || IncludeSonyNew)

#if NeedDriveNames
LOCALVAR HGLOBAL DriveNames[NumDrives];
	/*
		It is supposed to be possible to use
		GetMappedFileName to get name of open file,
		but that seems ugly kludge, so instead
		save the name on open.
	*/
#endif

LOCALPROC InitDrives(void)
{
	/*
		This isn't really needed, Drives[i] and DriveNames[i]
		need not have valid values when not vSonyIsInserted[i].
	*/
	tDrive i;

	for (i = 0; i < NumDrives; ++i) {
		Drives[i] = NotAfileRef;
#if NeedDriveNames
		DriveNames[i] = NULL;
#endif
	}
}

GLOBALOSGLUFUNC tMacErr vSonyTransfer(blnr IsWrite, ui3p Buffer,
	tDrive Drive_No, ui5r Sony_Start, ui5r Sony_Count,
	ui5r *Sony_ActCount)
{
	HANDLE refnum;
	DWORD newL;
	tMacErr result;
	DWORD BytesTransferred = 0;

	refnum = Drives[Drive_No];
	newL = SetFilePointer(
		refnum, /* handle of file */
		Sony_Start, /* number of bytes to move file pointer */
		nullpr, /* address of high-order word of distance to move */
		FILE_BEGIN /* how to move */
	);
	if (newL == 0xFFFFFFFF) {
		result = mnvm_miscErr; /*& figure out what really to return &*/
	} else if (Sony_Start != (ui5b)newL) {
		/* not supposed to get here */
		result = mnvm_miscErr; /*& figure out what really to return &*/
	} else {
		if (IsWrite) {
			if (! WriteFile(refnum, /* handle of file to read */
				(LPVOID)Buffer
					, /* address of buffer that receives data */
				(DWORD)Sony_Count, /* number of bytes to read */
				&BytesTransferred, /* address of number of bytes read */
				nullpr)) /* address of structure for data */
			{
				result = mnvm_miscErr;
					/*& figure out what really to return &*/
			} else if ((ui5b)BytesTransferred != Sony_Count) {
				result = mnvm_miscErr;
					/*& figure out what really to return &*/
			} else {
				result = mnvm_noErr;
			}
		} else {
			if (! ReadFile(refnum, /* handle of file to read */
				(LPVOID)Buffer,
					/* address of buffer that receives data */
				(DWORD)Sony_Count, /* number of bytes to read */
				&BytesTransferred,
					/* address of number of bytes read */
				nullpr)) /* address of structure for data */
			{
				result = mnvm_miscErr;
					/*& figure out what really to return &*/
			} else if ((ui5b)BytesTransferred != Sony_Count) {
				result = mnvm_miscErr;
					/*& figure out what really to return &*/
			} else {
				result = mnvm_noErr;
			}
		}
	}

	if (nullpr != Sony_ActCount) {
		*Sony_ActCount = BytesTransferred;
	}

	return result;
}

GLOBALOSGLUFUNC tMacErr vSonyGetSize(tDrive Drive_No, ui5r *Sony_Count)
{
	tMacErr result;
	DWORD L;

	L = GetFileSize(Drives[Drive_No], nullpr);
	if (L == 0xFFFFFFFF) {
		result = mnvm_miscErr; /*& figure out what really to return &*/
	} else {
		*Sony_Count = L;
		result = mnvm_noErr;
	}

	return result;
}

LOCALFUNC tMacErr vSonyEject0(tDrive Drive_No, blnr deleteit)
{
	HANDLE refnum = Drives[Drive_No];

#if ! NeedDriveNames
	UnusedParam(deleteit);
#endif

	Drives[Drive_No] = NotAfileRef; /* not really needed */

	DiskEjectedNotify(Drive_No);

	(void) FlushFileBuffers(refnum);
	(void) CloseHandle(refnum);

#if NeedDriveNames
	{
		HGLOBAL h = DriveNames[Drive_No];
		if (NULL != h) {
			if (deleteit) {
				LPTSTR drivepath = GlobalLock(h);
				if (drivepath != NULL) {
					(void) DeleteFile(drivepath);
					(void) GlobalUnlock(h);
				}
			}
			(void) GlobalFree(h);
			DriveNames[Drive_No] = NULL; /* not really needed */
		}
	}
#endif

	return mnvm_noErr;
}

GLOBALOSGLUFUNC tMacErr vSonyEject(tDrive Drive_No)
{
	return vSonyEject0(Drive_No, falseblnr);
}

#if IncludeSonyNew
GLOBALOSGLUFUNC tMacErr vSonyEjectDelete(tDrive Drive_No)
{
	return vSonyEject0(Drive_No, trueblnr);
}
#endif

LOCALPROC UnInitDrives(void)
{
	tDrive i;

	for (i = 0; i < NumDrives; ++i) {
		if (vSonyIsInserted(i)) {
			(void) vSonyEject(i);
		}
	}
}

#if NeedDriveNames
LOCALFUNC blnr LPTSTRtoHand(LPTSTR s, HGLOBAL *r)
{
	blnr IsOk = falseblnr;

	size_t L = _tcslen(s);
	HGLOBAL h = GlobalAlloc(GMEM_DDESHARE,
		(L + 1) * sizeof(TCHAR));
	if (h != NULL) {
		LPTSTR p = GlobalLock(h);
		if (p != NULL) {
			_tcscpy(p, s);
			IsOk = trueblnr;
			(void) GlobalUnlock(h);
		}
		if (! IsOk) {
			(void) GlobalFree(h);
		} else {
			*r = h;
		}
	}

	return IsOk;
}
#endif

#if IncludeSonyGetName
GLOBALOSGLUFUNC tMacErr vSonyGetName(tDrive Drive_No, tPbuf *r)
{
	WIN32_FIND_DATA fd;
	tMacErr err = mnvm_miscErr;
	HGLOBAL ph = DriveNames[Drive_No];
	if (NULL != ph) {
		LPTSTR drivepath = GlobalLock(ph);
		if (drivepath != NULL) {
			HANDLE hf = FindFirstFile(drivepath, &fd);
			(void) GlobalUnlock(ph);

			if (hf != INVALID_HANDLE_VALUE) {
				HGLOBAL h;
				if (LPTSTRtoHand(fd.cFileName, &h)) {
					err = NativeTextToMacRomanPbuf(h, r);
				}
				FindClose(hf);
			}
		}
	}

	return err;
}
#endif

LOCALFUNC blnr Sony_Insert0(HANDLE refnum, blnr locked,
	LPTSTR drivepath)
{
	tDrive Drive_No;

#if ! NeedDriveNames
	UnusedParam(drivepath);
#endif

	if (! FirstFreeDisk(&Drive_No)) {
		(void) CloseHandle(refnum);
		MacMsg(kStrTooManyImagesTitle,
			kStrTooManyImagesMessage, falseblnr);
		return falseblnr;
	} else {
		Drives[Drive_No] = refnum;
		DiskInsertNotify(Drive_No, locked);
#if NeedDriveNames
		{
			HGLOBAL h;

			if (! LPTSTRtoHand(drivepath, &h)) {
				h = NULL;
			}

			DriveNames[Drive_No] = h;
		}
#endif
		return trueblnr;
	}
}

LOCALFUNC blnr Sony_Insert1(LPTSTR drivepath, blnr SilentOnMissing)
{
	blnr locked = falseblnr;
	HANDLE refnum = CreateFile(
		drivepath, /* pointer to name of the file */
		GENERIC_READ + GENERIC_WRITE, /* access (read-write) mode */
		0, /* share mode */
		nullpr, /* pointer to security descriptor */
		OPEN_EXISTING, /* how to create */
		FILE_ATTRIBUTE_NORMAL, /* file attributes */
		nullpr /* handle to file with attributes to copy */
	);
	if (refnum == INVALID_HANDLE_VALUE) {
		if (ERROR_ACCESS_DENIED == GetLastError()) {
			locked = trueblnr;
			refnum = CreateFile(
				drivepath, /* pointer to name of the file */
				GENERIC_READ, /* access (read-write) mode */
				FILE_SHARE_READ, /* share mode */
				nullpr, /* pointer to security descriptor */
				OPEN_EXISTING, /* how to create */
				FILE_ATTRIBUTE_NORMAL, /* file attributes */
				nullpr /* handle to file with attributes to copy */
			);
		}
	}
	if (refnum == INVALID_HANDLE_VALUE) {
		DWORD err = GetLastError();
		if (ERROR_SHARING_VIOLATION == err) {
			MacMsg(kStrImageInUseTitle,
				kStrImageInUseMessage, falseblnr);
		} else if ((ERROR_FILE_NOT_FOUND == err) && SilentOnMissing) {
			/* ignore it */
		} else {
			MacMsg(kStrOpenFailTitle, kStrOpenFailMessage, falseblnr);
		}
	} else {
		return Sony_Insert0(refnum, locked, drivepath);
	}
	return falseblnr;
}

LOCALFUNC blnr LoadMacRomFromPath(LPTSTR drivepath)
{
	HANDLE refnum = INVALID_HANDLE_VALUE;
	blnr IsOk = falseblnr;

	refnum = CreateFile(
		drivepath, /* pointer to name of the file */
		GENERIC_READ, /* access (read-write) mode */
		FILE_SHARE_READ, /* share mode */
		nullpr, /* pointer to security descriptor */
		OPEN_EXISTING, /* how to create */
		FILE_ATTRIBUTE_NORMAL, /* file attributes */
		nullpr /* handle to file with attributes to copy */
	);

	if (refnum == INVALID_HANDLE_VALUE) {
		/* MacMsg(kStrNoROMTitle, kStrNoROMMessage, trueblnr); */
	} else {
		DWORD BytesRead;

		if (! ReadFile(refnum, /* handle of file to read */
			(LPVOID)ROM, /* address of buffer that receives data */
			(DWORD)kROM_Size, /* number of bytes to read */
			&BytesRead, /* address of number of bytes read */
			nullpr)) /* address of structure for data */
		{
			MacMsgOverride(kStrNoReadROMTitle, kStrNoReadROMMessage);
		} else
		if ((ui5b)BytesRead != kROM_Size) {
			MacMsgOverride(kStrShortROMTitle, kStrShortROMMessage);
		} else
		{
			IsOk = (mnvm_noErr == ROM_IsValid());
		}
		(void) CloseHandle(refnum);
	}

	return IsOk;
}

#ifndef EnableShellLinks
#define EnableShellLinks 1
#endif

#if EnableShellLinks
LOCALVAR blnr COMinited = falseblnr;
LOCALVAR blnr COMinitedOK;
#endif

#if EnableShellLinks
LOCALPROC MyUninitCOM(void)
{
	if (COMinited) {
		CoUninitialize();
	}
}
#endif

#if EnableShellLinks
LOCALFUNC blnr MyNeedCOM(void)
{
	HRESULT hres;

	if (! COMinited) {
		COMinitedOK = falseblnr;
		hres = CoInitialize(NULL);
		if (SUCCEEDED(hres)) {
			COMinitedOK = trueblnr;
		}

		COMinited = trueblnr;
	}
	return COMinitedOK;
}
#endif

#if EnableShellLinks
LOCALFUNC blnr MyResolveShortcut(LPTSTR FilePath, blnr *directory)
/* adapted from Microsoft example code */
{
	HRESULT hres;
	IShellLink *psl;
	IPersistFile* ppf;
	TCHAR szGotPath[MAX_PATH];
	WIN32_FIND_DATA wfd;
	blnr IsOk = falseblnr;

	if (MyNeedCOM()) {
		hres = CoCreateInstance(&CLSID_ShellLink, NULL,
			CLSCTX_INPROC_SERVER, &IID_IShellLink,
			(LPVOID *)(void *)&psl);
			/*
				the (void *) prevents a compiler warning
				from gcc
			*/
		if (SUCCEEDED(hres)) {
			/* Get a pointer to the IPersistFile interface. */
			hres = psl->lpVtbl->QueryInterface(psl, &IID_IPersistFile,
				(void **)(void *)&ppf);
			if (SUCCEEDED(hres)) {
				/* Ensure that the string is Unicode. */
#if MyUseUni
#define wsz FilePath
#else
				WORD wsz[MAX_PATH];
				MultiByteToWideChar(CP_ACP, 0, FilePath, -1, wsz,
					MAX_PATH);
#endif

				/* Load the shortcut. */
				hres = ppf->lpVtbl->Load(ppf, wsz, STGM_READ);
				if (SUCCEEDED(hres)) {
					/* Resolve the link. */
					hres = psl->lpVtbl->Resolve(psl, MainWnd,
						SLR_ANY_MATCH);
					if (SUCCEEDED(hres)) {
						/* Get the path to the link target. */
						hres = psl->lpVtbl->GetPath(psl, szGotPath,
							MAX_PATH, &wfd,
							SLGP_SHORTPATH);
						if (SUCCEEDED(hres)) {
							/*
								This is in the sample code, but doesn't
								seem to be needed:
								Get the description of the target.
								char szDescription[MAX_PATH];
								hres = psl->lpVtbl->GetDescription(psl,
									szDescription, MAX_PATH);
								if (SUCCEEDED(hres)) {
								}
							*/
							lstrcpy(FilePath, szGotPath);
							if (NULL != directory) {
								*directory = (0 != (wfd.dwFileAttributes
									& FILE_ATTRIBUTE_DIRECTORY));
							}
							IsOk = trueblnr;
						}
					}
				}

				ppf->lpVtbl->Release(ppf);
			}
			psl->lpVtbl->Release(psl);
		}
	}
	return IsOk;
}
#endif

#if EnableShellLinks
LOCALFUNC blnr FileIsLink(LPTSTR drivepath)
{
	LPTSTR p = FindLastTerm(drivepath, (TCHAR)('.'));

	if (p != nullpr) {
		if (_tcscmp(p, TEXT("lnk")) == 0) {
			return trueblnr;
		}
	}
	return falseblnr;
}
#endif

LOCALFUNC blnr InsertDiskOrAlias(LPTSTR drivepath,
	blnr MaybeROM, blnr MaybeAlias)
{
#if EnableShellLinks
	if (MaybeAlias && FileIsLink(drivepath)) {
		if (! MyResolveShortcut(drivepath, NULL)) {
			return falseblnr;
		}
	}
#endif

	if (MaybeROM && ! ROM_loaded) {
		return LoadMacRomFromPath(drivepath);
	} else {
		return Sony_Insert1(drivepath, falseblnr);
	}
}

LOCALFUNC blnr MyFileExists(LPTSTR pathName, blnr *directory)
{
	WIN32_FIND_DATA fd;
	HANDLE hf = FindFirstFile(pathName, &fd);
	blnr IsOk = falseblnr;

	if (hf != INVALID_HANDLE_VALUE) {
		if (NULL != directory) {
			*directory =
				(0 != (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY));
		}
		IsOk = trueblnr;
		FindClose(hf);
	}

	return IsOk;
}

LOCALFUNC tMacErr ResolveNamedChild0(LPTSTR pathName,
	LPTSTR Child, blnr *directory)
{
	size_t newlen;
	size_t oldlen = _tcslen(pathName);
	tMacErr err = mnvm_miscErr;

	newlen = oldlen + 1 + _tcslen(Child);
	if (newlen + 1 < _MAX_PATH) {
		_tcscat(pathName, TEXT("\\"));
		_tcscat(pathName, Child);

		if (MyFileExists(pathName, directory)) {
			err = mnvm_noErr;
		} else {
			err = mnvm_fnfErr;
#if EnableShellLinks
			if (newlen + 5 < _MAX_PATH) {
				_tcscat(pathName, TEXT(".lnk"));
				if (MyFileExists(pathName, NULL))
				if (MyResolveShortcut(pathName, directory))
				{
					err = mnvm_noErr;
				}
				if (mnvm_noErr != err) {
					pathName[newlen] = (TCHAR)('\0');
				}
			}
#endif
		}
	}

	return err;
}

LOCALFUNC tMacErr ResolveNamedChild(LPTSTR pathName,
	char *Child, blnr *directory)
{
	TCHAR Child0[ClStrMaxLength + 1];

	NativeStrFromCStr(Child0, Child, falseblnr);

	return ResolveNamedChild0(pathName, Child0, directory);
}

LOCALFUNC blnr ResolveNamedChildDir(LPTSTR pathName, char *Child)
{
	blnr directory;

	return (mnvm_noErr == ResolveNamedChild(
		pathName, Child, &directory))
		&& directory;
}

LOCALFUNC blnr ResolveNamedChildFile(LPTSTR pathName, char *Child)
{
	blnr directory;

	return (mnvm_noErr == ResolveNamedChild(
		pathName, Child, &directory))
		&& ! directory;
}

#if UseActvFile || (IncludeSonyNew && ! SaveDialogEnable)
LOCALFUNC blnr MakeNamedChildDir(LPTSTR pathName, char *Child)
{
	blnr directory;
	blnr IsOk = falseblnr;
	tMacErr err = ResolveNamedChild(pathName, Child, &directory);

	if (mnvm_noErr == err) {
		IsOk = directory;
	} else if (mnvm_fnfErr == err) {
		if (CreateDirectory(pathName, NULL)) {
			IsOk = trueblnr;
		}
	}

	return IsOk;
}
#endif

LOCALFUNC blnr MyGetAppDataPath(LPTSTR lpszPath,
	BOOL fCreate)
{
	blnr IsOk = falseblnr;

	if (HaveMySHGetSpecialFolderPath())
	if (MySHGetSpecialFolderPath(
		NULL /* HWND hwndOwner */,
		lpszPath, My_CSIDL_APPDATA, fCreate))
	{
		IsOk = trueblnr;
	}
	/*
		if not available, could perhaps
		use GetWindowsDirectory.
	*/
	/*
		SHGetFolderPath is more recent,
		could perhaps check for it first.
		might also be in "SHFolder.dll".

		SHGetKnownFolderPath is even
		more recent.
	*/

	return IsOk;
}

#if UseWinCE
/* Are we in control mode? */
/* Needed because you can't hold down a key with the virtual keyboard */
LOCALVAR blnr CtrlMode = falseblnr;
#endif

LOCALPROC InsertADisk0(void)
{
	OPENFILENAME ofn;
	TCHAR szDirName[256];
	TCHAR szFile[256];
#if ! UseWinCE
	TCHAR szFileTitle[256];
#endif
	UINT i;
	size_t cbString;
	TCHAR chReplace;
	TCHAR szFilter[256];
	blnr IsOk;

	szDirName[0] = (TCHAR)('\0');
	szFile[0] = (TCHAR)('\0');
	_tcscpy(szFilter,
		TEXT("Disk images|*.dsk;*.HF?;*.IMG;*.IMA;*.IMAGE")
		TEXT("|All files (*.*)|*.*|\0"));

	cbString = _tcslen(szFilter);

	chReplace = szFilter[cbString - 1];

	for (i = 0; szFilter[i] != (TCHAR)('\0'); ++i)
	{
		if (szFilter[i] == chReplace) {
			szFilter[i] = (TCHAR)('\0');
		}
	}

	memset(&ofn, 0, sizeof(OPENFILENAME));

	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner = MainWnd;
	ofn.lpstrFilter = szFilter;
	ofn.nFilterIndex = 2;
	ofn.lpstrFile= szFile;
	ofn.nMaxFile = sizeof(szFile);
#if ! UseWinCE
	ofn.lpstrFileTitle = szFileTitle;
	ofn.nMaxFileTitle = sizeof(szFileTitle);
#endif
	ofn.lpstrInitialDir = szDirName;
	ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST
		| OFN_HIDEREADONLY;

	MyBeginDialog();
	IsOk = GetOpenFileName(&ofn);
	MyEndDialog();

	if(! IsOk) {
		/* report error */
#if UseWinCE
		if (szFile[0]) {
			char wMsg[1024];
			sprintf(wMsg, "Couldn't open %ls", szFile);
			MacMsg("error", wMsg, falseblnr);
		}
#endif
	} else {
		(void) InsertDiskOrAlias(ofn.lpstrFile,
			trueblnr, falseblnr);
	}

#if UseWinCE
	CtrlMode = falseblnr;
#endif
}

LOCALFUNC blnr LoadInitialImageFromName(char *ImageName)
{
	TCHAR ImageFile[_MAX_PATH];

	if (GetAppDir(ImageFile))
	if (ResolveNamedChildFile(ImageFile, ImageName))
	if (Sony_Insert1(ImageFile, trueblnr))
	{
		return trueblnr;
	}
	return falseblnr;
}

LOCALFUNC blnr Sony_InsertIth(int i)
{
	blnr v;

	if ((i > 9) || ! FirstFreeDisk(nullpr)) {
		v = falseblnr;
	} else {
		char s[] = "disk?.dsk";

		s[4] = '0' + i;

		/* stop on first error (including file not found) */
		v = LoadInitialImageFromName(s);
	}

	return v;
}

LOCALFUNC blnr LoadInitialImages(void)
{
	if (! AnyDiskInserted()) {
		int i;

		for (i = 1; Sony_InsertIth(i); ++i) {
			/* stop on first error (including file not found) */
		}
	}

	return trueblnr;
}

#if UseActvFile

#define ActvCodeFileName "act_1"

LOCALFUNC tMacErr ActvCodeFileLoad(ui3p p)
{
	TCHAR pathName[_MAX_PATH];
	DWORD BytesRead;
	HANDLE refnum = INVALID_HANDLE_VALUE;
	blnr IsOk = falseblnr;

	if (MyGetAppDataPath(pathName, FALSE))
	if (ResolveNamedChildDir(pathName, "Gryphel"))
	if (ResolveNamedChildDir(pathName, "mnvm_act"))
	if (ResolveNamedChildFile(pathName, ActvCodeFileName))
	{
		refnum = CreateFile(
			pathName, /* pointer to name of the file */
			GENERIC_READ, /* access (read-write) mode */
			FILE_SHARE_READ, /* share mode */
			NULL, /* pointer to security descriptor */
			OPEN_EXISTING, /* how to create */
			FILE_ATTRIBUTE_NORMAL, /* file attributes */
			NULL /* handle to file with attributes to copy */
		);
		if (INVALID_HANDLE_VALUE == refnum) {
			/* report error */
		} else {
			if (SetFilePointer(
				refnum, /* handle of file */
				0, /* number of bytes to move file pointer */
				nullpr,
					/* address of high-order word of distance to move */
				FILE_BEGIN /* how to move */
				) != 0)
			{
				/* report error */
			} else if (! ReadFile(refnum, /* handle of file to read */
				(LPVOID)p, /* address of buffer that receives data */
				(DWORD)ActvCodeFileLen, /* number of bytes to read */
				&BytesRead, /* address of number of bytes read */
				nullpr) /* address of structure for data */
				|| ((ui5b)BytesRead != ActvCodeFileLen))
			{
				/* report error */
			} else {
				IsOk = trueblnr;
			}
			(void) CloseHandle(refnum);
		}
	}

	return IsOk ? mnvm_noErr : mnvm_miscErr;
}

LOCALFUNC blnr NewNamedChildFile(LPTSTR pathName, char *Child)
{
	blnr directory;
	blnr IsOk = falseblnr;
	tMacErr err = ResolveNamedChild(pathName, Child, &directory);

	if (mnvm_noErr == err) {
		IsOk = ! directory;
	} else if (mnvm_fnfErr == err) {
		IsOk = trueblnr;
	}

	return IsOk;
}

LOCALFUNC tMacErr ActvCodeFileSave(ui3p p)
{
	TCHAR pathName[_MAX_PATH];
	DWORD BytesWritten;
	HANDLE refnum = INVALID_HANDLE_VALUE;
	blnr IsOk = falseblnr;

	if (MyGetAppDataPath(pathName, TRUE))
	if (MakeNamedChildDir(pathName, "Gryphel"))
	if (MakeNamedChildDir(pathName, "mnvm_act"))
	if (NewNamedChildFile(pathName, ActvCodeFileName))
	{
		refnum = CreateFile(
			pathName, /* pointer to name of the file */
			GENERIC_READ + GENERIC_WRITE, /* access (read-write) mode */
			0, /* share mode */
			NULL, /* pointer to security descriptor */
			CREATE_ALWAYS, /* how to create */
			FILE_ATTRIBUTE_NORMAL, /* file attributes */
			NULL /* handle to file with attributes to copy */
		);
		if (INVALID_HANDLE_VALUE == refnum) {
			/* report error */
		} else {
			if (SetFilePointer(
				refnum, /* handle of file */
				0, /* number of bytes to move file pointer */
				nullpr,
					/* address of high-order word of distance to move */
				FILE_BEGIN /* how to move */
				) != 0)
			{
				/* report error */
			} else if (! WriteFile(refnum, /* handle of file to read */
				(LPVOID)p, /* address of buffer that receives data */
				(DWORD)ActvCodeFileLen, /* number of bytes to read */
				&BytesWritten, /* address of number of bytes read */
				nullpr) /* address of structure for data */
				|| ((ui5b)BytesWritten != ActvCodeFileLen))
			{
				/* report error */
			} else {
				IsOk = trueblnr;
			}
			(void) CloseHandle(refnum);
			if (! IsOk) {
				(void) DeleteFile(pathName);
			}
		}
	}

	return IsOk ? mnvm_noErr : mnvm_miscErr;
}

#endif /* UseActvFile */

#if IncludeSonyNew
LOCALFUNC blnr WriteZero(HANDLE refnum, ui5b L)
{
	if (SetFilePointer(
		refnum, /* handle of file */
		0, /* number of bytes to move file pointer */
		nullpr, /* address of high-order word of distance to move */
		FILE_BEGIN /* how to move */
		) != 0)
	{
		return falseblnr;
	} else {
#define ZeroBufferSize 2048
		ui5b i;
		ui3b buffer[ZeroBufferSize];
		DWORD BytesWritten;

		memset(&buffer, 0, ZeroBufferSize);

		while (L > 0) {
			i = (L > ZeroBufferSize) ? ZeroBufferSize : L;
			if (! WriteFile(refnum, /* handle of file to read */
				(LPVOID)buffer,
					/* address of buffer that receives data */
				(DWORD)i, /* number of bytes to read */
				&BytesWritten, /* address of number of bytes read */
				nullpr) /* address of structure for data */
				|| ((ui5b)BytesWritten != i))
			{
				return falseblnr;
			}
			L -= i;
		}
		return trueblnr;
	}
}
#endif

#define MaxSavePathSize MAX_PATH

#if IncludeSonyNew
LOCALPROC MakeNewDisk0(ui5b L, LPTSTR pathName)
{
	blnr IsOk = falseblnr;
	HANDLE newrefNum;

	IsOk = falseblnr;
	newrefNum = CreateFile(
		pathName, /* pointer to name of the file */
		GENERIC_READ + GENERIC_WRITE, /* access (read-write) mode */
		0, /* share mode */
		NULL, /* pointer to security descriptor */
		CREATE_ALWAYS, /* how to create */
		FILE_ATTRIBUTE_NORMAL, /* file attributes */
		NULL /* handle to file with attributes to copy */
	);
	if (newrefNum == INVALID_HANDLE_VALUE) {
		/* report error */
	} else {
		if (SetFilePointer(
			newrefNum, /* handle of file */
			L, /* number of bytes to move file pointer */
			nullpr,
				/* address of high-order word of distance to move */
			FILE_BEGIN /* how to move */
			) != L)
		{
			/* report error */
		} else if (! SetEndOfFile(newrefNum)) {
			/* report error */
		} else if (! WriteZero(newrefNum, L)) {
			/* report error */
		} else {
			IsOk =
				Sony_Insert0(newrefNum, falseblnr, pathName);
			newrefNum = INVALID_HANDLE_VALUE;
		}
		if (INVALID_HANDLE_VALUE != newrefNum) {
			(void) CloseHandle(newrefNum);
		}
		if (! IsOk) {
			(void) DeleteFile(pathName);
		}
	}
}
#endif

#if IncludeSonyNew
LOCALPROC MakeNewDisk(ui5b L, HGLOBAL NewDiskNameDat)
{
#if SaveDialogEnable
	OPENFILENAME ofn;
	blnr IsOk = falseblnr;
	TCHAR szFile[MaxSavePathSize];
	TCHAR szFileTitle[MaxSavePathSize];

	memset(&ofn, 0, sizeof(OPENFILENAME));
	szFile[0] = 0;
	szFileTitle[0] = 0;

#if IncludeSonyGetName
	if (NewDiskNameDat != NULL) {
		LPTSTR p = GlobalLock(NewDiskNameDat);
		if (p != NULL) {
			_tcscpy(szFile, p);
			(void) GlobalUnlock(NewDiskNameDat);
		}
	} else
#endif
	{
		NativeStrFromCStr(szFile, "untitled", falseblnr);
	}

	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.lpstrFile = szFile;
	ofn.hwndOwner = MainWnd;
	/* ofn.lpstrFilter = "All\0*.*\0Text\0*.txt\0Datafile\0*.dsk\0"; */
	/* ofn.lpstrFilter = NULL; */ /* szFilter */
	ofn.nMaxFile = MaxSavePathSize;
	ofn.lpstrFileTitle = szFileTitle;
	ofn.nMaxFileTitle = MaxSavePathSize;
	ofn.lpstrInitialDir = NULL;
	ofn.Flags = OFN_OVERWRITEPROMPT + OFN_HIDEREADONLY;
		/* + OFN_SHOWHELP */

	MyBeginDialog();
	IsOk = GetSaveFileName(&ofn);
	MyEndDialog();

	if (! IsOk) {
		/* report error */
	} else {
		MakeNewDisk0(L, ofn.lpstrFile);
	}
#else /* SaveDialogEnable */
	TCHAR pathName[MaxSavePathSize];

	if (GetAppDir(pathName))
	if (MakeNamedChildDir(pathName, "out"))
	{
		blnr directory;
		LPTSTR p = GlobalLock(NewDiskNameDat);

		if (p != NULL) {
			tMacErr err = ResolveNamedChild0(pathName, p,
				&directory);

			if (mnvm_fnfErr == err) {
				err = mnvm_noErr;
			} else if (mnvm_noErr == err) {
				if (directory) {
					err = mnvm_miscErr;
				}
			}

			if (mnvm_noErr == err) {
				MakeNewDisk0(L, pathName);
			}

			(void) GlobalUnlock(NewDiskNameDat);
		}
	}
#endif /* SaveDialogEnable */
}
#endif

LOCALFUNC blnr LoadMacRom(void)
{
	TCHAR ROMFile[_MAX_PATH];
	blnr IsOk = falseblnr;

	if (GetAppDir(ROMFile))
	if (ResolveNamedChildFile(ROMFile, RomFileName))
	{
		IsOk = trueblnr;
	}

	if (! IsOk) {
		if (MyGetAppDataPath(ROMFile, FALSE))
		if (ResolveNamedChildDir(ROMFile, "Gryphel"))
		if (ResolveNamedChildDir(ROMFile, "mnvm_rom"))
		if (ResolveNamedChildFile(ROMFile, RomFileName))
		{
			IsOk = trueblnr;
		}

	}

	if (IsOk) {
		IsOk = LoadMacRomFromPath(ROMFile);
	}

	return trueblnr;
}

#if InstallFileIcons
LOCALPROC MySetRegKey(HKEY hKeyRoot,
	LPTSTR strRegKey, LPTSTR strRegValue)
{
	HKEY RegKey;
	DWORD dwDisposition;

	if (ERROR_SUCCESS == RegCreateKeyEx(hKeyRoot, strRegKey, 0, NULL,
		REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
		NULL, &RegKey, &dwDisposition))
	{
		RegSetValueEx(RegKey, NULL, 0, REG_SZ,
			(CONST BYTE *)strRegValue,
			(DWORD)((_tcslen(strRegValue) + 1) * sizeof(TCHAR)));
		RegCloseKey(RegKey);
	}
}

LOCALPROC RegisterShellFileType(LPTSTR AppPath, LPTSTR strFilterExt,
	LPTSTR strFileTypeId, LPTSTR strFileTypeName,
	LPTSTR strIconId, blnr CanOpen)
{
	TCHAR strRegKey[_MAX_PATH];
	TCHAR strRegValue[_MAX_PATH + 2];
		/* extra room for ","{strIconId} */

	MySetRegKey(HKEY_CLASSES_ROOT, strFileTypeId, strFileTypeName);
	MySetRegKey(HKEY_CLASSES_ROOT, strFilterExt, strFileTypeId);

	_tcscpy(strRegKey, strFileTypeId);
	_tcscat(strRegKey, TEXT("\\DefaultIcon"));
	_tcscpy(strRegValue, TEXT("\""));
	_tcscat(strRegValue, AppPath);
	_tcscat(strRegValue, TEXT("\","));
	_tcscat(strRegValue, strIconId);
	MySetRegKey(HKEY_CLASSES_ROOT, strRegKey, strRegValue);

	if (CanOpen) {
		_tcscpy(strRegKey, strFileTypeId);
		_tcscat(strRegKey, TEXT("\\shell\\open\\command"));
		_tcscpy(strRegValue, TEXT("\""));
		_tcscat(strRegValue, AppPath);
		_tcscat(strRegValue, TEXT("\" \"%1\""));
		MySetRegKey(HKEY_CLASSES_ROOT, strRegKey, strRegValue);
	}
}

LOCALFUNC blnr RegisterInRegistry(void)
{
	TCHAR AppPath[_MAX_PATH];

	GetModuleFileName(NULL, AppPath, _MAX_PATH);
#if 0
	GetShortPathName(AppPath, AppPath, _MAX_PATH);
#endif

	RegisterShellFileType(AppPath, TEXT(".rom"), TEXT("minivmac.rom"),
		TEXT("Mini vMac ROM Image"), TEXT("1"), falseblnr);
	RegisterShellFileType(AppPath, TEXT(".dsk"), TEXT("minivmac.dsk"),
		TEXT("Mini vMac Disk Image"), TEXT("2"), trueblnr);

	return trueblnr;
}
#endif

LOCALVAR LPTSTR CommandLine;

LOCALFUNC blnr ScanCommandLine(void)
{
	TCHAR *p = CommandLine;
	TCHAR *p1;
	TCHAR *p2;
	TCHAR v;
	size_t L;

	v = *p;
	while (0 != v) {
		if (' ' == v) {
			v = *++p;
		} else {
			if ('\"' == v) {
				v = *++p;
				p1 = p;
				while (('\"' != v) && (0 != v)) {
					v = *++p;
				}
				p2 = p;
				if ('\"' == v) {
					v = *++p;
				}
			} else {
				p1 = p;
				while ((' ' != v) && (0 != v)) {
					v = *++p;
				}
				p2 = p;
			}
			L = p2 - p1;
			if (L + 1 <= _MAX_PATH) {
				TCHAR fileName[_MAX_PATH];
				TCHAR *filePtr = fileName;
				size_t i = L;

				while (i > 0) {
					*filePtr++ = *p1++;
					--i;
				}
				*filePtr = (char)0;

				if ((L > 0)
					&& (('/' == fileName[0]) || ('-' == fileName[0])))
				{
#if 0
					TCHAR *p3 = &fileName[1];
					if (0 == _tcscmp(p3, TEXT("l"))) {
						SpeedValue = 0;
					} else
#endif
					{
						MacMsg(kStrBadArgTitle, kStrBadArgMessage,
							falseblnr);
					}
				} else {
					(void) InsertDiskOrAlias(fileName,
						falseblnr, trueblnr);
				}
			}
		}
	}

	return trueblnr;
}

#if EnableRecreateW
LOCALPROC CheckMagnifyAndFullScreen(void)
{
	if (
#if EnableMagnify
		(UseMagnify != WantMagnify)
#endif
#if EnableMagnify && VarFullScreen
		||
#endif
#if VarFullScreen
		(UseFullScreen != WantFullScreen)
#endif
		)
	{
		(void) ReCreateMainWindow();
	}
}
#endif

#if VarFullScreen && EnableMagnify
enum {
	kWinStateWindowed,
#if EnableMagnify
	kWinStateFullScreen,
#endif
	kNumWinStates
};
#endif

#if VarFullScreen && EnableMagnify
LOCALVAR int WinMagStates[kNumWinStates];
#endif

LOCALPROC ZapWinStateVars(void)
{
#if MayNotFullScreen
	{
		int i;

		for (i = 0; i < kNumMagStates; ++i) {
			HavePositionWins[i] = falseblnr;
		}
	}
#endif
#if VarFullScreen && EnableMagnify
	{
		int i;

		for (i = 0; i < kNumWinStates; ++i) {
			WinMagStates[i] = kMagStateAuto;
		}
	}
#endif
}

#if VarFullScreen
LOCALPROC ToggleWantFullScreen(void)
{
	WantFullScreen = ! WantFullScreen;

#if EnableMagnify
	{
		int OldWinState =
			UseFullScreen ? kWinStateFullScreen : kWinStateWindowed;
		int OldMagState =
			UseMagnify ? kMagStateMagnifgy : kMagStateNormal;
		int NewWinState =
			WantFullScreen ? kWinStateFullScreen : kWinStateWindowed;
		int NewMagState = WinMagStates[NewWinState];

		WinMagStates[OldWinState] = OldMagState;
		if (kMagStateAuto != NewMagState) {
			WantMagnify = (kMagStateMagnifgy == NewMagState);
		} else {
			WantMagnify = falseblnr;
			if (WantFullScreen) {
				if ((GetSystemMetrics(SM_CXSCREEN)
						>= vMacScreenWidth * MyWindowScale)
					&& (GetSystemMetrics(SM_CYSCREEN)
						>= vMacScreenHeight * MyWindowScale)
					)
				{
					WantMagnify = trueblnr;
				}
			}
		}
	}
#endif
}
#endif

#if EnableDragDrop
LOCALPROC DragFunc(HDROP hDrop)
{
	WORD n;
	WORD i;
	TCHAR a[_MAX_PATH];

	n = DragQueryFile(hDrop, (UINT) -1, NULL, 0);
	for (i = 0; i < n; ++i) {
		if (DragQueryFile(hDrop, i, NULL, 0) < _MAX_PATH - 1) {
			(void) DragQueryFile(hDrop, i, a, _MAX_PATH);
			(void) InsertDiskOrAlias(a, trueblnr, trueblnr);
		}
	}

	DragFinish(hDrop);

	if (gTrueBackgroundFlag) {
		if (! SetForegroundWindow(MainWnd)) {
			/* error message here ? */
		}

		WantCmdOptOnReconnect = trueblnr;
	}
}
#endif

GLOBALOSGLUFUNC blnr ExtraTimeNotOver(void)
{
#if MySoundEnabled
	SoundCheckVeryOften();
#endif
	(void) UpdateTrueEmulatedTime();
	return (TrueEmulatedTime == OnTrueTime);
}

/* --- platform independent code can be thought of as going here --- */

LOCALPROC LeaveBackground(void)
{
	ReconnectKeyCodes3();
}

LOCALPROC EnterBackground(void)
{
	DisconnectKeyCodes3();

#if VarFullScreen
	if (WantFullScreen) {
		ToggleWantFullScreen();
	}
#endif
}

LOCALPROC LeaveSpeedStopped(void)
{
#if MySoundEnabled
	MySound_Start();
#endif
#if (MyTimeResolution != 0)
	MyTimer_Resume();
#endif
}

LOCALPROC EnterSpeedStopped(void)
{
#if (MyTimeResolution != 0)
	MyTimer_Suspend();
#endif
#if MySoundEnabled
	MySound_Stop();
#endif
}

LOCALPROC CheckForSavedTasks(void)
{
	/*
		Check for things to do that rather wouldn't
		have done at an awkward time.
	*/

	if (MyEvtQNeedRecover) {
		MyEvtQNeedRecover = falseblnr;

		/* attempt cleanup, MyEvtQNeedRecover may get set again */
		MyEvtQTryRecoverFromFull();
	}

#if EnableFSMouseMotion
	if (HaveMouseMotion) {
		MyMouseConstrain();
	}
#endif

	if (RequestMacOff) {
		RequestMacOff = falseblnr;
		if (AnyDiskInserted()) {
			MacMsgOverride(kStrQuitWarningTitle,
				kStrQuitWarningMessage);
		} else {
			ForceMacOff = trueblnr;
		}
	}

	if (ForceMacOff) {
		return;
	}

	if (gTrueBackgroundFlag != gBackgroundFlag) {
		gBackgroundFlag = gTrueBackgroundFlag;
		if (gTrueBackgroundFlag) {
			EnterBackground();
		} else {
			LeaveBackground();
		}
	}

	if (CurSpeedStopped != (SpeedStopped ||
		(gBackgroundFlag && ! RunInBackground
#if EnableAutoSlow && 0
			&& (QuietSubTicks >= 4092)
#endif
		)))
	{
		CurSpeedStopped = ! CurSpeedStopped;
		if (CurSpeedStopped) {
			EnterSpeedStopped();
		} else {
			LeaveSpeedStopped();
		}
	}

#if EnableRecreateW
	if (! (gTrueBackgroundFlag)) {
		CheckMagnifyAndFullScreen();
	}
#endif

#if MayFullScreen
	if (GrabMachine != (
#if VarFullScreen
		UseFullScreen &&
#endif
		! (gTrueBackgroundFlag || CurSpeedStopped)))
	{
		GrabMachine = ! GrabMachine;
		AdjustMachineGrab();
	}
#endif

	if (gTrueBackgroundFlag) {
		/*
			wait til later
		*/
	} else {
#if IncludeSonyNew
		if (vSonyNewDiskWanted) {
#if IncludeSonyNameNew
			if (vSonyNewDiskName != NotAPbuf) {
				HGLOBAL NewDiskNameDat;
				if (MacRomanTextToNativeHand(vSonyNewDiskName, trueblnr,
					&NewDiskNameDat))
				{
					MakeNewDisk(vSonyNewDiskSize, NewDiskNameDat);
					GlobalFree(NewDiskNameDat);
				}
				PbufDispose(vSonyNewDiskName);
				vSonyNewDiskName = NotAPbuf;
			} else
#endif
			{
				MakeNewDisk(vSonyNewDiskSize, NULL);
			}
			vSonyNewDiskWanted = falseblnr;
				/* must be done after may have gotten disk */
		}
#endif
		if (RequestInsertDisk) {
			RequestInsertDisk = falseblnr;
			InsertADisk0();
		}
	}

#if NeedRequestIthDisk
	if (0 != RequestIthDisk) {
		Sony_InsertIth(RequestIthDisk);
		RequestIthDisk = 0;
	}
#endif

	if (HaveCursorHidden != (WantCursorHidden
		&& ! (gTrueBackgroundFlag || CurSpeedStopped)))
	{
		HaveCursorHidden = ! HaveCursorHidden;
		if (HaveCursorHidden) {
			(void) ShowCursor(FALSE);
		} else {
			(void) ShowCursor(TRUE);
			SetCursor(LoadCursor(NULL, IDC_ARROW));
		}
	}

	if ((nullpr != SavedBriefMsg) & ! MacMsgDisplayed) {
		MacMsgDisplayOn();
	}

	if (NeedWholeScreenDraw) {
		NeedWholeScreenDraw = falseblnr;
		ScreenChangedAll();
	}
}

#if UseWinCE
/* Sip Status ON/OFF */
LOCALVAR blnr SipOn = falseblnr;
#endif

LRESULT CALLBACK Win32WMProc(HWND hwnd,
	UINT uMessage, WPARAM wparam, LPARAM lparam);

LRESULT CALLBACK Win32WMProc(HWND hwnd,
	UINT uMessage, WPARAM wparam, LPARAM lparam)
{
	switch (uMessage)
	{
		case WM_PAINT:
			{
				PAINTSTRUCT ps;

				BeginPaint(hwnd, (LPPAINTSTRUCT)&ps);
#if VarFullScreen
				if (UseFullScreen)
#endif
#if MayFullScreen
				{
					if (! FillRect(ps.hdc,
						&ps.rcPaint,
						GetStockObject(BLACK_BRUSH)))
					{
						/* ReportGetLastError(); */
					}
				}
#endif
				if (MainWnd == hwnd) {
					Screen_DrawAll();
				}
				EndPaint(hwnd, (LPPAINTSTRUCT)&ps);
			}
			break;

		case WM_KEYDOWN:
		case WM_SYSKEYDOWN:
#if UseWinCE
			SipOn = falseblnr;

			{
				SIPINFO r;

				memset(&r, 0 , sizeof(SIPINFO));
				r.cbSize = sizeof(SIPINFO);
				if (SipGetInfo(&r)) {
					SipOn = 0 != (r.fdwFlags & SIPF_ON);
				}
			}

			if (wparam == 0xAE) {
				break;
			} else if ((! SipOn) && (wparam == VK_RETURN)) {
				break;
			} else if ((! SipOn)
				&& (wparam >= VK_LEFT) && (wparam <= VK_DOWN))
			{
				break;
			} else if (wparam == VK_CONTROL && CtrlMode) {
				DoVKcode0(wparam, falseblnr);
				CtrlMode = falseblnr;
				break;
			} else if (wparam == VK_CONTROL) {
				DoVKcode0(wparam, trueblnr);
				CtrlMode = trueblnr;
				break;
			}
#endif
			if (! TestBit(lparam, 30)) { /* ignore repeats */
				DoVKcode(wparam, lparam >> 24, trueblnr);
			}

#if UseWinCE
			return TRUE;
				/*
					So that hardware keys won't be
					processed by the default handler
				*/
#endif

			break;
		case WM_KEYUP:
		case WM_SYSKEYUP:
#if UseWinCE
			SipOn = falseblnr;

			{
				SIPINFO r;

				memset(&r, 0 , sizeof(SIPINFO));
				r.cbSize = sizeof(SIPINFO);
				if (SipGetInfo(&r)) {
					SipOn = 0 != (r.fdwFlags & SIPF_ON);
				}
			}

			if (wparam == 0xAE) { /* to hide SoftInput panel */
				SipShowIM(SIPF_OFF);
				break;
			} else if ((! SipOn) && (wparam == VK_RETURN)) {
				/* DPad Action to show SIP */
				/* Show SoftInput Panel */
				SipShowIM(SIPF_ON);
				break;
			} else if ((! SipOn)
				&& (wparam >= VK_LEFT) && (wparam <= VK_DOWN))
			{
				switch (wparam) {
					case VK_LEFT:
						if (ViewHStart < (ViewHSize / 2)) {
							ViewHStart = 0;
						} else {
							ViewHStart -= (ViewHSize / 2);
						}
						break;
					case VK_UP:
						if (ViewVStart < (ViewVSize / 2)) {
							ViewVStart = 0;
						} else {
							ViewVStart -= (ViewVSize / 2);
						}
						break;
					case VK_RIGHT:
						ViewHStart += (ViewHSize / 2);
						if (ViewHStart >= (vMacScreenWidth - ViewHSize))
						{
							ViewHStart = vMacScreenWidth - ViewHSize;
						}
						break;
					case VK_DOWN:
						ViewVStart += (ViewVSize / 2);
						if (ViewVStart
							>= (vMacScreenHeight - ViewVSize))
						{
							ViewVStart = vMacScreenHeight - ViewVSize;
						}
						break;
				}
				Screen_DrawAll();
			} else
			if (wparam == VK_CONTROL && CtrlMode) {
				break;
			}
#endif
			DoVKcode(wparam, lparam >> 24, falseblnr);

#if UseWinCE
			return TRUE;
				/*
					So that hardware keys won't be
					processed by the default handler
				*/
#endif

			break;
#if ItnlKyBdFix && ! UseWinCE
		case WM_INPUTLANGCHANGE:
			MyCheckKeyboardLayout();
			return TRUE;
			break;
#endif

		case WM_CLOSE:
			RequestMacOff = trueblnr;
			break;
#if ! UseWinCE
		case WM_QUERYENDSESSION:
			if (AnyDiskInserted()) {
				RequestMacOff = trueblnr;
				return FALSE;
			} else {
				return TRUE;
			}
			break;
#endif
		case WM_ACTIVATE:
			if (MainWnd == hwnd) {
				gTrueBackgroundFlag = (LOWORD(wparam) == WA_INACTIVE);
			}
			break;
		case WM_COMMAND:
			switch(LOWORD(wparam))
			{
				case ID_FILE_INSERTDISK1:
					RequestInsertDisk = trueblnr;
					break;
				case ID_FILE_QUIT:
					RequestMacOff = trueblnr;
					break;
				case ID_SPECIAL_MORECOMMANDS:
					DoMoreCommandsMsg();
					break;
				case ID_HELP_ABOUT:
					DoAboutMsg();
					break;
			}
			break;
		case WM_MOVE:
			WndX = (si4b) LOWORD(lparam);
			WndY = (si4b) HIWORD(lparam);
			break;
		case WM_SYSCHAR:
		case WM_CHAR:
			/* prevent any further processing */
			break;
		case WM_LBUTTONDOWN:
		case WM_RBUTTONDOWN:
			MousePositionNotify(LOWORD (lparam), HIWORD (lparam));
			SetCurMouseButton(trueblnr);
			break;
		case WM_LBUTTONUP:
		case WM_RBUTTONUP:
			MousePositionNotify(LOWORD (lparam), HIWORD (lparam));
			SetCurMouseButton(falseblnr);
			break;
		case WM_MOUSEMOVE:
#if UseWinCE
			MousePositionNotify(LOWORD (lparam), HIWORD (lparam));
#endif
			/* windows may have messed up cursor */
			/*
				there is no notification when the mouse moves
				outside the window, and the cursor is automatically
				changed
			*/
			if (! HaveCursorHidden) {
				/* SetCursor(LoadCursor(NULL, IDC_ARROW)); */
			}
			break;
#if EnableDragDrop
		case WM_CREATE:
			DragAcceptFiles(hwnd, TRUE);
			break;
		case WM_DROPFILES:
			DragFunc((HDROP) wparam);
			break;
		case WM_DESTROY:
			DragAcceptFiles(hwnd, FALSE);
			break;
#endif
		default:
			return DefWindowProc(hwnd, uMessage, wparam, lparam);
	}
	return 0;
}

LOCALFUNC blnr RegisterOurClass(void)
{
	WNDCLASS wc;

	wc.style         = CS_HREDRAW | CS_VREDRAW
#if ! UseWinCE
		| CS_OWNDC
#endif
		;
	wc.lpfnWndProc   = (WNDPROC)Win32WMProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = AppInstance;
	wc.hIcon         = LoadIcon(AppInstance, MAKEINTRESOURCE(IDI_VMAC));
	wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = GetStockObject(BLACK_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = WndClassName;

	if (! RegisterClass(&wc)) {
		MacMsg("RegisterClass failed",
			"Sorry, Mini vMac encountered errors"
			" and cannot continue.", trueblnr);
		return falseblnr;
	} else {
		return trueblnr;
	}
}

LOCALPROC WaitForTheNextEvent(void)
{
	MSG msg;

	if (-1 != GetMessage(&msg, NULL, 0, 0)) {
		DispatchMessage(&msg);
	}
}

LOCALPROC CheckForSystemEvents(void)
{
	MSG msg;
	ui3r i = 0;

	while ((i < 32) && (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))) {
		DispatchMessage(&msg);
		++i;
	}
}

GLOBALOSGLUPROC WaitForNextTick(void)
{
label_retry:
	CheckForSystemEvents();
	CheckForSavedTasks();

	if (ForceMacOff) {
		return;
	}

	if (CurSpeedStopped) {
		DoneWithDrawingForTick();
		WaitForTheNextEvent();
		goto label_retry;
	}

	if (ExtraTimeNotOver()) {
		Sleep(NextIntTime - LastTime);
		goto label_retry;
	}

	if (CheckDateTime()) {
#if MySoundEnabled
		MySound_SecondNotify();
#endif
#if EnableDemoMsg
		DemoModeSecondNotify();
#endif
	}

	if (! (gBackgroundFlag)) {
#if ! UseWinCE
		CheckMouseState();
#endif

#if EnableGrabSpecialKeys
		CheckForLostKeyUps();
#endif
	}

	OnTrueTime = TrueEmulatedTime;

#if dbglog_TimeStuff
	dbglog_writelnNum("WaitForNextTick, OnTrueTime", OnTrueTime);
#endif
}

#if UseWinCE
LOCALFUNC blnr Init_ChangeOrientation(void)
{
	DEVMODE dm;

	/* initialize the DEVMODE structure */
	ZeroMemory(&dm, sizeof (dm));
	dm.dmSize = sizeof (dm);

	EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm);

	/* Backup old values */
	oldOrientation = dm.dmOrientation;
	oldDisplayOrientation = dm.dmDisplayOrientation;


	/* Hide SIP (you can never tell...) */
	SipShowIM(SIPF_OFF);

	/* Switch to Landscape mode if possible */
	dm.dmOrientation = DMORIENT_LANDSCAPE;
	dm.dmDisplayOrientation = DMDO_90;
	dm.dmFields = DM_ORIENTATION | DM_DISPLAYORIENTATION;
	(void) ChangeDisplaySettingsEx(NULL, &dm, NULL, 0, 0);
	/*
		if (ChangeDisplaySettingsEx(NULL, &dm, NULL, 0, 0) !=
			DISP_CHANGE_SUCCESSFUL)
		{
			MacMsg ("warning",
				"Couldn't switch to Landscape mode.", falseblnr);
		}
	*/

	return trueblnr;
}
#endif

#if UseWinCE
LOCALPROC Uninit_ChangeOrientation(void)
{
	DEVMODE dm;

	/* Restore old display orientation */
	ZeroMemory(&dm, sizeof (dm));
	dm.dmSize = sizeof(dm);

	EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm);

	dm.dmOrientation = oldOrientation;
	dm.dmDisplayOrientation = oldDisplayOrientation;
	dm.dmFields = DM_ORIENTATION | DM_DISPLAYORIENTATION;

	ChangeDisplaySettingsEx(NULL, &dm, 0, 0, 0);
}
#endif


/* ** code for handling hardware keys in Pocket PC devices ** */

#if UseWinCE
typedef BOOL (__stdcall *UnregisterFunc1Proc)(UINT, UINT);
LOCALVAR HINSTANCE hCoreDLL = NULL;
#endif

#if UseWinCE
LOCALFUNC blnr InitHotKeys(void)
{
	UnregisterFunc1Proc procUndergisterFunc;
	int i;

	hCoreDLL = LoadLibrary(TEXT("coredll.dll"));
	if (! hCoreDLL) {
		MacMsg ("Fatal", "Could not load coredll.dll", trueblnr);
	} else {
		procUndergisterFunc =
			(UnregisterFunc1Proc) GetProcAddress(hCoreDLL,
			TEXT("UnregisterFunc1"));
		if (! procUndergisterFunc) {
			MacMsg ("Fatal",
				"Could not get UnregisterFunc1 procedure", trueblnr);
		} else {
			for (i = 0xc1; i <= 0xcf; ++i) {
				procUndergisterFunc(MOD_WIN, i);
				RegisterHotKey(MainWnd, i, MOD_WIN, i);
			}
		}
	}
	return trueblnr;
}
#endif

#if UseWinCE
LOCALPROC UninitHotKeys(void)
{
	if (! hCoreDLL) {
		FreeLibrary(hCoreDLL);
	}
}
#endif

#include "PROGMAIN.h"

/* ************************ */

LOCALPROC ZapOSGLUVars(void)
{
	InitDrives();
	ZapWinStateVars();
}

LOCALPROC ReserveAllocAll(void)
{
#if dbglog_HAVE
	dbglog_ReserveAlloc();
#endif
	ReserveAllocOneBlock(&ROM, kROM_Size, 5, falseblnr);
	ReserveAllocOneBlock(&screencomparebuff,
		vMacScreenNumBytes, 5, trueblnr);
#if UseControlKeys
	ReserveAllocOneBlock(&CntrlDisplayBuff,
		vMacScreenNumBytes, 5, falseblnr);
#endif
#if EnableScalingBuff
	{
		ui5r n = vMacScreenMonoNumBytes
#if EnableMagnify
			* MyWindowScale * MyWindowScale
#endif
			;
#if 1 == vMacScreenDepth
		if (vMacScreenNumBytes * 2 > n) {
			n = vMacScreenNumBytes * 2;
		}
#elif vMacScreenDepth >= 4
		if (vMacScreenNumBytes > n) {
			n = vMacScreenNumBytes;
		}
#endif
		ReserveAllocOneBlock(&ScalingBuff, n, 5, falseblnr);
	}
#endif
#if MySoundEnabled
	ReserveAllocOneBlock((ui3p *)&TheSoundBuffer,
		dbhBufferSize, 5, falseblnr);
#endif

	EmulationReserveAlloc();
}

LOCALFUNC blnr AllocMyMemory(void)
{
	uimr n;
	blnr IsOk = falseblnr;

	ReserveAllocOffset = 0;
	ReserveAllocBigBlock = nullpr;
	ReserveAllocAll();
	n = ReserveAllocOffset;
	ReserveAllocBigBlock =
		(ui3p)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, n);
	if (NULL == ReserveAllocBigBlock) {
		MacMsg(kStrOutOfMemTitle, kStrOutOfMemMessage, trueblnr);
	} else {
		ReserveAllocOffset = 0;
		ReserveAllocAll();
		if (n != ReserveAllocOffset) {
			/* oops, program error */
		} else {
			IsOk = trueblnr;
		}
	}

	return IsOk;
}

LOCALPROC UnallocMyMemory(void)
{
	if (nullpr != ReserveAllocBigBlock) {
		if (GlobalFree(ReserveAllocBigBlock) != NULL) {
			MacMsg("error", "GlobalFree failed", falseblnr);
		}
	}
}

LOCALFUNC blnr InitOSGLU(void)
{
	if (AllocMyMemory())
#if dbglog_HAVE
	if (dbglog_open())
#endif
	if (RegisterOurClass())
	if (ScanCommandLine())
	if (LoadInitialImages())
#if InstallFileIcons
	if (RegisterInRegistry())
#endif
	if (LoadMacRom())
#if UseActvCode
	if (ActvCodeInit())
#endif
#if UseWinCE
	if (Init_ChangeOrientation())
#endif
	if (ReCreateMainWindow())
	if (InitWinKey2Mac())
	if (InitTheCursor())
#if UseWinCE
	if (InitHotKeys())
#endif
	if (Init60thCheck())
	if (WaitForRom())
	{
		return trueblnr;
	}
	return falseblnr;
}

LOCALPROC UnInitOSGLU(void)
{
#if (MyTimeResolution != 0)
	MyTimer_Suspend();
#endif
	MyMouseCaptureSet(falseblnr);

	if (MacMsgDisplayed) {
		MacMsgDisplayOff();
	}

#if MayFullScreen
	UnGrabTheMachine();
#endif
#if MySoundEnabled
	MySound_Stop();
#endif
#if IncludePbufs
	UnInitPbufs();
#endif
	UnInitDrives();

	ForceShowCursor();
#if UseWinCE
	Uninit_ChangeOrientation();
	UninitHotKeys();
#endif

#if EnableShellLinks
	MyUninitCOM();
#endif

	if (! gTrueBackgroundFlag) {
		CheckSavedMacMsg();
	}

	DisposeMainWindow();

#if dbglog_HAVE
	dbglog_close();
#endif

	UnallocMyMemory();
}

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	LPTSTR lpCmdLine, int nCmdShow)
{
	UnusedParam(hPrevInstance);
	AppInstance = hInstance;
	CmdShow = nCmdShow;
	CommandLine = lpCmdLine;

	GetWndTitle();
#if UseWinCE
	if (AlreadyRunningCheck()) {
		return 0;
	}
#endif

	ZapOSGLUVars();
	if (InitOSGLU()) {
		ProgramMain();
	}
	UnInitOSGLU();

	return(0);
}