/*
 *   kml.c
 *
 *   This file is part of Emu48
 *
 *   Copyright (C) 1995 Sebastien Carlier
 *
 */
#include "pch.h"
#include "resource.h"
#include "Emu48.h"
#include "kml.h"

static VOID    FatalError();
static VOID    InitLex(LPSTR szScript);
static VOID    CleanLex();
static BOOL    IsDigit(CHAR cChar);
static VOID    SkipWhite(UINT nMode);
static TokenId ParseToken(UINT nMode);
static DWORD   ParseInteger();
static LPSTR   ParseString();
static TokenId Lex(UINT nMode);
static Line*   ParseLine(TokenId eCommand);
static Line*   IncludeLines(LPCSTR szFilename);
static Line*   ParseLines();
static Block*  ParseBlock(TokenId eBlock);
static Block*  IncludeBlocks(LPCSTR szFilename);
static Block*  ParseBlocks();
static VOID    FreeLines(Line* pLine);
static VOID    PressButton(UINT nId);
static VOID    ReleaseButton(UINT nId);
static VOID    PressButtonById(UINT nId);
static VOID    ReleaseButtonById(UINT nId);
static LPSTR   GetStringParam(Block* pBlock, TokenId eBlock, enum eCommand, UINT nParam);
static DWORD   GetIntegerParam(Block* pBlock, TokenId eBlock, enum eCommand, UINT nParam);
static Line*   SkipLines(Line* pLine, enum eCommand);
static Line*   If(Line* pLine, BOOL bCondition);
static Line*   RunLine(Line* pLine);
static Block*  LoadKMLGlobal(LPCSTR szFilename);

Block* pKml;
static Block* pVKey[256];
static BYTE   byVKeyMap[256];
static Button pButton[256];
static Annunciator pAnnunciator[6];
static UINT nButtons = 0;
static UINT nScancodes = 0;
static UINT nAnnunciators = 0;
static BOOL  bDebug = TRUE;
static UINT  nLexLine;
static UINT  nLexInteger;
static UINT  nBlocksIncludeLevel;
static UINT  nLinesIncludeLevel;
static DWORD nKMLFlags = 0;
static LPSTR szLexString;
static LPSTR szText;
static LPSTR szLexDelim[] =
{
	"",
	" \t\n\r",
	" \t\n\r",
	" \t\r"
};

static Token pLexToken[] =
{
	{TOK_ANNUNCIATOR,000001,11,"Annunciator"},
	{TOK_BACKGROUND, 000000,10,"Background"},
	{TOK_IFPRESSED,  000001, 9,"IfPressed"},
	{TOK_RESETFLAG,  000001, 9,"ResetFlag"},
	{TOK_SCANCODE,   000001, 8,"Scancode"},
	{TOK_MENUITEM,   000001, 8,"MenuItem"},
	{TOK_SETFLAG,    000001, 7,"SetFlag"},
	{TOK_RELEASE,    000001, 7,"Release"},
	{TOK_VIRTUAL,    000000, 7,"Virtual"},
	{TOK_INCLUDE,    000002, 7,"Include"},
	{TOK_NOTFLAG,    000001, 7,"NotFlag"},
	{TOK_GLOBAL,     000000, 6,"Global"},
	{TOK_AUTHOR,     000002, 6,"Author"},
	{TOK_BITMAP,     000002, 6,"Bitmap"},
	{TOK_OFFSET,     000011, 6,"Offset"},
	{TOK_BUTTON,     000001, 6,"Button"},
	{TOK_IFFLAG,     000001, 6,"IfFlag"},
	{TOK_ONDOWN,     000000, 6,"OnDown"},
	{TOK_NOHOLD,     000000, 6,"NoHold"},
	{TOK_TITLE,      000002, 5,"Title"},
	{TOK_OUTIN,      000011, 5,"OutIn"},
	{TOK_PATCH,      000002, 5,"Patch"},
	{TOK_PRINT,      000002, 5,"Print"},
	{TOK_DEBUG,      000001, 5,"Debug"},
	{TOK_COLOR,      001111, 5,"Color"},
	{TOK_MODEL,      000002, 5,"Model"},
	{TOK_PRESS,      000001, 5,"Press"},
	{TOK_TYPE,       000001, 4,"Type"},
	{TOK_SIZE,       000011, 4,"Size"},
	{TOK_ZOOM,       000001, 4,"Zoom"},
	{TOK_DOWN,       000011, 4,"Down"},
	{TOK_ELSE,       000000, 4,"Else"},
	{TOK_ONUP,       000000, 4,"OnUp"},
	{TOK_MAP,        000011, 3,"Map"},
	{TOK_ROM,        000002, 3,"Rom"},
	{TOK_LCD,        000000, 3,"Lcd"},
	{TOK_END,        000000, 3,"End"},
	{0,              000000, 0,""},
};

static TokenId eIsBlock[] =
{
	TOK_IFFLAG,
	TOK_IFPRESSED,
	TOK_ONDOWN,
	TOK_ONUP,
	TOK_NONE
};

static BOOL bClicking = FALSE;
static UINT uButtonClicked = 0;

//################
//#
//#    Compilation Result
//#
//################

static UINT nLogLength = 0;
static LPSTR szLog = NULL;
static BOOL bKmlLogOkEnabled = FALSE;

static VOID ClearLog()
{
	nLogLength = 0;
	if (szLog != NULL)
	{
		LocalFree(szLog);
		szLog = NULL;
	}
	return;
}

static VOID AddToLog(LPSTR szString)
{
	UINT nLength = strlen(szString);
	if (szLog == NULL)
	{
		nLogLength = nLength+3;
		szLog = LocalAlloc(0,nLogLength);
		if (szLog==NULL)
		{
			nLogLength = 0;
			return;
		}
		lstrcpy(szLog,szString);
	}
	else
	{
		szLog = LocalReAlloc(szLog,nLogLength+nLength+2,LMEM_MOVEABLE);
		if (szLog == NULL)
		{
			nLogLength = 0;
			return;
		}
		lstrcpy(szLog+nLogLength-1,szString);
		nLogLength += nLength+2;
	}
	szLog[nLogLength-3] = 0x0D;
	szLog[nLogLength-2] = 0x0A;
	szLog[nLogLength-1] = 0;
	return;
}

static VOID __cdecl PrintfToLog(LPCTSTR lpFormat, ...)
{
	LPSTR lpOutput;
	va_list arglist;

	va_start(arglist,lpFormat);
	lpOutput = LocalAlloc(0,1024);
	wvsprintf(lpOutput,lpFormat,arglist);
	AddToLog(lpOutput);
	LocalFree(lpOutput);
	va_end(arglist);
	return;
}

