/* * 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 = ""; 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 ""; } 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].nLeneCommand = 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=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; ipFirstLine; 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; }