static BOOL CALLBACK KMLLogProc(HWND hDlg, UINT message, DWORD wParam, LONG lParam)
{
	LPSTR szString;

	switch (message)
	{
	case WM_INITDIALOG:
		// set OK
		EnableWindow(GetDlgItem(hDlg,IDOK),bKmlLogOkEnabled);
		// set IDC_TITLE
		szString = GetStringParam(pKml, TOK_GLOBAL, TOK_TITLE, 0);
		if (szString == NULL) szString = "Untitled";
		SetDlgItemText(hDlg,IDC_TITLE,szString);
		// set IDC_AUTHOR
		szString = GetStringParam(pKml, TOK_GLOBAL, TOK_AUTHOR, 0);
		if (szString == NULL) szString = "<Unknown Author>";
		SetDlgItemText(hDlg,IDC_AUTHOR,szString);
		// set IDC_KMLLOG
		if (szLog == NULL)
			SetDlgItemText(hDlg,IDC_KMLLOG,"Memory Allocation Failure.");
		else
			SetDlgItemText(hDlg,IDC_KMLLOG,szLog);
		// set IDC_ALWAYSDISPLOG
		CheckDlgButton(hDlg,IDC_ALWAYSDISPLOG,bAlwaysDisplayLog);
		// redraw window
		InvalidateRect(hDlg, NULL, TRUE);
		return TRUE;
	case WM_COMMAND:
		bAlwaysDisplayLog = IsDlgButtonChecked(hDlg, IDC_ALWAYSDISPLOG);
		if ((wParam == IDCANCEL)||(wParam == IDOK)) EndDialog(hDlg, wParam);
		break;
	}
	return FALSE;
    UNREFERENCED_PARAMETER(lParam);
    UNREFERENCED_PARAMETER(hDlg);
}

BOOL DisplayKMLLog(BOOL bOkEnabled)
{
	BOOL bResult;
	bKmlLogOkEnabled = bOkEnabled;
	bResult = DialogBox(hApp, MAKEINTRESOURCE(IDD_KMLLOG), hWnd, (DLGPROC)KMLLogProc);
	return (bResult == IDOK);
}



//################
//#
//#    Choose Script
//#
//################

typedef struct _KmlScript
{
	LPSTR szFilename;
	LPSTR szTitle;
	DWORD   nId;
	struct _KmlScript* pNext;
} KmlScript;

static UINT nKmlFiles;
static KmlScript* pKmlList;
static CHAR cKmlType;

static VOID DestroyKmlList()
{
	KmlScript* pList;

	while (pKmlList)
	{
		pList = pKmlList->pNext;
		LocalFree(pKmlList->szFilename);
		LocalFree(pKmlList->szTitle);
		LocalFree(pKmlList);
		pKmlList = pList;
	}
	nKmlFiles = 0;
}

static VOID CreateKmlList()
{
	HANDLE hFindFile;
	WIN32_FIND_DATA pFindFileData;

	SetCurrentDirectory(szEmu48Directory);
	hFindFile = FindFirstFile("*.KML",&pFindFileData);
	SetCurrentDirectory(szCurrentDirectory);
	nKmlFiles = 0;
	if (hFindFile == INVALID_HANDLE_VALUE) return;
	do
	{
		KmlScript* pScript;
		Block* pBlock;
		LPSTR szTitle;

		pBlock = LoadKMLGlobal(pFindFileData.cFileName);
		if (pBlock == NULL) continue;
		if (cKmlType)
		{
			szTitle = GetStringParam(pBlock,TOK_GLOBAL,TOK_MODEL,0);
			if ((!szTitle)||(szTitle[0]!=cKmlType))
			{
				FreeBlocks(pBlock);
				continue;
			}
		}
		pScript = LocalAlloc(0, sizeof(KmlScript));
		pScript->szFilename = DuplicateString(pFindFileData.cFileName);
		szTitle = GetStringParam(pBlock,TOK_GLOBAL,TOK_TITLE,0);
		if (szTitle == NULL) szTitle = DuplicateString(pScript->szFilename);
		pScript->szTitle = DuplicateString(szTitle);
		FreeBlocks(pBlock);
		pScript->nId = nKmlFiles;
		pScript->pNext = pKmlList;
		pKmlList = pScript;
		nKmlFiles++;
	} while (FindNextFile(hFindFile,&pFindFileData));
	FindClose(hFindFile);
	return;
};

static BOOL CALLBACK ChooseKMLProc(HWND hDlg, UINT message, DWORD wParam, LONG lParam)
{
	HWND hList;
	KmlScript* pList;
	UINT nIndex;

	switch (message)
	{
	case WM_INITDIALOG:
		SetDlgItemText(hDlg,IDC_EMU48DIR,szEmu48Directory);
		hList = GetDlgItem(hDlg,IDC_KMLSCRIPT);
		SendMessage(hList, CB_RESETCONTENT, 0, 0);
		pList = pKmlList;
		while (pList)
		{
			nIndex = SendMessage(hList, CB_ADDSTRING, 0, (LPARAM)pList->szTitle);
			SendMessage(hList, CB_SETITEMDATA, nIndex, (LPARAM)pList->nId);
			pList = pList->pNext;
		}
		SendMessage(hList, CB_SETCURSEL, 0, 0);
		return TRUE;
	case WM_SETTEXT:
		{
			CHAR szBuffer[80];
			wsprintf(szBuffer,"%i %i %i", hDlg, wParam, lParam);
			InfoMessage(szBuffer);
		}
		break;
	case WM_COMMAND:
		if (wParam == IDC_UPDATE)
		{
			DestroyKmlList();
			GetDlgItemText(hDlg,IDC_EMU48DIR,szEmu48Directory,260);
			CreateKmlList();
			hList = GetDlgItem(hDlg,IDC_KMLSCRIPT);
			SendMessage(hList, CB_RESETCONTENT, 0, 0);
			pList = pKmlList;
			while (pList)
			{
				nIndex = SendMessage(hList, CB_ADDSTRING, 0, (LPARAM)pList->szTitle);
				SendMessage(hList, CB_SETITEMDATA, nIndex, (LPARAM)pList->nId);
				pList = pList->pNext;
			}
			SendMessage(hList, CB_SETCURSEL, 0, 0);
			break;
		}
		if (wParam == IDOK)
		{
			GetDlgItemText(hDlg,IDC_EMU48DIR,szEmu48Directory,260);
			hList = GetDlgItem(hDlg,IDC_KMLSCRIPT);
			nIndex = SendMessage(hList, CB_GETCURSEL, 0, 0);
			nIndex = SendMessage(hList, CB_GETITEMDATA, nIndex, 0);
			pList = pKmlList;
			while (pList)
			{
				if (pList->nId == nIndex) break;
				pList = pList->pNext;
			}
			if (pList)
			{
				lstrcpy(szCurrentKml, pList->szFilename);
				EndDialog(hDlg, IDOK);
			}
			break;
		}
		if (wParam == IDCANCEL)
		{
			EndDialog(hDlg, IDCANCEL);
			break;
		}
		break;
	}
	return FALSE;
    UNREFERENCED_PARAMETER(lParam);
    UNREFERENCED_PARAMETER(hDlg);
}

BOOL DisplayChooseKml(CHAR cType)
{
	BOOL bResult;
	cKmlType = cType;
	CreateKmlList();
	bResult = DialogBox(hApp, MAKEINTRESOURCE(IDD_CHOOSEKML), hWnd, (DLGPROC)ChooseKMLProc);
	DestroyKmlList();
	return (bResult == IDOK);
}



//################
//#
//#    Script Parsing
//#
//################

static VOID FatalError()
{
	PrintfToLog("Fatal Error at line %i", nLexLine);
	szText[0] = 0;
	return;
}

static VOID InitLex(LPSTR szScript)
{
	nLexLine = 1;
	szText = szScript;
	return;
}

static VOID CleanLex()
{
	nLexLine = 0;
	nLexInteger = 0;
	szLexString = NULL;
	szText = NULL;
	return;
}

// TODO: Change this poor (and slow!) code
static BOOL IsBlock(TokenId eId)
{
	UINT uBlock = 0;
	while (eIsBlock[uBlock] != TOK_NONE)
	{
		if (eId == eIsBlock[uBlock]) return TRUE;
		uBlock++;
	}
	return FALSE;
}


static LPCTSTR GetStringOf(TokenId eId)
{
	UINT i = 0;
	while (pLexToken[i].nLen)
	{
		if (pLexToken[i].eId == eId) return pLexToken[i].szName;
		i++;
	}
	return "<Undefined>";
}

static BOOL IsDigit(CHAR cChar)
{
	if (cChar<'0') return FALSE;
	if (cChar>'9') return FALSE;
	return TRUE;
}

static VOID SkipWhite(UINT nMode)
{
	UINT i;
	BOOL bStop = FALSE;
loop:
	i = 0;
	while (szLexDelim[nMode][i])
	{
		if (*szText == szLexDelim[nMode][i]) break;
		i++;
	}
	if (szLexDelim[nMode][i] != 0)
	{
		if (szLexDelim[nMode][i]=='\n') nLexLine++;
		szText++;
		goto loop;
	}
	if (*szText=='#')
	{
		do szText++; while (*szText != '\n');
		if (nMode != LEX_PARAM) goto loop;
	}
	return;
}

static TokenId ParseToken(UINT nMode)
{
	UINT i, j, k;
	i = 0;
	while (szText[i])
	{
		j = 0;
		while (szLexDelim[nMode][j])
		{
			if (szLexDelim[nMode][j] == szText[i]) break;
			j++;
		}
		if (szLexDelim[nMode][j] == '\n') nLexLine++;
		if (szLexDelim[nMode][j] != 0) break;
		i++;
	}
	if (i==0)
	{
		return TOK_NONE;
	}
	j = 0;
	while (pLexToken[j].nLen)
	{
		if (pLexToken[j].nLen>i)
		{
			j++;
			continue;
		}
		if (pLexToken[j].nLen<i) break;
		k = 0;
		if (strncmp(pLexToken[j].szName, szText, i)==0)
		{
			szText += i;
			return pLexToken[j].eId;
		}
		j++;
	}
	szText[i] = 0;
	if (bDebug)
	{
		PrintfToLog("%i: Undefined token %s", nLexLine, szText);
		return TOK_NONE;
	}
	return TOK_NONE;
}

static DWORD ParseInteger()
{
	DWORD nNum = 0;
	while (IsDigit(*szText))
	{
		nNum = nNum*10 + ((*szText)-'0');
		szText++;
	} 
	return nNum;
}

static LPSTR ParseString()
{
	LPSTR szString;
	LPSTR szBuffer;
	UINT  nLength;
	UINT  nBlock;

	szText++;
	nLength = 0;
	nBlock = 255;
	szBuffer = LocalAlloc(0,nBlock+1);
	while (*szText != '"')
	{
		if (*szText == '\\') szText++;
		if (*szText == 0)
		{
			FatalError();
			return NULL;
		}
		szBuffer[nLength] = *szText;
		nLength++;
		if (nLength == nBlock)
		{
			nBlock += 256;
			szBuffer = LocalReAlloc(szBuffer, nBlock+1, LMEM_MOVEABLE);
		}
		szText++;
	}
	szText++;
	szBuffer[nLength] = 0;
	szString = DuplicateString(szBuffer);
	LocalFree(szBuffer);
	return szString;
}		

static TokenId Lex(UINT nMode)
{
	SkipWhite(nMode);
	if (IsDigit(*szText))
	{
		nLexInteger = ParseInteger();
		return TOK_INTEGER;
	}
	if (*szText == '"')
	{
		szLexString = ParseString();
		return TOK_STRING;
	}
	if ((nMode == LEX_PARAM) && (*szText == '\n'))
	{
		nLexLine++;
		szText++;
		return TOK_EOL;
	}
	return ParseToken(nMode);
}

static Line* ParseLine(TokenId eCommand)
{
	UINT   i, j;
	DWORD  nParams;
	TokenId eToken;
	Line*  pLine;

	i = 0;
	while (pLexToken[i].nLen)
	{
		if (pLexToken[i].eId == eCommand) break;
		i++;
	}
	if (pLexToken[i].nLen == 0) return NULL;

	j = 0;
	pLine = LocalAlloc(LPTR,sizeof(Line));
	pLine->eCommand = eCommand;
	nParams = pLexToken[i].nParams;
loop:
	eToken = Lex(LEX_PARAM);
	if ((nParams&7)==TYPE_NONE)
	{
		if (eToken != TOK_EOL)
		{
			PrintfToLog("%i: Too many parameters (%i expected).", nLexLine, j);
			LocalFree(pLine);
			return NULL;
		}
		return pLine;
	}
	if ((nParams&7)==TYPE_INTEGER)
	{
		if (eToken != TOK_INTEGER)
		{
			PrintfToLog("%i: Parameter %i of %s must be an integer.", nLexLine, j+1, pLexToken[i].szName);
			LocalFree(pLine);
			return NULL;
		}
		pLine->nParam[j++] = nLexInteger;
		nParams >>= 3;
		goto loop;
	}
	if ((nParams&7)==TYPE_STRING)
	{
		if (eToken != TOK_STRING)
		{
			PrintfToLog("%i: Parameter %i of %s must be a string.", nLexLine, j+1, pLexToken[i].szName);
			LocalFree(pLine);
			return NULL;
		}
		pLine->nParam[j++] = (DWORD)szLexString;
		nParams >>= 3;
		goto loop;
	}
	AddToLog("Oops...");
	LocalFree(pLine);
	return NULL;
}

static Line* IncludeLines(LPCSTR szFilename)
{
	HANDLE hFile;
	DWORD  dwFileSizeLow;
	DWORD  dwFileSizeHigh;
	DWORD  dwBytesRead;
	LPBYTE lpbyBuf;
	UINT   uOldLine;
	LPSTR  szOldText;
	Line*  pLine;

	SetCurrentDirectory(szEmu48Directory);
	hFile = CreateFile(szFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
	SetCurrentDirectory(szCurrentDirectory);
	if (hFile == INVALID_HANDLE_VALUE)
	{
		PrintfToLog("Error while opening include file %s.", szFilename);
		FatalError();
		return NULL;
	}
	dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
	if (dwFileSizeHigh != 0)
	{ // file is too large.
		AddToLog("File is too large.");
		CloseHandle(hFile);
		FatalError();
		return NULL;
	}
	lpbyBuf = (LPBYTE)LocalAlloc(0,dwFileSizeLow+1);
	if (lpbyBuf == NULL)
	{
		PrintfToLog("Cannot allocate %i bytes.", dwFileSizeLow+1);
		CloseHandle(hFile);
		FatalError();
		return NULL;
	}
	ReadFile(hFile, lpbyBuf, dwFileSizeLow, &dwBytesRead, NULL);
	CloseHandle(hFile);
	lpbyBuf[dwFileSizeLow] = 0;

	uOldLine  = nLexLine;
	szOldText = szText;

	nLinesIncludeLevel++;
	PrintfToLog("l%i:Including %s", nLinesIncludeLevel, szFilename);
	InitLex(lpbyBuf);
	pLine = ParseLines();
	CleanLex();
	nLinesIncludeLevel--;

	nLexLine  = uOldLine;
	szText    = szOldText;
	LocalFree(lpbyBuf);

	return pLine;
}

static Line* ParseLines()
{
	Line*   pLine;
	Line*   pFirst = NULL;
	TokenId eToken;
	UINT    nLevel = 0;

	while (eToken = Lex(LEX_COMMAND))
	{
		if (IsBlock(eToken)) nLevel++;
		if (eToken == TOK_INCLUDE)
		{
			eToken = Lex(LEX_PARAM);
			if (eToken != TOK_STRING)
			{
				AddToLog("Include: string expected as parameter.");
				goto abort;
			}
			if (pFirst)
			{
				pLine->pNext = IncludeLines(szLexString);
			}
			else
			{
				pLine = pFirst = IncludeLines(szLexString);
			}
			while (pLine->pNext) pLine=pLine->pNext;
			continue;
		}
		if (eToken == TOK_END)
		{
			if (nLevel)
			{
				nLevel--;
			}
			else
			{
				if (pLine) pLine->pNext = NULL;
				return pFirst;
			}
		}
		if (pFirst)
		{
			pLine = pLine->pNext = ParseLine(eToken);
		}
		else
		{
			pLine = pFirst = ParseLine(eToken);
		}
	}
	if (nLinesIncludeLevel)
	{
		if (pLine) pLine->pNext = NULL;
		return pFirst;
	}	
	AddToLog("Open block.");
abort:
	if (pFirst)
	{
		pLine->pNext = NULL;
		FreeLines(pFirst);
	}
	FatalError();
	return NULL;
}

static Block* ParseBlock(TokenId eType)
{
	UINT    u1;
	Block*  pBlock;
	TokenId eToken;

	nLinesIncludeLevel = 0;

	pBlock = LocalAlloc(LPTR,sizeof(Block));
	pBlock->eType = eType;

	u1 = 0;
	while (pLexToken[u1].nLen)
	{
		if (pLexToken[u1].eId == eType) break;
		u1++;
	}
	if (pLexToken[u1].nParams)
	{
		eToken = Lex(LEX_COMMAND);
		switch (eToken)
		{
		case TOK_NONE:
			AddToLog("Open Block at End Of File.");
			LocalFree(pBlock);
			FatalError();
			return NULL;
		case TOK_INTEGER:
			if ((pLexToken[u1].nParams&7)!=TYPE_INTEGER)
			{
				AddToLog("Wrong block argument.");
				LocalFree(pBlock);
				FatalError();
				return NULL;
			}
			pBlock->nId = nLexInteger;
			break;
		default:
			AddToLog("Wrong block argument.");
			LocalFree(pBlock);
			FatalError();
			return NULL;
		}
	}

	pBlock->pFirstLine = ParseLines();

	return pBlock;
}

static Block* IncludeBlocks(LPCSTR szFilename)
{
	HANDLE hFile;
	DWORD  dwFileSizeLow;
	DWORD  dwFileSizeHigh;
	DWORD  dwBytesRead;
	LPBYTE lpbyBuf;
	UINT   uOldLine;
	LPSTR  szOldText;
	Block* pFirst;

	SetCurrentDirectory(szEmu48Directory);
	hFile = CreateFile(szFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
	SetCurrentDirectory(szCurrentDirectory);
	if (hFile == INVALID_HANDLE_VALUE)
	{
		PrintfToLog("Error while opening include file %s.", szFilename);
		FatalError();
		return NULL;
	}
	dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
	if (dwFileSizeHigh != 0)
	{ // file is too large.
		AddToLog("File is too large.");
		CloseHandle(hFile);
		FatalError();
		return NULL;
	}
	lpbyBuf = (LPBYTE)LocalAlloc(0,dwFileSizeLow+1);
	if (lpbyBuf == NULL)
	{
		PrintfToLog("Cannot allocate %i bytes.", dwFileSizeLow+1);
		CloseHandle(hFile);
		FatalError();
		return NULL;
	}
	ReadFile(hFile, lpbyBuf, dwFileSizeLow, &dwBytesRead, NULL);
	CloseHandle(hFile);
	lpbyBuf[dwFileSizeLow] = 0;

	uOldLine  = nLexLine;
	szOldText = szText;

	nBlocksIncludeLevel++;
	PrintfToLog("b%i:Including %s", nBlocksIncludeLevel, szFilename);
	InitLex(lpbyBuf);
	pFirst = ParseBlocks();
	CleanLex();
	nBlocksIncludeLevel--;

	nLexLine  = uOldLine;
	szText    = szOldText;
	LocalFree(lpbyBuf);

	return pFirst;
}

static Block* ParseBlocks()
{
	TokenId eToken;
	Block* pFirst = NULL;
	Block* pBlock;

	while ((eToken=Lex(LEX_BLOCK))!=TOK_NONE)
	{
		if (eToken == TOK_INCLUDE)
		{
			eToken = Lex(LEX_PARAM);
			if (eToken != TOK_STRING)
			{
				AddToLog("Include: string expected as parameter.");
				goto abort;
			}
			if (pFirst)
				pBlock = pBlock->pNext = IncludeBlocks(szLexString);
			else
				pBlock = pFirst = IncludeBlocks(szLexString);
			while (pBlock->pNext) pBlock=pBlock->pNext;
			continue;
		}
		if (pFirst)
			pBlock = pBlock->pNext = ParseBlock(eToken);
		else
			pBlock = pFirst = ParseBlock(eToken);
		if (pBlock == NULL)
		{
			AddToLog("Invalid block.");
			goto abort;
		}
	}
	if (pFirst) pBlock->pNext = NULL;
	return pFirst;
abort:
	if (pFirst) FreeBlocks(pFirst);
	FatalError();
	return NULL;
}



//################
//#
//#    Initialization Phase
//#
//################

static VOID InitGlobal(Block* pBlock)
{
	Line* pLine = pBlock->pFirstLine;
	while (pLine)
	{
		switch (pLine->eCommand)
		{
		case TOK_TITLE:
			PrintfToLog("Title: %s", (LPSTR)pLine->nParam[0]);
			break;
		case TOK_AUTHOR:
			PrintfToLog("Author: %s", (LPSTR)pLine->nParam[0]);
			break;
		case TOK_PRINT:
			AddToLog((LPSTR)pLine->nParam[0]);
			break;
		case TOK_MODEL:
			cCurrentRomType = ((LPSTR)pLine->nParam[0])[0];
			PrintfToLog("Calculator Model : %c", cCurrentRomType);
			break;
		case TOK_DEBUG:
			bDebug = pLine->nParam[0]&1;
			PrintfToLog("Debug %s", bDebug?"On":"Off");
			break;
		case TOK_ROM:
			if (pbyRom != NULL)
			{
				PrintfToLog("Rom %s Ignored.", (LPSTR)pLine->nParam[0]);
				AddToLog("Please put only one Rom command in the Global block.");
				break;
			}
			if (!MapRom((LPSTR)pLine->nParam[0]))
			{
				PrintfToLog("Cannot open Rom %s", (LPSTR)pLine->nParam[0]);
				break;
			}
			PrintfToLog("Rom %s Loaded.", (LPSTR)pLine->nParam[0]);
			break;
		case TOK_PATCH:
			if (pbyRom == NULL)
			{
				PrintfToLog("Patch %s ignored.", (LPSTR)pLine->nParam[0]);
				AddToLog("Please put the Rom command before any Patch.");
				break;
			}
			if (PatchRom((LPSTR)pLine->nParam[0]) == TRUE)
				PrintfToLog("Patch %s Loaded", (LPSTR)pLine->nParam[0]);
			else
				PrintfToLog("Patch %s is Wrong or Missing", (LPSTR)pLine->nParam[0]);
			break;
		case TOK_BITMAP:
			if (hMainDC != NULL)
			{
				PrintfToLog("Bitmap %s Ignored.", (LPSTR)pLine->nParam[0]);
				AddToLog("Please put only one Bitmap command in the Global block.");
				break;
			}
			if (!CreateMainBitmap((LPSTR)pLine->nParam[0]))
			{
				PrintfToLog("Cannot Load Bitmap %s.", (LPSTR)pLine->nParam[0]);
				break;
			}
			PrintfToLog("Bitmap %s Loaded.", (LPSTR)pLine->nParam[0]);
			break;
		default:
			PrintfToLog("Command %s Ignored in Block %s", GetStringOf(pLine->eCommand), GetStringOf(pBlock->eType));
		}
		pLine = pLine->pNext;
	}
	return;
}

static Line* InitBackground(Block* pBlock)
{
	Line* pLine = pBlock->pFirstLine;
	while (pLine)
	{
		switch (pLine->eCommand)
		{
		case TOK_OFFSET:
			nBackgroundX = pLine->nParam[0];
			nBackgroundY = pLine->nParam[1];
			break;
		case TOK_SIZE:
			nBackgroundW = pLine->nParam[0];
			nBackgroundH = pLine->nParam[1];
			break;
		case TOK_END:
			return pLine;
		default:
			PrintfToLog("Command %s Ignored in Block %s", GetStringOf(pLine->eCommand), GetStringOf(pBlock->eType));
		}
		pLine = pLine->pNext;
	}
	return NULL;
}

static Line* InitLcd(Block* pBlock)
{
	Line* pLine = pBlock->pFirstLine;
	while (pLine)
	{
		switch (pLine->eCommand)
		{
		case TOK_OFFSET:
			nLcdX = pLine->nParam[0];
			nLcdY = pLine->nParam[1];
			break;
		case TOK_ZOOM:
			bLcdDoubled = (pLine->nParam[0]==2);
			break;
		case TOK_COLOR:
			SetLcdColor(pLine->nParam[0],pLine->nParam[1],pLine->nParam[2],pLine->nParam[3]);
			break;
		case TOK_END:
			return pLine;
		default:
			PrintfToLog("Command %s Ignored in Block %s", GetStringOf(pLine->eCommand), GetStringOf(pBlock->eType));
		}
		pLine = pLine->pNext;
	}
	return NULL;
}

static Line* InitAnnunciator(Block* pBlock)
{
	Line* pLine = pBlock->pFirstLine;
	UINT nId = pBlock->nId-1;
	if (nId >= 6)
	{
		PrintfToLog("Wrong Annunciator Id %i", nId);
		return NULL;
	}
	nAnnunciators++;
	while (pLine)
	{
		switch (pLine->eCommand)
		{
		case TOK_OFFSET:
			pAnnunciator[nId].nOx = pLine->nParam[0];
			pAnnunciator[nId].nOy = pLine->nParam[1];
			break;
		case TOK_DOWN:
			pAnnunciator[nId].nDx = pLine->nParam[0];
			pAnnunciator[nId].nDy = pLine->nParam[1];
			break;
		case TOK_SIZE:
			pAnnunciator[nId].nCx = pLine->nParam[0];
			pAnnunciator[nId].nCy = pLine->nParam[1];
			break;
		case TOK_END:
			return pLine;
		default:
			PrintfToLog("Command %s Ignored in Block %s", GetStringOf(pLine->eCommand), GetStringOf(pBlock->eType));
		}
		pLine = pLine->pNext;
	}
	return NULL;
}

static VOID InitButton(Block* pBlock)
{
	Line* pLine = pBlock->pFirstLine;
	UINT nLevel = 0;
	if (nButtons>=256)
	{
		AddToLog("Only the first 256 buttons will be defined.");
		return;
	}
	pButton[nButtons].nId = pBlock->nId;
	pButton[nButtons].bDown = FALSE;
	pButton[nButtons].nType = 0; // default : user defined button
	while (pLine)
	{
		if (nLevel)
		{
			if (pLine->eCommand == TOK_END) nLevel--;
			pLine = pLine->pNext;
			continue;
		}
		if (IsBlock(pLine->eCommand)) nLevel++;
		switch (pLine->eCommand)
		{
		case TOK_TYPE:
			pButton[nButtons].nType = pLine->nParam[0];
			break;
		case TOK_OFFSET:
			pButton[nButtons].nOx = pLine->nParam[0];
			pButton[nButtons].nOy = pLine->nParam[1];
			break;
		case TOK_DOWN:
			pButton[nButtons].nDx = pLine->nParam[0];
			pButton[nButtons].nDy = pLine->nParam[1];
			break;
		case TOK_SIZE:
			pButton[nButtons].nCx = pLine->nParam[0];
			pButton[nButtons].nCy = pLine->nParam[1];
			break;
		case TOK_OUTIN:
			pButton[nButtons].nOut = pLine->nParam[0];
			pButton[nButtons].nIn  = pLine->nParam[1];
			break;
		case TOK_ONDOWN:
			pButton[nButtons].pOnDown = pLine;
			break;
		case TOK_ONUP:
			pButton[nButtons].pOnUp = pLine;
			break;
		case TOK_NOHOLD:
			pButton[nButtons].dwFlags &= ~(BUTTON_VIRTUAL);
			pButton[nButtons].dwFlags |= BUTTON_NOHOLD;
			break;
		case TOK_VIRTUAL:
			pButton[nButtons].dwFlags &= ~(BUTTON_NOHOLD);
			pButton[nButtons].dwFlags |= BUTTON_VIRTUAL;
			break;
		default:
			PrintfToLog("Command %s Ignored in Block %s %i", GetStringOf(pLine->eCommand), GetStringOf(pBlock->eType), pBlock->nId);
		}
		pLine = pLine->pNext;
	}
	if (nLevel)
		PrintfToLog("%i Open Block(s) in Block %s %i", nLevel, GetStringOf(pBlock->eType), pBlock->nId);
	nButtons++;
	return;
}



//################
//#
//#    Execution
//#
//################

static Line* SkipLines(Line* pLine, TokenId eCommand)
{
	UINT nLevel = 0;
	while (pLine)
	{
		if (IsBlock(pLine->eCommand)) nLevel++;
		if (pLine->eCommand==eCommand)
		{
			if (nLevel == 0) return pLine->pNext;
		}
		if (pLine->eCommand == TOK_END)
		{
			if (nLevel)
				nLevel--;
			else
				return NULL;
		}
		pLine = pLine->pNext;
	}
	return pLine;
}

static Line* If(Line* pLine, BOOL bCondition)
{
	UINT nLevel = 0;
	pLine = pLine->pNext;
	if (bCondition)
	{
		while (pLine)
		{
			if (pLine->eCommand == TOK_END)
			{
				pLine = pLine->pNext;
				break;
			}
			if (pLine->eCommand == TOK_ELSE)
			{
				pLine = SkipLines(pLine, TOK_END);
				break;
			}
			pLine = RunLine(pLine);
		}
	}
	else
	{
		pLine = SkipLines(pLine, TOK_ELSE);
		while (pLine)
		{
			if (pLine->eCommand == TOK_END)
			{
				pLine = pLine->pNext;
				break;
			}
			pLine = RunLine(pLine);
		}
	}
	return pLine;
}

static Line* RunLine(Line* pLine)
{
	switch (pLine->eCommand)
	{
	case TOK_MAP:
		if (byVKeyMap[pLine->nParam[0]&0xFF]&1)
			PressButtonById(pLine->nParam[1]);
		else
			ReleaseButtonById(pLine->nParam[1]);
		break;
	case TOK_PRESS:
		PressButtonById(pLine->nParam[0]);
		break;
	case TOK_RELEASE:
		ReleaseButtonById(pLine->nParam[0]);
		break;
	case TOK_MENUITEM:
		PostMessage(hWnd, WM_COMMAND, 0x19C40+(pLine->nParam[0]&0xFF), 0);
		break;
	case TOK_SETFLAG:
		nKMLFlags |= 1<<(pLine->nParam[0]&0x1F);
		break;
	case TOK_RESETFLAG:
		nKMLFlags &= ~(1<<(pLine->nParam[0]&0x1F));
		break;
	case TOK_NOTFLAG:
		nKMLFlags ^= 1<<(pLine->nParam[0]&0x1F);
		break;
	case TOK_IFPRESSED:
		return If(pLine,byVKeyMap[pLine->nParam[0]&0xFF]);
		break;
	case TOK_IFFLAG:
		return If(pLine,(nKMLFlags>>(pLine->nParam[0]&0x1F))&1);
	}
	return pLine->pNext;
}



//################
//#
//#    Clean Up
//#
//################

static VOID FreeLines(Line* pLine)
{
	while (pLine)
	{
		Line* pThisLine = pLine;
		UINT i = 0;
		DWORD nParams;
		while (pLexToken[i].nLen)
		{
			if (pLexToken[i].eId == pLine->eCommand) break;
			i++;
		}
		nParams = pLexToken[i].nParams;
		i = 0;
		while ((nParams&7))
		{
			if ((nParams&7) == TYPE_STRING)
			{
				LocalFree((LPVOID)pLine->nParam[i++]);
			}
			nParams >>= 3;
		}
		pLine = pLine->pNext;
		LocalFree(pThisLine);
	}
	return;
}

VOID FreeBlocks(Block* pBlock)
{
	while (pBlock)
	{
		Block* pThisBlock = pBlock;
		pBlock = pBlock->pNext;
		FreeLines(pThisBlock->pFirstLine);
		LocalFree(pThisBlock);
	}
	return;
}

VOID KillKML()
{
	if ((nState==0)||(nState==3))
	{
		AbortMessage("FATAL: KillKML while emulator is running !!!");
		SwitchToState(2);
		DestroyWindow(hWnd);
	}
	UnmapRom();
	DestroyLcdBitmap();
	DestroyMainBitmap();
	if (hPalette)
	{
		DeleteObject(hPalette);
		hPalette = NULL;
	}
	bClicking = FALSE;
	uButtonClicked = 0;
	FreeBlocks(pKml);
	pKml = NULL;
	nButtons = 0;
	nScancodes = 0;
	nAnnunciators = 0;
	FillMemory(pButton, 256*sizeof(Button), 0);
	FillMemory(pAnnunciator, 6*sizeof(Annunciator), 0);
	FillMemory(pVKey, 256*sizeof(Block*), 0);
	ClearLog();
	nBackgroundW = 256;
	nBackgroundH = 0;
	UpdateWindowStatus();
	ResizeWindow();

	return;
}



//################
//#
//#    Extract Keyword's Parameters
//#
//################

static LPSTR GetStringParam(Block* pBlock, TokenId eBlock, TokenId eCommand, UINT nParam)
{
	while (pBlock)
	{
		if (pBlock->eType == eBlock)
		{
			Line* pLine = pBlock->pFirstLine;
			while (pLine)
			{
				if (pLine->eCommand == eCommand)
				{
					return (LPSTR)pLine->nParam[nParam];
				}
				pLine = pLine->pNext;
			}
		}
		pBlock = pBlock->pNext;
	}
	return NULL;
}

static DWORD GetIntegerParam(Block* pBlock, TokenId eBlock, TokenId eCommand, UINT nParam)
{
	while (pBlock)
	{
		if (pBlock->eType == eBlock)
		{
			Line* pLine = pBlock->pFirstLine;
			while (pLine)
			{
				if (pLine->eCommand == eCommand)
				{
					return pLine->nParam[nParam];
				}
				pLine = pLine->pNext;
			}
		}
		pBlock = pBlock->pNext;
	}
	return 0;
}



//################
//#
//#    Buttons
//#
//################

static VOID DrawButton(UINT nId)
{
	UINT x0 = pButton[nId].nOx;
	UINT y0 = pButton[nId].nOy;

	switch (pButton[nId].nType)
	{
	case 0:
		if (pButton[nId].bDown)
		{
			BitBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, hMainDC, pButton[nId].nDx, pButton[nId].nDy, SRCCOPY);
		}
		else
		{
			BitBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, hMainDC, x0, y0, SRCCOPY);
		}
		break;
	case 1:
		if (pButton[nId].bDown)
		{
			UINT x1 = x0+pButton[nId].nCx-1;
			UINT y1 = y0+pButton[nId].nCy-1;
			BitBlt(hWindowDC, x0+3,y0+3,pButton[nId].nCx-5,pButton[nId].nCy-5,hMainDC,x0+2,y0+2,SRCCOPY);
			SelectObject(hWindowDC, GetStockObject(BLACK_PEN));
			MoveToEx(hWindowDC, x0,y0, NULL); LineTo(hWindowDC, x1,y0);
			MoveToEx(hWindowDC, x0,y0, NULL); LineTo(hWindowDC, x0,y1);
			SelectObject(hWindowDC, GetStockObject(WHITE_PEN));
			MoveToEx(hWindowDC, x1,y0, NULL); LineTo(hWindowDC, x1,y1);
			MoveToEx(hWindowDC, x0,y1, NULL); LineTo(hWindowDC, x1+1,y1);
		}
		else
		{
			BitBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, hMainDC, x0, y0, SRCCOPY);
		}
		break;
	case 2:
		break;
	case 3:
		if (pButton[nId].bDown)
		{
			PatBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, DSTINVERT);
		}
		else
		{
			RECT Rect;
			Rect.left = x0;
			Rect.right = x0 + pButton[nId].nCx;
			Rect.top = y0;
			Rect.bottom = y0 + pButton[nId].nCy;
			InvalidateRect(hWnd, &Rect, 0);
		}
		break;
	case 4:
		if (pButton[nId].bDown)
		{
			BitBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, hMainDC, x0, y0, SRCCOPY);
		}
		else
		{
			RECT Rect;
			Rect.left = x0;
			Rect.right = x0 + pButton[nId].nCx;
			Rect.top = y0;
			Rect.bottom = y0 + pButton[nId].nCy;
			InvalidateRect(hWnd, &Rect, 0);
		}
		break;
	case 5:
		if (pButton[nId].bDown)
		{
			BitBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, hMainDC, pButton[nId].nDx, pButton[nId].nDy, SRCCOPY);
		}
		else
		{
			BitBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, hMainDC, x0, y0, SRCCOPY);
		}
		break;
	default:
		if (pButton[nId].bDown)
			PatBlt(hWindowDC, x0,y0, pButton[nId].nCx, pButton[nId].nCy, BLACKNESS);
		else
			BitBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, hMainDC, x0, y0, SRCCOPY);
	}
	return;
}

static VOID PressButton(UINT nId)
{
	pButton[nId].bDown = TRUE;
	DrawButton(nId);
	if (pButton[nId].nIn)
	{
		KeyboardEvent(TRUE,pButton[nId].nOut,pButton[nId].nIn);
	}
	else
	{
		Line* pLine = pButton[nId].pOnDown;
		while ((pLine)&&(pLine->eCommand!=TOK_END))
		{
			pLine = RunLine(pLine);
		}
	}
	return;
}

static VOID ReleaseButton(UINT nId)
{
	pButton[nId].bDown = FALSE;
	DrawButton(nId);
	if (pButton[nId].nIn)
	{
		KeyboardEvent(FALSE,pButton[nId].nOut,pButton[nId].nIn);
	}
	else
	{
		Line* pLine = pButton[nId].pOnUp;
		while ((pLine)&&(pLine->eCommand!=TOK_END))
		{
			pLine = RunLine(pLine);
		}
	}
	return;
}

static VOID PressButtonById(UINT nId)
{
	UINT i;
	for (i=0; i<nButtons; i++)
	{
		if (nId == pButton[i].nId)
		{
			PressButton(i);
			return;
		}
	}
	return;
}

static VOID ReleaseButtonById(UINT nId)
{
	UINT i;
	for (i=0; i<nButtons; i++)
	{
		if (nId == pButton[i].nId)
		{
			ReleaseButton(i);
			return;
		}
	}
	return;
}

VOID RefreshButtons()
{
	UINT i;
	for (i=0; i<nButtons; i++) 
	{
		if (pButton[i].bDown) DrawButton(i);
	}
	return;
}


//################
//#
//#    Annunciators
//#
//################

VOID DrawAnnunciator(UINT nId, BOOL bOn)
{
	nId--;
	if (nId>=6) return;
	if (bOn)
	{
		BitBlt(hWindowDC,
			pAnnunciator[nId].nOx, pAnnunciator[nId].nOy,
			pAnnunciator[nId].nCx, pAnnunciator[nId].nCy,
			hMainDC,
			pAnnunciator[nId].nDx, pAnnunciator[nId].nDy,
			SRCCOPY);
	}
	else
	{
		BitBlt(hWindowDC,
			pAnnunciator[nId].nOx, pAnnunciator[nId].nOy,
			pAnnunciator[nId].nCx, pAnnunciator[nId].nCy,
			hMainDC,
			pAnnunciator[nId].nOx, pAnnunciator[nId].nOy,
			SRCCOPY);
	}
	return;
}



//################
//#
//#    Mouse
//#
//################

static BOOL ClipButton(UINT x, UINT y, UINT nId)
{
	return (pButton[nId].nOx<=x)
		&& (pButton[nId].nOy<=y)
		&&(x<(pButton[nId].nOx+pButton[nId].nCx))
		&&(y<(pButton[nId].nOy+pButton[nId].nCy));
}

VOID MouseButtonDownAt(UINT nFlags, DWORD x, DWORD y)
{
	UINT i;
	for (i=0; i<nButtons; i++)
	{
		if (ClipButton(x,y,i))
		{
			if (pButton[i].dwFlags&BUTTON_NOHOLD)
			{
				bClicking = TRUE;
				uButtonClicked = i;
				pButton[i].bDown = TRUE;
				DrawButton(i);
				return;
			}
			if (pButton[i].dwFlags&BUTTON_VIRTUAL)
			{
				bClicking = TRUE;
				uButtonClicked = i;
			}
			PressButton(i);
			return;
		}
	}
 	UNREFERENCED_PARAMETER(nFlags);
}

VOID MouseButtonUpAt(UINT nFlags, DWORD x, DWORD y)
{
	UINT i;
	for (i=0; i<nButtons; i++)
	{
		if (ClipButton(x,y,i))
		{
			if ((bClicking)&&(uButtonClicked != i)) break;
			ReleaseButton(i);
			break;
		}
	}
	bClicking = FALSE;
	uButtonClicked = 0;
	return;
 	UNREFERENCED_PARAMETER(nFlags);
}

VOID MouseMovesTo(UINT nFlags, DWORD x, DWORD y)
{
	if (!bClicking) return;
	if (!(nFlags&MK_LBUTTON)) return;
	if (pButton[uButtonClicked].dwFlags&BUTTON_NOHOLD)
	{
		if (ClipButton(x,y, uButtonClicked) != pButton[uButtonClicked].bDown)
		{
			pButton[uButtonClicked].bDown = !pButton[uButtonClicked].bDown;
			DrawButton(uButtonClicked);
		}
		return;
	}
	if (pButton[uButtonClicked].dwFlags&BUTTON_VIRTUAL)
	{
		if (!ClipButton(x,y, uButtonClicked))
		{
			ReleaseButton(uButtonClicked);
			bClicking = FALSE;
			uButtonClicked = 0;
		}
		return;
	}
	return;
}



//################
//#
//#    Keyboard
//#
//################

VOID RunKey(BYTE nId, BOOL bPressed)
{
	if (pVKey[nId])
	{
		Line* pLine = pVKey[nId]->pFirstLine;
		byVKeyMap[nId] = bPressed;
		while (pLine) pLine = RunLine(pLine);
	}
	else
	{
		if (bDebug&&bPressed)
		{
			CHAR szTemp[128];
			wsprintf(szTemp,"Scancode %i",nId);
			InfoMessage(szTemp);
		}
	}
	return;
}



//################
//#
//#    Load and Initialize Script
//#
//################

static Block* LoadKMLGlobal(LPCSTR szFilename)
{
	HANDLE hFile;
	DWORD dwFileSizeLow;
	DWORD dwFileSizeHigh;
	DWORD lBytesRead;
	LPBYTE lpBuf;
	Block* pBlock;
	DWORD  eToken;

	SetCurrentDirectory(szEmu48Directory);
	hFile = CreateFile(szFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
	SetCurrentDirectory(szCurrentDirectory);
	if (hFile == INVALID_HANDLE_VALUE) return NULL;
	dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
	if (dwFileSizeHigh != 0)
	{ // file is too large.
		CloseHandle(hFile);
		return NULL;
	}
	lpBuf = (LPBYTE)LocalAlloc(0,dwFileSizeLow+1);
	if (lpBuf == NULL)
	{
		CloseHandle(hFile);
		return NULL;
	}
	ReadFile(hFile, lpBuf, dwFileSizeLow, &lBytesRead, NULL);
	CloseHandle(hFile);
	lpBuf[dwFileSizeLow] = 0;

	InitLex(lpBuf);
	pBlock = NULL;
	eToken = Lex(LEX_BLOCK);
	if (eToken == TOK_GLOBAL)
	{
		pBlock = ParseBlock(eToken);
		if (pBlock) pBlock->pNext = NULL;
	}
	CleanLex();
	ClearLog();
	LocalFree(lpBuf);
	return pBlock;
}


BOOL InitKML(LPCSTR szFilename, BOOL bNoLog)
{
	HANDLE hFile;
	DWORD dwFileSizeLow;
	DWORD dwFileSizeHigh;
	DWORD lBytesRead;
	LPBYTE lpBuf;
	Block* pBlock;
	BOOL bOk;
	BYTE bySum = 0;

	KillKML();

	nBlocksIncludeLevel = 0;
	PrintfToLog("Reading %s", szFilename);
	SetCurrentDirectory(szEmu48Directory);
	hFile = CreateFile(szFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
	SetCurrentDirectory(szCurrentDirectory);
	if (hFile == INVALID_HANDLE_VALUE)
	{
		AddToLog("Error while opening the file.");
		goto quit;
	}
	dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
	if (dwFileSizeHigh != 0)
	{ // file is too large.
		AddToLog("File is too large.");
		CloseHandle(hFile);
		goto quit;
	}
	lpBuf = (LPBYTE)LocalAlloc(0,dwFileSizeLow+1);
	if (lpBuf == NULL)
	{
		PrintfToLog("Cannot allocate %i bytes.", dwFileSizeLow+1);
		CloseHandle(hFile);
		goto quit;
	}
	ReadFile(hFile, lpBuf, dwFileSizeLow, &lBytesRead, NULL);
	CloseHandle(hFile);
	lpBuf[dwFileSizeLow] = 0;

	InitLex(lpBuf);
	pKml = ParseBlocks();
	CleanLex();

	LocalFree(lpBuf);
	if (pKml == NULL) goto quit;

	pBlock = pKml;
	while (pBlock)
	{
		switch (pBlock->eType)
		{
		case TOK_BUTTON:
			InitButton(pBlock);
			break;
		case TOK_SCANCODE:
			nScancodes++;
			pVKey[pBlock->nId] = pBlock;
			break;
		case TOK_ANNUNCIATOR:
			InitAnnunciator(pBlock);
			break;
		case TOK_GLOBAL:
			InitGlobal(pBlock);
			break;
		case TOK_LCD:
			InitLcd(pBlock);
			break;
		case TOK_BACKGROUND:
			InitBackground(pBlock);
			break;
		default:
			PrintfToLog("Block %s Ignored.", GetStringOf(pBlock->eType));
			pBlock = pBlock->pNext;
		}
		pBlock = pBlock->pNext;
	}
		
	if (cCurrentRomType == 0)
	{
		AddToLog("This KML Script doesn't specify the ROM Type.");
		goto quit;
	}
	if (pbyRom == NULL)
	{
		AddToLog("This KML Script doesn't specify the ROM to use, or the ROM could not be loaded.");
		goto quit;
	}
	CreateLcdBitmap();
	Map(0x00,0xFF);

	PrintfToLog("%i Buttons Defined", nButtons);
	PrintfToLog("%i Scancodes Defined", nScancodes);
	PrintfToLog("%i Annunciators Defined", nAnnunciators);

	bOk = TRUE;

quit:
	if (bOk)
	{
		if (!bNoLog)
		{
			AddToLog("Press Ok to Continue.");
			if (bAlwaysDisplayLog&&(!DisplayKMLLog(bOk)))
			{
				KillKML();
				return FALSE;
			}
		}
	}
	else
	{
		AddToLog("Press Cancel to Abort.");
		if (!DisplayKMLLog(bOk))
		{
			KillKML();
			return FALSE;
		}
	}

	ResizeWindow();
	ClearLog();
	return bOk;
}
