From b00ca49050697c5ddf6e07422fba07ab4aa0ed31 Mon Sep 17 00:00:00 2001 From: watamario15 Date: Sun, 26 Mar 2023 22:59:09 +0900 Subject: [PATCH 1/9] Add layout switching --- bf.cpp | 110 ++++++++++++++++++----------------- bf.hpp | 20 ++++--- brain.bat | 2 +- brain.sh | 12 +--- main.cpp | 36 ++++++------ resource.h | 5 +- resource.rc | 161 ++++++++++++++++++++++++++-------------------------- ui.cpp | 93 +++++++++++++++++------------- ui.hpp | 5 +- win.bat | 2 +- win.sh | 18 ++---- 11 files changed, 237 insertions(+), 227 deletions(-) diff --git a/bf.cpp b/bf.cpp index 253405d..04c1653 100644 --- a/bf.cpp +++ b/bf.cpp @@ -1,11 +1,10 @@ -#if defined UNDER_CE || (defined _MSC_VER && _MSC_VER < 1800) -#define PRIu64 "I64u" -#else -#define PRIu64 "zu" -#endif - #include "bf.hpp" +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include + #include #include #include @@ -63,12 +62,10 @@ enum Brainfuck::result_t Brainfuck::next(unsigned char *_output, bool *_didOutpu ++memIndex; if (memory.size() == memIndex) memory.push_back(0); } else { - char errorMsg[128]; - sprintf(errorMsg, - "%" PRIu64 - ": Instruction '>' used when the memory pointer is std::vector::max_size.", - progIndex); - throw std::invalid_argument(errorMsg); + wsprintfW(lastError, + L"%lu: Instruction '>' used when the memory pointer is std::vector::max_size.", + (unsigned long)progIndex); + return RESULT_ERR; } break; @@ -76,10 +73,9 @@ enum Brainfuck::result_t Brainfuck::next(unsigned char *_output, bool *_didOutpu if (memIndex != 0) { --memIndex; } else { - char errorMsg[128]; - sprintf(errorMsg, "%" PRIu64 ": Instruction '<' used when the memory pointer is 0.", - progIndex); - throw std::invalid_argument(errorMsg); + wsprintfW(lastError, L"%lu: Instruction '<' used when the memory pointer is 0.", + (unsigned long)progIndex); + return RESULT_ERR; } break; @@ -91,19 +87,17 @@ enum Brainfuck::result_t Brainfuck::next(unsigned char *_output, bool *_didOutpu if (memory[memIndex] != 0x7F) { ++memory[memIndex]; } else { - char errorMsg[128]; - sprintf(errorMsg, "%" PRIu64 ": Instruction '+' used when the pointed memory is 127.", - progIndex); - throw std::invalid_argument(errorMsg); + wsprintfW(lastError, L"%lu: Instruction '+' used when the pointed memory is 127.", + (unsigned long)progIndex); + return RESULT_ERR; } } else { if (memory[memIndex] != 0xFF) { ++memory[memIndex]; } else { - char errorMsg[128]; - sprintf(errorMsg, "%" PRIu64 ": Instruction '+' used when the pointed memory is 255.", - progIndex); - throw std::invalid_argument(errorMsg); + wsprintfW(lastError, L"%lu: Instruction '+' used when the pointed memory is 255.", + (unsigned long)progIndex); + return RESULT_ERR; } } break; @@ -116,19 +110,17 @@ enum Brainfuck::result_t Brainfuck::next(unsigned char *_output, bool *_didOutpu if (memory[memIndex] != 0x80) { --memory[memIndex]; } else { - char errorMsg[128]; - sprintf(errorMsg, "%" PRIu64 ": Instruction '-' used when the pointed memory is -128.", - progIndex); - throw std::invalid_argument(errorMsg); + wsprintfW(lastError, L"%lu: Instruction '-' used when the pointed memory is -128.", + (unsigned long)progIndex); + return RESULT_ERR; } } else { if (memory[memIndex] != 0x00) { --memory[memIndex]; } else { - char errorMsg[128]; - sprintf(errorMsg, "%" PRIu64 ": Instruction '-' used when the pointed memory is 0.", - progIndex); - throw std::invalid_argument(errorMsg); + wsprintfW(lastError, L"%lu: Instruction '-' used when the pointed memory is 0.", + (unsigned long)progIndex); + return RESULT_ERR; } } break; @@ -145,10 +137,9 @@ enum Brainfuck::result_t Brainfuck::next(unsigned char *_output, bool *_didOutpu } else if (noInput == NOINPUT_FF) { memory[memIndex] = 255; } else { - char errorMsg[128]; - sprintf(errorMsg, "%" PRIu64 ": Instruction ',' used when the input stream is empty.", - progIndex); - throw std::invalid_argument(errorMsg); + wsprintfW(lastError, L"%lu: Instruction ',' used when the input stream is empty.", + (unsigned long)progIndex); + return RESULT_ERR; } } else { memory[memIndex] = input[inIndex]; @@ -158,46 +149,53 @@ enum Brainfuck::result_t Brainfuck::next(unsigned char *_output, bool *_didOutpu case L'[': if (memory[memIndex] == 0) { - size_t bracketIndex = progIndex++, ketCnt = 0; - while (progIndex < progLen) { - if (program[progIndex] == L']') { + if (progIndex == progLen - 1) { + wsprintfW(lastError, L"%lu: No matching closing bracket.", (unsigned long)progIndex); + return RESULT_ERR; + } + size_t nextIndex = progIndex + 1, ketCnt = 0; + while (true) { + if (program[nextIndex] == L']') { if (ketCnt == 0) { break; // match } else { --ketCnt; // recursive bracket } } - if (program[progIndex] == L'[') ketCnt++; - ++progIndex; - } - if (progIndex >= progLen) { - char errorMsg[128]; - sprintf(errorMsg, "%" PRIu64 ": No matching closing bracket.", bracketIndex); - throw std::invalid_argument(errorMsg); + if (program[nextIndex] == L'[') ketCnt++; + if (nextIndex == progLen - 1) { + wsprintfW(lastError, L"%lu: No matching closing bracket.", (unsigned long)progIndex); + return RESULT_ERR; + } + ++nextIndex; } + progIndex = nextIndex; } break; case L']': if (memory[memIndex] != 0) { - size_t bracketIndex = progIndex--, braCnt = 0; + if (progIndex == 0) { + wsprintfW(lastError, L"%lu: No matching opening bracket.", (unsigned long)progIndex); + return RESULT_ERR; + } + size_t nextIndex = progIndex - 1, braCnt = 0; while (true) { - if (program[progIndex] == L'[') { + if (program[nextIndex] == L'[') { if (braCnt == 0) { break; // match } else { --braCnt; // recursive bracket } } - if (program[progIndex] == L']') braCnt++; - if (progIndex == 0) break; - --progIndex; - } - if (progIndex <= 0 && (program[progIndex] != L'[' || braCnt != 0)) { - char errorMsg[128]; - sprintf(errorMsg, "%" PRIu64 ": No matching opening bracket.", bracketIndex); - throw std::invalid_argument(errorMsg); + if (program[nextIndex] == L']') braCnt++; + if (nextIndex == 0) { + wsprintfW(lastError, L"%lu: No matching opening bracket.", (unsigned long)progIndex); + return RESULT_ERR; + } + --nextIndex; } + progIndex = nextIndex; } break; diff --git a/bf.hpp b/bf.hpp index 4105c0f..556a041 100644 --- a/bf.hpp +++ b/bf.hpp @@ -14,7 +14,7 @@ class Brainfuck { enum noinput_t { NOINPUT_ERROR, NOINPUT_ZERO, NOINPUT_FF }; // Execution result returned from the next() function. - enum result_t { RESULT_RUN, RESULT_BREAK, RESULT_FIN }; + enum result_t { RESULT_RUN, RESULT_BREAK, RESULT_FIN, RESULT_ERR }; // Initializes the internal state. Brainfuck() @@ -24,7 +24,6 @@ class Brainfuck { signedness(true), debug(false), noInput(NOINPUT_ZERO) { - memory.reserve(1000); reset(); } @@ -37,7 +36,6 @@ class Brainfuck { signedness(true), debug(false), noInput(NOINPUT_ZERO) { - memory.reserve(30000); reset(_progLen, _program, _inLen, _input); } @@ -51,15 +49,15 @@ class Brainfuck { // Change implementation-defined behaviors. // When wrapInt is false, `next` throws an exception on an overflow/underflow. // When wrapInt is true, signedness doen't have any effect. - // When debug is true, breakpoint instruction (@) is enabled. - // Default options are, zero for no input, wrap around integer, signed integer (no effect here), - // and no debug. + // When debug is true, breakpoint instruction ("@") is enabled. + // Default options are, zero for no input, wrap around integer, signed integer (no effects in + // this case), and no debug. void setBehavior(enum noinput_t _noInput = NOINPUT_ZERO, bool _wrapInt = true, bool _signedness = true, bool _debug = false); // Executes the next code, and writes its output on `output` if any. - // A return value is the result of an execution, which is running, breakpoint, and finished. - // Throws `std::invalid_argument` when encounters an invalid Brainfuck code. + // A return value is the result of an execution, which is running, breakpoint, finished, and + // error. enum result_t next(unsigned char *_output, bool *_didOutput); // Returns the memory. The returned content becomes invalid on reset/next. @@ -71,13 +69,17 @@ class Brainfuck { // Returns the program pointer. size_t getProgPtr() { return progIndex; } + // Returns the last error. + const wchar_t *getLastError() { return lastError; } + private: - // All initializations are in constructor, as old C++ doesn't allow it on a declaration. + // All initializations are in constructor, as old C++ doesn't allow them on the declaration. const wchar_t *program; // Program const unsigned char *input; // Input stream std::vector memory; // Memory size_t memIndex, progIndex, inIndex, progLen, inLen; bool wrapInt, signedness, debug; enum noinput_t noInput; + wchar_t lastError[128]; }; #endif diff --git a/brain.bat b/brain.bat index 633e8f6..d7ed74b 100644 --- a/brain.bat +++ b/brain.bat @@ -6,7 +6,7 @@ rem You can remove this line if you add required CeGCC paths to your system. set PATH=C:\cegcc\main\bin;C:\cegcc\main\libexec\gcc\arm-mingw32ce\9.3.0;C:\cegcc\cygwin echo "*** Compiling C++ sources..." -g++ -Wall -Wextra -O3 -std=c++17 -c *.cpp +g++ -Wall -Wextra -O3 -std=c++98 -c *.cpp echo "*** Compiling res.rc..." windres resource.rc resource.o diff --git a/brain.sh b/brain.sh index 8887e73..d8ac435 100644 --- a/brain.sh +++ b/brain.sh @@ -1,19 +1,13 @@ #!/bin/bash set -e -if [ $# -ne 0 ]; then - GCCPATH=$1/ -else - GCCPATH= -fi - echo "*** Compiling C++ sources..." -${GCCPATH}arm-mingw32ce-g++ -Wall -Wextra -O3 -std=c++17 -march=armv5tej -mcpu=arm926ej-s -c *.cpp +arm-mingw32ce-g++ -Wall -Wextra -O3 -std=c++98 -march=armv5tej -mcpu=arm926ej-s -c ./*.cpp echo "*** Compiling res.rc..." -${GCCPATH}arm-mingw32ce-windres resource.rc resource.o +arm-mingw32ce-windres resource.rc resource.o echo "*** Linking..." -${GCCPATH}arm-mingw32ce-g++ *.o -static -s -lcommctrl -lcommdlg -lmmtimer -o AppMain_.exe +arm-mingw32ce-g++ ./*.o -static -s -lcommctrl -lcommdlg -lmmtimer -o AppMain_.exe echo "OK" diff --git a/main.cpp b/main.cpp index 400f14a..9b9fcb3 100644 --- a/main.cpp +++ b/main.cpp @@ -18,7 +18,7 @@ #endif #ifdef UNDER_CE -// Workaround for wrong macro definitions of CeGCC. +// Workaround for wrong macro definitions in CeGCC. #if SW_MAXIMIZE != 12 #undef SW_MAXIMIZE #define SW_MAXIMIZE 12 @@ -43,7 +43,7 @@ #define myCreateThread(lpsa, cbStack, lpStartAddr, lpvThreadParam, fdwCreate, lpIDThread) \ CreateThread(lpsa, cbStack, lpStartAddr, lpvThreadParam, fdwCreate, lpIDThread) -// Return type for thread functions. +// The return type of a thread function. typedef DWORD tret_t; #include @@ -55,7 +55,7 @@ typedef DWORD tret_t; #define myCreateThread(lpsa, cbStack, lpStartAddr, lpvThreadParam, fdwCreate, lpIDThread) \ (HANDLE) _beginthreadex(lpsa, cbStack, lpStartAddr, lpvThreadParam, fdwCreate, lpIDThread) -// Return type for thread functions +// The return type of a thread function. typedef unsigned int tret_t; #endif @@ -157,22 +157,16 @@ static bool bfInit() { static enum Brainfuck::result_t bfNext() { unsigned char output; bool didOutput; - enum Brainfuck::result_t result; g_bf->setBehavior(ui::noInput, ui::wrapInt, ui::signedness, ui::breakpoint); - try { - result = g_bf->next(&output, &didOutput); - } catch (std::invalid_argument const &ex) { + enum Brainfuck::result_t result = g_bf->next(&output, &didOutput); + if (result == Brainfuck::RESULT_ERR) { if (g_timerID) { timeKillEvent(g_timerID); timeEndPeriod(ui::speed); g_timerID = 0; } - int exLen = MultiByteToWideChar(CP_UTF8, 0, ex.what(), -1, (wchar_t *)NULL, 0); - wchar_t *wcException = new wchar_t[exLen]; - MultiByteToWideChar(CP_UTF8, 0, ex.what(), -1, wcException, exLen); - ui::messageBox(ui::hWnd, wcException, L"Error", MB_ICONWARNING); - delete[] wcException; + ui::messageBox(ui::hWnd, g_bf->getLastError(), L"Error", MB_ICONWARNING); result = Brainfuck::RESULT_FIN; } @@ -226,7 +220,7 @@ static enum Brainfuck::result_t bfNext() { } // Executes an Brainfuck program until it completes. -tret_t WINAPI threadRunner(LPVOID lpParameter) { +tret_t WINAPI threadRunner(void *lpParameter) { UNREFERENCED_PARAMETER(lpParameter); HANDLE hEvent = 0; @@ -238,7 +232,7 @@ tret_t WINAPI threadRunner(LPVOID lpParameter) { } if (!g_timerID) { ui::messageBox(ui::hWnd, L"This speed is not supported on your device. Try slowing down.", - L"Error", MB_ICONERROR); + L"Error", MB_ICONERROR); ui::setState(ui::STATE_INIT); PostMessageW(ui::hWnd, WM_APP_THREADEND, 0, 0); return 1; @@ -275,7 +269,7 @@ tret_t WINAPI threadRunner(LPVOID lpParameter) { return 0; } -static LRESULT CALLBACK wndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { +static LRESULT CALLBACK wndProc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam) { static bool didInit = false; static HBRUSH BGDark = CreateSolidBrush(0x3f3936); @@ -425,7 +419,7 @@ static LRESULT CALLBACK wndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar case IDM_FILE_EXIT: SendMessageW(hWnd, WM_CLOSE, 0, 0); break; - + case IDM_EDIT_UNDO: ui::undo(); break; @@ -546,6 +540,10 @@ static LRESULT CALLBACK wndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar ui::switchTheme(); break; + case IDM_OPT_LAYOUT: + ui::switchLayout(); + break; + case IDM_OPT_FONT: ui::chooseFont(); break; @@ -566,7 +564,8 @@ static LRESULT CALLBACK wndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar break; default: - if (LOWORD(wParam) >= IDC_SCRKBD_FIRST && LOWORD(wParam) < IDC_SCRKBD_FIRST + SCRKBD_LEN) { + if (LOWORD(wParam) >= IDC_SCRKBD_FIRST && + LOWORD(wParam) < IDC_SCRKBD_FIRST + SCRKBD_LEN) { ui::onScreenKeyboard(LOWORD(wParam) - IDC_SCRKBD_FIRST); } } @@ -579,7 +578,8 @@ static LRESULT CALLBACK wndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar return 0; } -int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd) { +int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, wchar_t *lpCmdLine, + int nShowCmd) { size_t i; UNREFERENCED_PARAMETER(hPrevInstance); diff --git a/resource.h b/resource.h index 7f6e6be..387b658 100644 --- a/resource.h +++ b/resource.h @@ -57,8 +57,9 @@ #define IDM_OPT_HLTPROG 430 #define IDM_OPT_HLTMEM 440 #define IDM_OPT_DARK 450 -#define IDM_OPT_FONT 460 -#define IDM_OPT_WORDWRAP 470 +#define IDM_OPT_LAYOUT 460 +#define IDM_OPT_FONT 470 +#define IDM_OPT_WORDWRAP 480 #define IDM_ABOUT 500 #endif diff --git a/resource.rc b/resource.rc index b156cdb..b37d74a 100644 --- a/resource.rc +++ b/resource.rc @@ -14,24 +14,24 @@ FILEFLAGS 0 FILEOS VOS_NT_WINDOWS32 FILETYPE VFT_APP { - BLOCK "StringFileInfo" + BLOCK "StringFileInfo" + { + BLOCK "040904b0" { - BLOCK "040904b0" - { - VALUE "CompanyName", VER_STR_COMPANYNAME - VALUE "FileDescription", VER_STR_DESCRIPTION - VALUE "FileVersion", VER_STR_VERSION - VALUE "InternalName", VER_STR_ORIGINALFILENAME - VALUE "LegalCopyright", VER_STR_COPYRIGHT - VALUE "OriginalFileName", VER_STR_ORIGINALFILENAME - VALUE "ProductName", VER_STR_APPNAME - VALUE "ProductVersion", VER_STR_VERSION - } - } - BLOCK "VarFileInfo" - { - VALUE "Translation", 0x409, 1200 + VALUE "CompanyName", VER_STR_COMPANYNAME + VALUE "FileDescription", VER_STR_DESCRIPTION + VALUE "FileVersion", VER_STR_VERSION + VALUE "InternalName", VER_STR_ORIGINALFILENAME + VALUE "LegalCopyright", VER_STR_COPYRIGHT + VALUE "OriginalFileName", VER_STR_ORIGINALFILENAME + VALUE "ProductName", VER_STR_APPNAME + VALUE "ProductVersion", VER_STR_VERSION } + } + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x409, 1200 + } } @@ -48,73 +48,74 @@ icon ICON "app.ico" menu MENU { - POPUP "&File" + POPUP "&File" + { + MENUITEM "&New", IDM_FILE_NEW + MENUITEM "&Open...", IDM_FILE_OPEN + MENUITEM "&Save", IDM_FILE_SAVE + MENUITEM "Save &As...", IDM_FILE_SAVE_AS + MENUITEM "&Exit", IDM_FILE_EXIT + } + POPUP "&Edit" + { + MENUITEM "&Undo", IDM_EDIT_UNDO + MENUITEM "Cu&t", IDM_EDIT_CUT + MENUITEM "&Copy", IDM_EDIT_COPY + MENUITEM "&Paste", IDM_EDIT_PASTE + MENUITEM "Select &All", IDM_EDIT_SELALL + } + POPUP "&Brainfuck" + { + POPUP "&Memory Type" + { + MENUITEM "&Signed (-128 to 127)", IDM_BF_MEMTYPE_SIGNED + MENUITEM "&Unsigned (0 to 255)", IDM_BF_MEMTYPE_UNSIGNED + } + POPUP "&Output Charset" + { + MENUITEM "&ASCII (Real Time Output)", IDM_BF_OUTPUT_ASCII + MENUITEM "&UTF-8 (Bufferred Output)", IDM_BF_OUTPUT_UTF8 + MENUITEM "Shift_&JIS (Bufferred Output)", IDM_BF_OUTPUT_SJIS + MENUITEM "&Hexadecimal (Real Time Output)", IDM_BF_OUTPUT_HEX + } + POPUP "Input &Charset" { - MENUITEM "&New", IDM_FILE_NEW - MENUITEM "&Open...", IDM_FILE_OPEN - MENUITEM "&Save", IDM_FILE_SAVE - MENUITEM "Save &As...", IDM_FILE_SAVE_AS - MENUITEM "&Exit", IDM_FILE_EXIT + MENUITEM "&UTF-8", IDM_BF_INPUT_UTF8 + MENUITEM "Shift_&JIS", IDM_BF_INPUT_SJIS + MENUITEM "&Hexadecimal", IDM_BF_INPUT_HEX } - POPUP "&Edit" + POPUP "&Input Instruction" { - MENUITEM "&Undo", IDM_EDIT_UNDO - MENUITEM "Cu&t", IDM_EDIT_CUT - MENUITEM "&Copy", IDM_EDIT_COPY - MENUITEM "&Paste", IDM_EDIT_PASTE - MENUITEM "Select &All", IDM_EDIT_SELALL + MENUITEM "&Error When No Input", IDM_BF_NOINPUT_ERROR + MENUITEM "&Zero When No Input", IDM_BF_NOINPUT_ZERO + MENUITEM "&FF (-1 or 255) When No Input", IDM_BF_NOINPUT_FF } - POPUP "&Brainfuck" + POPUP "Integer &Overflow" { - POPUP "&Memory type" - { - MENUITEM "&Signed (-128 to 127)", IDM_BF_MEMTYPE_SIGNED - MENUITEM "&Unsigned (0 to 255)", IDM_BF_MEMTYPE_UNSIGNED - } - POPUP "&Output charset" - { - MENUITEM "&ASCII (real time output)", IDM_BF_OUTPUT_ASCII - MENUITEM "&UTF-8 (bufferred output)", IDM_BF_OUTPUT_UTF8 - MENUITEM "Shift_&JIS (bufferred output)", IDM_BF_OUTPUT_SJIS - MENUITEM "&Hexadecimal (real time output)", IDM_BF_OUTPUT_HEX - } - POPUP "Input &charset" - { - MENUITEM "&UTF-8", IDM_BF_INPUT_UTF8 - MENUITEM "Shift_&JIS", IDM_BF_INPUT_SJIS - MENUITEM "&Hexadecimal", IDM_BF_INPUT_HEX - } - POPUP "&Input instruction" - { - MENUITEM "&Error on no input", IDM_BF_NOINPUT_ERROR - MENUITEM "&Zero on no input", IDM_BF_NOINPUT_ZERO - MENUITEM "&FF (-1 or 255) on no input", IDM_BF_NOINPUT_FF - } - POPUP "Integer &overflow" - { - MENUITEM "&Error", IDM_BF_INTOVF_ERROR - MENUITEM "&Wrap around", IDM_BF_INTOVF_WRAPAROUND - } - MENUITEM "&Breakpoint instruction (@)", IDM_BF_BREAKPOINT + MENUITEM "&Error", IDM_BF_INTOVF_ERROR + MENUITEM "&Wrap Around", IDM_BF_INTOVF_WRAPAROUND } - POPUP "&Options" + MENUITEM "&Breakpoint Instruction (@)", IDM_BF_BREAKPOINT + } + POPUP "&Options" + { + POPUP "&Speed" { - POPUP "&Speed" - { - MENUITEM "&Fastest", IDM_OPT_SPEED_FASTEST - MENUITEM "1 ms (&o)", IDM_OPT_SPEED_1MS - MENUITEM "10 ms (&t)", IDM_OPT_SPEED_10MS - MENUITEM "100 ms (&h)", IDM_OPT_SPEED_100MS - } - MENUITEM "Memory &view...", IDM_OPT_MEMVIEW - MENUITEM "&Real time debugging (slow)", IDM_OPT_TRACK - MENUITEM "Highlight &program pointer", IDM_OPT_HLTPROG - MENUITEM "Highlight &memory pointer", IDM_OPT_HLTMEM - MENUITEM "&Dark theme", IDM_OPT_DARK - MENUITEM "&Font...", IDM_OPT_FONT - MENUITEM "&Word wrap", IDM_OPT_WORDWRAP + MENUITEM "&Fastest", IDM_OPT_SPEED_FASTEST + MENUITEM "1 ms (&o)", IDM_OPT_SPEED_1MS + MENUITEM "10 ms (&t)", IDM_OPT_SPEED_10MS + MENUITEM "100 ms (&h)", IDM_OPT_SPEED_100MS } - MENUITEM "&About", IDM_ABOUT + MENUITEM "Memory &View...", IDM_OPT_MEMVIEW + MENUITEM "&Real Time Debugging (Slow)", IDM_OPT_TRACK + MENUITEM "Highlight &Program Pointer", IDM_OPT_HLTPROG + MENUITEM "Highlight &Memory Pointer", IDM_OPT_HLTMEM + MENUITEM "Switch &Theme", IDM_OPT_DARK + MENUITEM "Switch &Layout", IDM_OPT_LAYOUT + MENUITEM "&Font...", IDM_OPT_FONT + MENUITEM "&Word Wrap", IDM_OPT_WORDWRAP + } + MENUITEM "&About", IDM_ABOUT } @@ -124,7 +125,7 @@ menu MENU accel ACCELERATORS { - "^A", IDM_EDIT_SELALL + "^A", IDM_EDIT_SELALL } @@ -143,12 +144,12 @@ FONT 9, "MS Shell Dlg 2" CAPTION "Memory view options" STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU { - LTEXT "First address of the memory view (0-indexed byte)", -1, 8, 8, 112, 28, SS_NOPREFIX + LTEXT "First address of the memory view (0-indexed byte)", -1, 8, 8, 112, 28, SS_NOPREFIX #ifdef UNDER_CE // It already has an OK button on the title bar. - EDITTEXT 3, 32, 40, 64, 14, ES_NUMBER + EDITTEXT 3, 32, 40, 64, 14, ES_NUMBER #else - EDITTEXT 3, 8, 40, 64, 14, ES_NUMBER - DEFPUSHBUTTON "&OK", IDOK, 80, 40, 40, 14 + EDITTEXT 3, 8, 40, 64, 14, ES_NUMBER + DEFPUSHBUTTON "&OK", IDOK, 80, 40, 40, 14 #endif } diff --git a/ui.cpp b/ui.cpp index afc6546..253e41f 100644 --- a/ui.cpp +++ b/ui.cpp @@ -32,12 +32,14 @@ typedef enum MONITOR_DPI_TYPE { // Function pointer type for GetDpiForMonitor API. typedef HRESULT(CALLBACK *GetDpiForMonitor_t)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, - UINT *dpiX, UINT *dpiY); + unsigned int *dpiX, unsigned int *dpiY); // Function pointer type for TaskDialog API. -typedef HRESULT(__stdcall *TaskDialog_t)(HWND hwndOwner, HINSTANCE hInstance, PCWSTR pszWindowTitle, - PCWSTR pszMainInstruction, PCWSTR pszContent, - int dwCommonButtons, PCWSTR pszIcon, int *pnButton); +typedef HRESULT(__stdcall *TaskDialog_t)(HWND hwndOwner, HINSTANCE hInstance, + const wchar_t *pszWindowTitle, + const wchar_t *pszMainInstruction, + const wchar_t *pszContent, int dwCommonButtons, + const wchar_t *pszIcon, int *pnButton); #endif // Makes SetWindowLongW/GetWindowLongW compatible for both 32-bit and 64-bit system. @@ -81,13 +83,14 @@ static int dpi = 96, sysDPI = 96; #endif enum state_t state = STATE_INIT; -bool signedness = true, wrapInt = true, breakpoint = false, debug = true, dark = true; +bool signedness = true, wrapInt = true, breakpoint = false, debug = true, dark = true, + horizontal = false; int speed = 10, outCharSet = IDM_BF_OUTPUT_ASCII, inCharSet = IDM_BF_INPUT_UTF8; enum Brainfuck::noinput_t noInput = Brainfuck::NOINPUT_ZERO; HWND hWnd; HINSTANCE hInst; -// Translates newline characters from CRLF/LF/CR/LFCR to CRLF/LF/CR. +// Translates newlines and returns the previous newline code. // `_target`: A `std::wstring` to operate on, `_newLine`: A desired newline code. static enum newline_t convertCRLF(std::wstring &_target, enum newline_t _newLine) { std::wstring::iterator iter = _target.begin(); @@ -308,7 +311,7 @@ void onSize() { LOGFONTW rLogfont; GetClientRect(hWnd, &rect); scrX = rect.right; - scrY = rect.bottom - topPadding; + scrY = rect.bottom; // Button font ZeroMemory(&rLogfont, sizeof(rLogfont)); @@ -341,14 +344,24 @@ void onSize() { SendMessageW(hScrKB[i], WM_SETFONT, (WPARAM)newBtnFont, MAKELPARAM(TRUE, 0)); curX += adjust(30); } - MoveWindow(hEditor, 0, topPadding + adjust(32), scrX, scrY - adjust(32) - adjust(64) * 2, TRUE); + + int topEditor = topPadding + adjust(32); + if (horizontal) { + int halfY = (scrY - topEditor) / 2; + MoveWindow(hEditor, 0, topEditor, scrX / 3, scrY - topEditor, TRUE); + MoveWindow(hInput, scrX / 3, topEditor, scrX / 3, halfY, TRUE); + MoveWindow(hOutput, scrX / 3, halfY + topEditor, scrX / 3, scrY - halfY - topEditor, TRUE); + MoveWindow(hMemView, scrX * 2 / 3, topEditor, scrX - scrX * 2 / 3, scrY - topEditor, TRUE); + } else { + int halfY = (scrY - topEditor) / 2, centerY = (scrY + topEditor) / 2; + MoveWindow(hEditor, 0, topEditor, scrX, halfY, TRUE); + MoveWindow(hInput, 0, centerY, scrX / 2, halfY / 2, TRUE); + MoveWindow(hOutput, scrX / 2, centerY, scrX - scrX / 2, halfY / 2, TRUE); + MoveWindow(hMemView, 0, centerY + halfY / 2, scrX, scrY - centerY - halfY / 2, TRUE); + } SendMessageW(hEditor, WM_SETFONT, (WPARAM)newEditFont, MAKELPARAM(TRUE, 0)); - MoveWindow(hInput, 0, scrY + topPadding - adjust(64) * 2, scrX / 2, adjust(64), TRUE); SendMessageW(hInput, WM_SETFONT, (WPARAM)newEditFont, MAKELPARAM(TRUE, 0)); - MoveWindow(hOutput, scrX / 2, scrY + topPadding - adjust(64) * 2, scrX - scrX / 2, adjust(64), - TRUE); SendMessageW(hOutput, WM_SETFONT, (WPARAM)newEditFont, MAKELPARAM(TRUE, 0)); - MoveWindow(hMemView, 0, scrY + topPadding - adjust(64), scrX, adjust(64), TRUE); SendMessageW(hMemView, WM_SETFONT, (WPARAM)newEditFont, MAKELPARAM(TRUE, 0)); if (hBtnFont) DeleteObject(hBtnFont); @@ -359,21 +372,21 @@ void onSize() { } void onInitMenuPopup() { - // Brainfuck -> Memory type + // Brainfuck -> Memory Type CheckMenuRadioItem(hMenu, IDM_BF_MEMTYPE_SIGNED, IDM_BF_MEMTYPE_UNSIGNED, signedness ? IDM_BF_MEMTYPE_SIGNED : IDM_BF_MEMTYPE_UNSIGNED, MF_BYCOMMAND); - // Brainfuck -> Output charset + // Brainfuck -> Output Charset CheckMenuRadioItem(hMenu, IDM_BF_OUTPUT_ASCII, IDM_BF_OUTPUT_HEX, outCharSet, MF_BYCOMMAND); - // Brainfuck -> Input charset + // Brainfuck -> Input Charset CheckMenuRadioItem(hMenu, IDM_BF_INPUT_UTF8, IDM_BF_INPUT_HEX, inCharSet, MF_BYCOMMAND); - // Brainfuck -> Input instruction + // Brainfuck -> Input Instruction CheckMenuRadioItem(hMenu, IDM_BF_NOINPUT_ERROR, IDM_BF_NOINPUT_FF, IDM_BF_NOINPUT_ERROR + noInput, MF_BYCOMMAND); - // Brainfuck -> Integer overflow + // Brainfuck -> Integer Overflow CheckMenuRadioItem(hMenu, IDM_BF_INTOVF_ERROR, IDM_BF_INTOVF_WRAPAROUND, wrapInt ? IDM_BF_INTOVF_WRAPAROUND : IDM_BF_INTOVF_ERROR, MF_BYCOMMAND); @@ -398,10 +411,7 @@ void onInitMenuPopup() { // Options -> Debug CheckMenuItem(hMenu, IDM_OPT_TRACK, MF_BYCOMMAND | debug ? MF_CHECKED : MF_UNCHECKED); - // Options -> Dark theme - CheckMenuItem(hMenu, IDM_OPT_DARK, MF_BYCOMMAND | dark ? MF_CHECKED : MF_UNCHECKED); - - // Options -> Word wrap + // Options -> Word Wrap CheckMenuItem(hMenu, IDM_OPT_WORDWRAP, MF_BYCOMMAND | wordwrap ? MF_CHECKED : MF_UNCHECKED); bool undoable = SendMessageW(hEditor, EM_CANUNDO, 0, 0) != 0; @@ -542,49 +552,50 @@ int messageBox(HWND _hWnd, const wchar_t *_lpText, const wchar_t *_lpCaption, un // Hook window procedure for the edit control in the memory view options dialog. // This procedure translates top row character keys to numbers according to the keyboard layout of // SHARP Brain. -static LRESULT CALLBACK memViewDlgEditor(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { +static LRESULT CALLBACK memViewDlgEditor(HWND hWnd, unsigned int uMsg, WPARAM wParam, + LPARAM lParam) { static WNDPROC prevWndProc = (WNDPROC)myGetWindowLongW(hWnd, GWL_USERDATA); switch (uMsg) { case WM_CHAR: - switch ((CHAR)wParam) { - case 'q': + switch ((wchar_t)wParam) { + case L'q': SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"1"); return 0; - case 'w': + case L'w': SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"2"); return 0; - case 'e': + case L'e': SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"3"); return 0; - case 'r': + case L'r': SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"4"); return 0; - case 't': + case L't': SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"5"); return 0; - case 'y': + case L'y': SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"6"); return 0; - case 'u': + case L'u': SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"7"); return 0; - case 'i': + case L'i': SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"8"); return 0; - case 'o': + case L'o': SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"9"); return 0; - case 'p': + case L'p': SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"0"); return 0; } @@ -599,7 +610,7 @@ static LRESULT CALLBACK memViewDlgEditor(HWND hWnd, UINT uMsg, WPARAM wParam, LP } // Window procedure for the memory view options dialog. -INT_PTR CALLBACK memViewProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { +INT_PTR CALLBACK memViewProc(HWND hDlg, unsigned int uMsg, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (uMsg) { @@ -846,10 +857,16 @@ void switchWordwrap() { void switchTheme() { dark = !dark; - InvalidateRect(hEditor, NULL, FALSE); - InvalidateRect(hInput, NULL, FALSE); - InvalidateRect(hOutput, NULL, FALSE); - InvalidateRect(hMemView, NULL, FALSE); + InvalidateRect(hEditor, NULL, TRUE); + InvalidateRect(hInput, NULL, TRUE); + InvalidateRect(hOutput, NULL, TRUE); + InvalidateRect(hMemView, NULL, TRUE); +} + +void switchLayout() { + horizontal = !horizontal; + + onSize(); } void chooseFont() { diff --git a/ui.hpp b/ui.hpp index b2fb795..cd0447f 100644 --- a/ui.hpp +++ b/ui.hpp @@ -54,7 +54,7 @@ void selProg(unsigned int _progPtr); void selMemView(unsigned int _memPtr); // Memory view settings dialog. -INT_PTR CALLBACK memViewProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK memViewProc(HWND hwndDlg, unsigned int uMsg, WPARAM wParam, LPARAM lParam); // Retrieves the editor content to the returned buffer. // Previously returned pointer gets invalidated on each call. @@ -79,6 +79,9 @@ void switchWordwrap(); // Switches between dark/light theme. void switchTheme(); +// Switches between horizontal/portrait layout. +void switchLayout(); + // Opens a font dialog and applies it on editors. void chooseFont(); diff --git a/win.bat b/win.bat index fd6659f..ed81269 100644 --- a/win.bat +++ b/win.bat @@ -6,7 +6,7 @@ rem You can remove this line if you add GCC paths to your system. set PATH=C:\MinGW\bin echo "*** Compiling C++ sources..." -g++ -Wall -Wextra -O3 -std=c++17 -c *.cpp +g++ -Wall -Wextra -O3 -std=c++98 -c *.cpp echo "*** Compiling res.rc..." windres resource.rc resource.o diff --git a/win.sh b/win.sh index bd7524c..5f4a27b 100644 --- a/win.sh +++ b/win.sh @@ -1,28 +1,22 @@ #!/bin/bash set -e -if [ $# -ne 0 ]; then - GCCPATH=$1/ -else - GCCPATH= -fi - echo "*** [IA-32] Compiling C++ sources..." -${GCCPATH}i686-w64-mingw32-g++ -Wall -Wextra -O3 -std=c++17 -c *.cpp +i686-w64-mingw32-g++ -Wall -Wextra -O3 -std=c++98 -c ./*.cpp echo "*** [IA-32] Compiling res.rc..." -${GCCPATH}i686-w64-mingw32-windres resource.rc resource.o +i686-w64-mingw32-windres resource.rc resource.o echo "*** [IA-32] Linking..." -${GCCPATH}i686-w64-mingw32-g++ *.o -static -s -lcomctl32 -lwinmm -mwindows -municode -o Brainfuck32.exe +i686-w64-mingw32-g++ ./*.o -static -s -lcomctl32 -lwinmm -mwindows -municode -o Brainfuck32.exe echo "*** [AMD64] Compiling C++ sources..." -${GCCPATH}x86_64-w64-mingw32-g++ -Wall -Wextra -O3 -std=c++17 -c *.cpp +x86_64-w64-mingw32-g++ -Wall -Wextra -O3 -std=c++98 -c ./*.cpp echo "*** [AMD64] Compiling res.rc..." -${GCCPATH}x86_64-w64-mingw32-windres resource.rc resource.o +x86_64-w64-mingw32-windres resource.rc resource.o echo "*** [AMD64] Linking..." -${GCCPATH}x86_64-w64-mingw32-g++ *.o -static -s -lcomctl32 -lwinmm -mwindows -municode -o Brainfuck64.exe +x86_64-w64-mingw32-g++ ./*.o -static -s -lcomctl32 -lwinmm -mwindows -municode -o Brainfuck64.exe echo "OK" From f65b86a7bcd41d86cf4fb391459ab2acc5eea8ae Mon Sep 17 00:00:00 2001 From: watamario15 Date: Mon, 27 Mar 2023 01:10:23 +0900 Subject: [PATCH 2/9] Eliminate the use of STL other than string --- bf.cpp | 58 +++++++++++++++++------------------- bf.hpp | 25 +++++++++------- main.cpp | 88 +++++++++++++++++++++++++++++++------------------------ readme.md | 2 +- ui.cpp | 39 ++++++++++++------------ ui.hpp | 2 +- 6 files changed, 110 insertions(+), 104 deletions(-) diff --git a/bf.cpp b/bf.cpp index 04c1653..394f2ae 100644 --- a/bf.cpp +++ b/bf.cpp @@ -4,20 +4,18 @@ #define NOMINMAX #endif #include - -#include -#include -#include +#include void Brainfuck::reset() { progIndex = 0; inIndex = 0; - memory.clear(); - memory.push_back(0); + memset(memory, 0, sizeof(memory)); memIndex = 0; + memLen = 1; } -void Brainfuck::reset(size_t _progLen, const wchar_t *_program, size_t _inLen, const void *_input) { +void Brainfuck::reset(unsigned _progLen, const wchar_t *_program, unsigned _inLen, + const void *_input) { if (_progLen == 0 || !_program) { program = NULL; progLen = 0; @@ -36,9 +34,9 @@ void Brainfuck::reset(size_t _progLen, const wchar_t *_program, size_t _inLen, c } inIndex = 0; - memory.clear(); - memory.push_back(0); + memset(memory, 0, sizeof(memory)); memIndex = 0; + memLen = 1; } void Brainfuck::setBehavior(enum noinput_t _noInput, bool _wrapInt, bool _signedness, bool _debug) { @@ -58,13 +56,12 @@ enum Brainfuck::result_t Brainfuck::next(unsigned char *_output, bool *_didOutpu switch (program[progIndex]) { case L'>': - if (memIndex != memory.max_size() - 1) { + if (memIndex != 65535) { ++memIndex; - if (memory.size() == memIndex) memory.push_back(0); + if (memIndex == memLen) ++memLen; } else { - wsprintfW(lastError, - L"%lu: Instruction '>' used when the memory pointer is std::vector::max_size.", - (unsigned long)progIndex); + wsprintfW(lastError, L"%u: Instruction '>' used when the memory pointer is 65535.", + progIndex); return RESULT_ERR; } break; @@ -73,8 +70,7 @@ enum Brainfuck::result_t Brainfuck::next(unsigned char *_output, bool *_didOutpu if (memIndex != 0) { --memIndex; } else { - wsprintfW(lastError, L"%lu: Instruction '<' used when the memory pointer is 0.", - (unsigned long)progIndex); + wsprintfW(lastError, L"%u: Instruction '<' used when the memory pointer is 0.", progIndex); return RESULT_ERR; } break; @@ -87,16 +83,16 @@ enum Brainfuck::result_t Brainfuck::next(unsigned char *_output, bool *_didOutpu if (memory[memIndex] != 0x7F) { ++memory[memIndex]; } else { - wsprintfW(lastError, L"%lu: Instruction '+' used when the pointed memory is 127.", - (unsigned long)progIndex); + wsprintfW(lastError, L"%u: Instruction '+' used when the pointed memory is 127.", + progIndex); return RESULT_ERR; } } else { if (memory[memIndex] != 0xFF) { ++memory[memIndex]; } else { - wsprintfW(lastError, L"%lu: Instruction '+' used when the pointed memory is 255.", - (unsigned long)progIndex); + wsprintfW(lastError, L"%u: Instruction '+' used when the pointed memory is 255.", + progIndex); return RESULT_ERR; } } @@ -110,16 +106,16 @@ enum Brainfuck::result_t Brainfuck::next(unsigned char *_output, bool *_didOutpu if (memory[memIndex] != 0x80) { --memory[memIndex]; } else { - wsprintfW(lastError, L"%lu: Instruction '-' used when the pointed memory is -128.", - (unsigned long)progIndex); + wsprintfW(lastError, L"%u: Instruction '-' used when the pointed memory is -128.", + progIndex); return RESULT_ERR; } } else { if (memory[memIndex] != 0x00) { --memory[memIndex]; } else { - wsprintfW(lastError, L"%lu: Instruction '-' used when the pointed memory is 0.", - (unsigned long)progIndex); + wsprintfW(lastError, L"%u: Instruction '-' used when the pointed memory is 0.", + progIndex); return RESULT_ERR; } } @@ -137,8 +133,8 @@ enum Brainfuck::result_t Brainfuck::next(unsigned char *_output, bool *_didOutpu } else if (noInput == NOINPUT_FF) { memory[memIndex] = 255; } else { - wsprintfW(lastError, L"%lu: Instruction ',' used when the input stream is empty.", - (unsigned long)progIndex); + wsprintfW(lastError, L"%u: Instruction ',' used when the input stream is empty.", + progIndex); return RESULT_ERR; } } else { @@ -150,10 +146,10 @@ enum Brainfuck::result_t Brainfuck::next(unsigned char *_output, bool *_didOutpu case L'[': if (memory[memIndex] == 0) { if (progIndex == progLen - 1) { - wsprintfW(lastError, L"%lu: No matching closing bracket.", (unsigned long)progIndex); + wsprintfW(lastError, L"%u: No matching closing bracket.", progIndex); return RESULT_ERR; } - size_t nextIndex = progIndex + 1, ketCnt = 0; + unsigned nextIndex = progIndex + 1, ketCnt = 0; while (true) { if (program[nextIndex] == L']') { if (ketCnt == 0) { @@ -164,7 +160,7 @@ enum Brainfuck::result_t Brainfuck::next(unsigned char *_output, bool *_didOutpu } if (program[nextIndex] == L'[') ketCnt++; if (nextIndex == progLen - 1) { - wsprintfW(lastError, L"%lu: No matching closing bracket.", (unsigned long)progIndex); + wsprintfW(lastError, L"%u: No matching closing bracket.", progIndex); return RESULT_ERR; } ++nextIndex; @@ -176,10 +172,10 @@ enum Brainfuck::result_t Brainfuck::next(unsigned char *_output, bool *_didOutpu case L']': if (memory[memIndex] != 0) { if (progIndex == 0) { - wsprintfW(lastError, L"%lu: No matching opening bracket.", (unsigned long)progIndex); + wsprintfW(lastError, L"%u: No matching opening bracket.", progIndex); return RESULT_ERR; } - size_t nextIndex = progIndex - 1, braCnt = 0; + unsigned nextIndex = progIndex - 1, braCnt = 0; while (true) { if (program[nextIndex] == L'[') { if (braCnt == 0) { diff --git a/bf.hpp b/bf.hpp index 556a041..d921b7a 100644 --- a/bf.hpp +++ b/bf.hpp @@ -6,8 +6,6 @@ #include -#include - class Brainfuck { public: // Defines a desired behavior for input instructions with no input. @@ -20,6 +18,7 @@ class Brainfuck { Brainfuck() : program(NULL), input(NULL), + memLen(1), wrapInt(true), signedness(true), debug(false), @@ -29,9 +28,10 @@ class Brainfuck { // Initializes the internal state and sets `_program` and `_input`. // DO NOT CHANGE THE CONTENTS OF THEM. You must call reset() when you update them. - Brainfuck(size_t _progLen, const wchar_t *_program, size_t _inLen, const void *_input) + Brainfuck(unsigned _progLen, const wchar_t *_program, unsigned _inLen, const void *_input) : program(NULL), input(NULL), + memLen(1), wrapInt(true), signedness(true), debug(false), @@ -44,7 +44,7 @@ class Brainfuck { // Resets the internal state and copies `_program` and `_input`. // DO NOT CHANGE THE CONTENTS OF THEM. You must call reset() when you update them. - void reset(size_t _progLen, const wchar_t *_program, size_t _inLen, const void *_input); + void reset(unsigned _progLen, const wchar_t *_program, unsigned _inLen, const void *_input); // Change implementation-defined behaviors. // When wrapInt is false, `next` throws an exception on an overflow/underflow. @@ -61,23 +61,26 @@ class Brainfuck { enum result_t next(unsigned char *_output, bool *_didOutput); // Returns the memory. The returned content becomes invalid on reset/next. - const std::vector &getMemory() { return memory; } + const unsigned char *getMemory(unsigned *_size) { + *_size = memLen; + return memory; + } // Returns the memory pointer. - size_t getMemPtr() { return memIndex; } + unsigned getMemPtr() { return memIndex; } // Returns the program pointer. - size_t getProgPtr() { return progIndex; } + unsigned getProgPtr() { return progIndex; } // Returns the last error. const wchar_t *getLastError() { return lastError; } private: // All initializations are in constructor, as old C++ doesn't allow them on the declaration. - const wchar_t *program; // Program - const unsigned char *input; // Input stream - std::vector memory; // Memory - size_t memIndex, progIndex, inIndex, progLen, inLen; + const wchar_t *program; // Program + const unsigned char *input; // Input stream + unsigned char memory[65536]; // Memory + unsigned memIndex, progIndex, inIndex, progLen, inLen, memLen; bool wrapInt, signedness, debug; enum noinput_t noInput; wchar_t lastError[128]; diff --git a/main.cpp b/main.cpp index 9b9fcb3..50d31c7 100644 --- a/main.cpp +++ b/main.cpp @@ -1,5 +1,4 @@ -#include -#include +#include #ifndef _UNICODE #define _UNICODE @@ -72,7 +71,7 @@ static class Brainfuck *g_bf; static unsigned int g_timerID = 0; static wchar_t *g_cmdLine; static bool g_prevCR = false; -static std::vector g_outBuf; +static std::string g_outBuf; static HANDLE g_hThread = NULL; static volatile enum ctrlthread_t g_ctrlThread = CTRLTHREAD_RUN; @@ -83,19 +82,19 @@ static inline bool isHex(wchar_t chr) { // Initializes the Brainfuck module. Returns false on an invalid hexadecimal input. static bool bfInit() { - static std::vector vecIn; - static unsigned char *input = NULL; + static std::string inBuf; + static const char *input = NULL; ui::setOutput(L""); ui::setMemory(NULL); - g_outBuf.clear(); + g_outBuf = ""; g_prevCR = false; int inLen; wchar_t *wcInput = ui::getInput(); - // We shouldn't delete when vecIn isn't empty, as it means input is allocated by vecIn. - if (input && vecIn.empty()) delete[] input; + // We shouldn't delete when inBuf isn't empty, as it means input is allocated by inBuf. + if (input && inBuf.empty()) delete[] (char *)input; input = NULL; - vecIn.clear(); + inBuf = ""; if (ui::inCharSet == IDM_BF_INPUT_HEX) { // Converts the hexadecimal input. @@ -113,21 +112,21 @@ static bool bfInit() { } else if (iswspace(wcInput[i]) || !wcInput[i]) { if (hexLen == 1) { if (hex[0] < L'A') { - vecIn.push_back(hex[0] - L'0'); + inBuf += (char)(hex[0] - L'0'); } else { - vecIn.push_back(hex[0] - L'A' + 10); + inBuf += (char)(hex[0] - L'A' + 10); } hexLen = 0; } else if (hexLen == 2) { if (hex[0] < L'A') { - vecIn.push_back((hex[0] - L'0') << 4); + inBuf += (char)((hex[0] - L'0') << 4); } else { - vecIn.push_back((hex[0] - L'A' + 10) << 4); + inBuf += (char)((hex[0] - L'A' + 10) << 4); } if (hex[1] < L'A') { - vecIn.back() += hex[1] - L'0'; + inBuf[inBuf.size() - 1] += hex[1] - L'0'; } else { - vecIn.back() += hex[1] - L'A' + 10; + inBuf[inBuf.size() - 1] += hex[1] - L'A' + 10; } hexLen = 0; } @@ -137,13 +136,16 @@ static bool bfInit() { if (wcInput[i] == L'\0') break; } - input = vecIn.empty() ? NULL : &vecIn.front(); - inLen = (int)vecIn.size(); + + input = inBuf.empty() ? NULL : inBuf.c_str(); + inLen = (int)inBuf.size(); } else { int codePage = (ui::inCharSet == IDM_BF_INPUT_SJIS) ? 932 : CP_UTF8; - inLen = WideCharToMultiByte(codePage, 0, wcInput, -1, (char *)NULL, 0, NULL, NULL); - input = new unsigned char[inLen]; - WideCharToMultiByte(codePage, 0, wcInput, -1, (char *)input, inLen, NULL, NULL); + inLen = WideCharToMultiByte(codePage, 0, wcInput, -1, NULL, 0, NULL, NULL); + char *converted = new char[inLen]; + WideCharToMultiByte(codePage, 0, wcInput, -1, converted, inLen, NULL, NULL); + + input = converted; inLen--; } @@ -155,11 +157,11 @@ static bool bfInit() { // Executes the next instruction. static enum Brainfuck::result_t bfNext() { - unsigned char output; + char output; bool didOutput; g_bf->setBehavior(ui::noInput, ui::wrapInt, ui::signedness, ui::breakpoint); - enum Brainfuck::result_t result = g_bf->next(&output, &didOutput); + enum Brainfuck::result_t result = g_bf->next((unsigned char *)&output, &didOutput); if (result == Brainfuck::RESULT_ERR) { if (g_timerID) { timeKillEvent(g_timerID); @@ -173,12 +175,10 @@ static enum Brainfuck::result_t bfNext() { if (result == Brainfuck::RESULT_FIN) { if (!g_outBuf.empty()) { int codePage = (ui::outCharSet == IDM_BF_OUTPUT_SJIS) ? 932 : CP_UTF8; - if (g_outBuf.back() != 0) g_outBuf.push_back(0); // null terminator - int outLen = MultiByteToWideChar(codePage, 0, (char *)&g_outBuf.front(), (int)g_outBuf.size(), + int outLen = MultiByteToWideChar(codePage, 0, g_outBuf.c_str(), (int)g_outBuf.size() + 1, (wchar_t *)NULL, 0); wchar_t *wcOut = new wchar_t[outLen]; - MultiByteToWideChar(codePage, 0, (char *)&g_outBuf.front(), (int)g_outBuf.size(), wcOut, - outLen); + MultiByteToWideChar(codePage, 0, g_outBuf.c_str(), (int)g_outBuf.size() + 1, wcOut, outLen); ui::setOutput(wcOut); delete[] wcOut; } @@ -209,9 +209,10 @@ static enum Brainfuck::result_t bfNext() { ui::appendOutput(wcOut); } else { // Align newlines to CRLF. - if (g_prevCR && output != '\n') g_outBuf.push_back('\n'); - if (!g_prevCR && output == '\n') g_outBuf.push_back('\r'); - g_outBuf.push_back(output); + if (g_prevCR && output != '\n') g_outBuf += '\n'; + if (!g_prevCR && output == '\n') g_outBuf += '\r'; + g_prevCR = output == L'\r'; + g_outBuf += output; } } } @@ -244,8 +245,10 @@ tret_t WINAPI threadRunner(void *lpParameter) { if (ui::speed != 0) WaitForSingleObject(hEvent, INFINITE); if ((result = bfNext()) != Brainfuck::RESULT_RUN) break; if (ui::debug) { - ui::setMemory(&g_bf->getMemory()); - ui::selProg((int)g_bf->getProgPtr()); + unsigned size; + const unsigned char *memory = g_bf->getMemory(&size); + ui::setMemory(memory, size); + ui::selProg(g_bf->getProgPtr()); } } @@ -261,8 +264,10 @@ tret_t WINAPI threadRunner(void *lpParameter) { if (g_ctrlThread == CTRLTHREAD_RUN) { // Finished or error ui::setState(result == Brainfuck::RESULT_FIN ? ui::STATE_FINISH : ui::STATE_PAUSE); } - ui::setMemory(&g_bf->getMemory()); - ui::selProg((int)g_bf->getProgPtr()); + unsigned size; + const unsigned char *memory = g_bf->getMemory(&size); + ui::setMemory(memory, size); + ui::selProg(g_bf->getProgPtr()); } PostMessageW(ui::hWnd, WM_APP_THREADEND, 0, 0); @@ -371,7 +376,7 @@ static LRESULT CALLBACK wndProc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPA ui::setState(ui::STATE_RUN); break; - case IDC_CMDBTN_FIRST + 1: // Next + case IDC_CMDBTN_FIRST + 1: { // Next if (!didInit) { if (!bfInit()) { ui::messageBox(hWnd, L"Invalid input.", L"Error", MB_ICONWARNING); @@ -381,9 +386,12 @@ static LRESULT CALLBACK wndProc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPA } ui::setState(bfNext() == Brainfuck::RESULT_FIN ? ui::STATE_FINISH : ui::STATE_PAUSE); - ui::setMemory(&g_bf->getMemory()); - ui::selProg((int)g_bf->getProgPtr()); + unsigned size; + const unsigned char *memory = g_bf->getMemory(&size); + ui::setMemory(memory, size); + ui::selProg(g_bf->getProgPtr()); break; + } case IDC_CMDBTN_FIRST + 2: // Pause if (g_hThread) g_ctrlThread = CTRLTHREAD_PAUSE; @@ -520,7 +528,9 @@ static LRESULT CALLBACK wndProc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPA case IDM_OPT_MEMVIEW: if (DialogBoxW(ui::hInst, L"memviewopt", hWnd, ui::memViewProc) == IDOK && ui::state != ui::STATE_INIT) { - ui::setMemory(&g_bf->getMemory()); + unsigned size; + const unsigned char *memory = g_bf->getMemory(&size); + ui::setMemory(memory, size); } break; @@ -529,11 +539,11 @@ static LRESULT CALLBACK wndProc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPA break; case IDM_OPT_HLTPROG: - ui::selProg((int)g_bf->getProgPtr()); + ui::selProg(g_bf->getProgPtr()); break; case IDM_OPT_HLTMEM: - ui::selMemView((int)g_bf->getMemPtr()); + ui::selMemView(g_bf->getMemPtr()); break; case IDM_OPT_DARK: diff --git a/readme.md b/readme.md index 822e90f..48a885b 100644 --- a/readme.md +++ b/readme.md @@ -10,7 +10,7 @@ Default options are shown in **bold** typeface. For I/O charset, UTF-8 is defaul |Item|Spec| |:-:|:-:| |Memory type|**8-bit two's-complement integer (-128 to 127)**, 8-bit integer (0 to 255)| -|Memory length|**infinity (your device's memory capacity)**| +|Memory length|**65,536 bytes**| |I/O charset|**ASCII, UTF-8**, Shift\_JIS| |Input error|**input 0**, input FF, error| |Integer overflow|**wrap around**, error| diff --git a/ui.cpp b/ui.cpp index 253e41f..2b41ccd 100644 --- a/ui.cpp +++ b/ui.cpp @@ -32,7 +32,7 @@ typedef enum MONITOR_DPI_TYPE { // Function pointer type for GetDpiForMonitor API. typedef HRESULT(CALLBACK *GetDpiForMonitor_t)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, - unsigned int *dpiX, unsigned int *dpiY); + unsigned *dpiX, unsigned *dpiY); // Function pointer type for TaskDialog API. typedef HRESULT(__stdcall *TaskDialog_t)(HWND hwndOwner, HINSTANCE hInstance, @@ -69,8 +69,8 @@ static HWND hEditor, hInput, hOutput, hMemView, hFocused, hCmdBtn[CMDBTN_LEN], h static HMENU hMenu; static HFONT hBtnFont = NULL, hEditFont = NULL; static LOGFONTW editFont; -static int topPadding = 0, scrX = 480, scrY = 320; -static unsigned int memViewStart = 0; +static int topPadding = 0; +static unsigned memViewStart = 0; static wchar_t *retEditBuf = NULL, *retInBuf = NULL; static std::wstring wstrFileName; static bool withBOM = false, wordwrap = true; @@ -151,8 +151,8 @@ static enum newline_t convertCRLF(std::wstring &_target, enum newline_t _newLine } // Enables/Disables menu items from the smaller nearest 10 multiple to `_endID`. -static void enableMenus(unsigned int _endID, bool _enable) { - unsigned int i; +static void enableMenus(unsigned _endID, bool _enable) { + unsigned i; for (i = (_endID / 10) * 10; i <= _endID; ++i) { EnableMenuItem(hMenu, i, MF_BYCOMMAND | (_enable ? MF_ENABLED : MF_GRAYED)); } @@ -263,7 +263,7 @@ void onCreate(HWND _hWnd, HINSTANCE _hInst) { // Tests whether it successfully got the GetDpiForMonitor API. if (getDpiForMonitor) { // It got (the system is presumably Windows 8.1 or later). - unsigned int tmpX, tmpY; + unsigned tmpX, tmpY; getDpiForMonitor(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST), MDT_EFFECTIVE_DPI, &tmpX, &tmpY); dpi = tmpX; @@ -309,9 +309,6 @@ void onSize() { size_t i; LOGFONTW rLogfont; - GetClientRect(hWnd, &rect); - scrX = rect.right; - scrY = rect.bottom; // Button font ZeroMemory(&rLogfont, sizeof(rLogfont)); @@ -346,14 +343,15 @@ void onSize() { } int topEditor = topPadding + adjust(32); + GetClientRect(hWnd, &rect); + int scrX = rect.right, scrY = rect.bottom; + int halfY = (scrY - topEditor) / 2, centerY = (scrY + topEditor) / 2; if (horizontal) { - int halfY = (scrY - topEditor) / 2; MoveWindow(hEditor, 0, topEditor, scrX / 3, scrY - topEditor, TRUE); MoveWindow(hInput, scrX / 3, topEditor, scrX / 3, halfY, TRUE); MoveWindow(hOutput, scrX / 3, halfY + topEditor, scrX / 3, scrY - halfY - topEditor, TRUE); MoveWindow(hMemView, scrX * 2 / 3, topEditor, scrX - scrX * 2 / 3, scrY - topEditor, TRUE); } else { - int halfY = (scrY - topEditor) / 2, centerY = (scrY + topEditor) / 2; MoveWindow(hEditor, 0, topEditor, scrX, halfY, TRUE); MoveWindow(hInput, 0, centerY, scrX / 2, halfY / 2, TRUE); MoveWindow(hOutput, scrX / 2, centerY, scrX - scrX / 2, halfY / 2, TRUE); @@ -498,7 +496,7 @@ void selAll() { SendMessageW(hFocused, EM_SETSEL, 0, -1); } void undo() { SendMessageW(hEditor, EM_UNDO, 0, 0); } -int messageBox(HWND _hWnd, const wchar_t *_lpText, const wchar_t *_lpCaption, unsigned int _uType) { +int messageBox(HWND _hWnd, const wchar_t *_lpText, const wchar_t *_lpCaption, unsigned _uType) { #ifndef UNDER_CE if (taskDialog) { // Tests whether _uType uses some features that TaskDialog doesn't support. @@ -552,8 +550,7 @@ int messageBox(HWND _hWnd, const wchar_t *_lpText, const wchar_t *_lpCaption, un // Hook window procedure for the edit control in the memory view options dialog. // This procedure translates top row character keys to numbers according to the keyboard layout of // SHARP Brain. -static LRESULT CALLBACK memViewDlgEditor(HWND hWnd, unsigned int uMsg, WPARAM wParam, - LPARAM lParam) { +static LRESULT CALLBACK memViewDlgEditor(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam) { static WNDPROC prevWndProc = (WNDPROC)myGetWindowLongW(hWnd, GWL_USERDATA); switch (uMsg) { @@ -610,7 +607,7 @@ static LRESULT CALLBACK memViewDlgEditor(HWND hWnd, unsigned int uMsg, WPARAM wP } // Window procedure for the memory view options dialog. -INT_PTR CALLBACK memViewProc(HWND hDlg, unsigned int uMsg, WPARAM wParam, LPARAM lParam) { +INT_PTR CALLBACK memViewProc(HWND hDlg, unsigned uMsg, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (uMsg) { @@ -737,24 +734,24 @@ void updateFocus(int _id) { } } -void selProg(unsigned int _progPtr) { SendMessageW(hEditor, EM_SETSEL, _progPtr, _progPtr + 1); } +void selProg(unsigned _progPtr) { SendMessageW(hEditor, EM_SETSEL, _progPtr, _progPtr + 1); } -void selMemView(unsigned int _memPtr) { +void selMemView(unsigned _memPtr) { SendMessageW(hMemView, EM_SETSEL, (_memPtr - memViewStart) * 3, (_memPtr - memViewStart) * 3 + 2); } -void setMemory(const std::vector *memory) { +void setMemory(const unsigned char *memory, unsigned size) { if (!memory) { SetWindowTextW(hMemView, NULL); return; } - unsigned int i; + unsigned i; std::wstring wstrOut; - for (i = memViewStart; i < memViewStart + 100 && i < memory->size(); ++i) { + for (i = memViewStart; i < memViewStart + 100 && i < size; ++i) { // Converts to a hexadecimal string. wchar_t wcOut[] = {0, 0, L' ', 0}; - unsigned char high = memory->at(i) >> 4, low = memory->at(i) & 0xF; + unsigned char high = memory[i] >> 4, low = memory[i] & 0xF; if (high < 10) { wcOut[0] = L'0' + high; } else { diff --git a/ui.hpp b/ui.hpp index cd0447f..4798712 100644 --- a/ui.hpp +++ b/ui.hpp @@ -71,7 +71,7 @@ void setOutput(const wchar_t *_str); void appendOutput(const wchar_t *_str); // Sets memory. Clears the memory view when NULL is given. -void setMemory(const std::vector *memory); +void setMemory(const unsigned char *memory, unsigned size = 0); // Switches between wordwrap enabled and disabled for edit controls. void switchWordwrap(); From 051d54b02965a4866a4cc78b42fcf82492928a7f Mon Sep 17 00:00:00 2001 From: watamario15 Date: Tue, 28 Mar 2023 01:31:17 +0900 Subject: [PATCH 3/9] Better error handling and refactor --- evc4proj/brainfuck_evc4.vcp | 15 +++ main.cpp | 110 +++++-------------- ui.cpp | 202 +++++++++++++++------------------- ui.hpp | 6 +- util.cpp | 209 ++++++++++++++++++++++++++++++++++++ util.hpp | 45 ++++++++ 6 files changed, 384 insertions(+), 203 deletions(-) create mode 100644 util.cpp create mode 100644 util.hpp diff --git a/evc4proj/brainfuck_evc4.vcp b/evc4proj/brainfuck_evc4.vcp index a2759d0..f852d79 100644 --- a/evc4proj/brainfuck_evc4.vcp +++ b/evc4proj/brainfuck_evc4.vcp @@ -113,6 +113,7 @@ DEP_CPP_MAIN_=\ "..\bf.hpp"\ "..\resource.h"\ "..\ui.hpp"\ + "..\util.hpp"\ # End Source File # Begin Source File @@ -122,6 +123,16 @@ DEP_CPP_UI_CP=\ "..\bf.hpp"\ "..\resource.h"\ "..\ui.hpp"\ + "..\util.hpp"\ + +# End Source File +# Begin Source File + +SOURCE=..\util.cpp +DEP_CPP_UTIL_=\ + "..\bf.hpp"\ + "..\ui.hpp"\ + "..\util.hpp"\ # End Source File # End Group @@ -140,6 +151,10 @@ SOURCE=..\resource.h SOURCE=..\ui.hpp # End Source File +# Begin Source File + +SOURCE=..\util.hpp +# End Source File # End Group # Begin Group "Resource Files" diff --git a/main.cpp b/main.cpp index 50d31c7..74937e2 100644 --- a/main.cpp +++ b/main.cpp @@ -64,6 +64,7 @@ typedef unsigned int tret_t; #include "bf.hpp" #include "resource.h" #include "ui.hpp" +#include "util.hpp" enum ctrlthread_t { CTRLTHREAD_RUN, CTRLTHREAD_PAUSE, CTRLTHREAD_END }; @@ -75,81 +76,33 @@ static std::string g_outBuf; static HANDLE g_hThread = NULL; static volatile enum ctrlthread_t g_ctrlThread = CTRLTHREAD_RUN; -static inline bool isHex(wchar_t chr) { - return (chr >= L'0' && chr <= L'9') || (chr >= L'A' && chr <= L'F') || - (chr >= L'a' && chr <= L'f'); -} - // Initializes the Brainfuck module. Returns false on an invalid hexadecimal input. static bool bfInit() { - static std::string inBuf; - static const char *input = NULL; + static unsigned char *input = NULL; ui::setOutput(L""); ui::setMemory(NULL); g_outBuf = ""; g_prevCR = false; + if (input) free(input); + int inLen; wchar_t *wcInput = ui::getInput(); - // We shouldn't delete when inBuf isn't empty, as it means input is allocated by inBuf. - if (input && inBuf.empty()) delete[] (char *)input; - input = NULL; - inBuf = ""; + if (!wcInput) return false; if (ui::inCharSet == IDM_BF_INPUT_HEX) { - // Converts the hexadecimal input. - wchar_t hex[2]; - int hexLen = 0; - size_t i; - for (i = 0; true; ++i) { - if (isHex(wcInput[i])) { - if (hexLen >= 2) return false; // Exceeding the 8-bit range. - if (wcInput[i] > L'Z') { // Aligns to the upper case. - hex[hexLen++] = wcInput[i] - (L'a' - L'A'); - } else { - hex[hexLen++] = wcInput[i]; - } - } else if (iswspace(wcInput[i]) || !wcInput[i]) { - if (hexLen == 1) { - if (hex[0] < L'A') { - inBuf += (char)(hex[0] - L'0'); - } else { - inBuf += (char)(hex[0] - L'A' + 10); - } - hexLen = 0; - } else if (hexLen == 2) { - if (hex[0] < L'A') { - inBuf += (char)((hex[0] - L'0') << 4); - } else { - inBuf += (char)((hex[0] - L'A' + 10) << 4); - } - if (hex[1] < L'A') { - inBuf[inBuf.size() - 1] += hex[1] - L'0'; - } else { - inBuf[inBuf.size() - 1] += hex[1] - L'A' + 10; - } - hexLen = 0; - } - } else { - return false; // Invalid hexadecimal input. - } - - if (wcInput[i] == L'\0') break; - } - - input = inBuf.empty() ? NULL : inBuf.c_str(); - inLen = (int)inBuf.size(); + if (!util::parseHex(ui::hWnd, wcInput, &input)) return false; + inLen = input ? strlen((char *)input) : 0; } else { int codePage = (ui::inCharSet == IDM_BF_INPUT_SJIS) ? 932 : CP_UTF8; inLen = WideCharToMultiByte(codePage, 0, wcInput, -1, NULL, 0, NULL, NULL); - char *converted = new char[inLen]; - WideCharToMultiByte(codePage, 0, wcInput, -1, converted, inLen, NULL, NULL); - - input = converted; + input = (unsigned char *)malloc(inLen); + WideCharToMultiByte(codePage, 0, wcInput, -1, (char *)input, inLen, NULL, NULL); inLen--; } wchar_t *wcEditor = ui::getEditor(); + if (!wcEditor) return false; g_bf->reset(wcslen(wcEditor), wcEditor, inLen, input); return true; @@ -157,11 +110,11 @@ static bool bfInit() { // Executes the next instruction. static enum Brainfuck::result_t bfNext() { - char output; + unsigned char output; bool didOutput; g_bf->setBehavior(ui::noInput, ui::wrapInt, ui::signedness, ui::breakpoint); - enum Brainfuck::result_t result = g_bf->next((unsigned char *)&output, &didOutput); + enum Brainfuck::result_t result = g_bf->next(&output, &didOutput); if (result == Brainfuck::RESULT_ERR) { if (g_timerID) { timeKillEvent(g_timerID); @@ -177,10 +130,14 @@ static enum Brainfuck::result_t bfNext() { int codePage = (ui::outCharSet == IDM_BF_OUTPUT_SJIS) ? 932 : CP_UTF8; int outLen = MultiByteToWideChar(codePage, 0, g_outBuf.c_str(), (int)g_outBuf.size() + 1, (wchar_t *)NULL, 0); - wchar_t *wcOut = new wchar_t[outLen]; - MultiByteToWideChar(codePage, 0, g_outBuf.c_str(), (int)g_outBuf.size() + 1, wcOut, outLen); - ui::setOutput(wcOut); - delete[] wcOut; + wchar_t *wcOut = (wchar_t *)malloc(sizeof(wchar_t) * outLen); + if (!wcOut) { + ui::messageBox(ui::hWnd, L"Memory allocation failed.", L"Error", MB_ICONWARNING); + } else { + MultiByteToWideChar(codePage, 0, g_outBuf.c_str(), (int)g_outBuf.size() + 1, wcOut, outLen); + ui::setOutput(wcOut); + free(wcOut); + } } } else { if (didOutput) { @@ -193,19 +150,8 @@ static enum Brainfuck::result_t bfNext() { g_prevCR = wcOut[0] == L'\r'; ui::appendOutput(wcOut); } else if (ui::outCharSet == IDM_BF_OUTPUT_HEX) { - // Converts the output to a hexadecimal string. - wchar_t wcOut[] = {0, 0, L' ', 0}; - unsigned char high = output >> 4, low = output & 0xF; - if (high < 10) { - wcOut[0] = L'0' + high; - } else { - wcOut[0] = L'A' + (high - 10); - } - if (low < 10) { - wcOut[1] = L'0' + low; - } else { - wcOut[1] = L'A' + (low - 10); - } + wchar_t wcOut[4]; + util::toHex(output, wcOut); ui::appendOutput(wcOut); } else { // Align newlines to CRLF. @@ -357,10 +303,7 @@ static LRESULT CALLBACK wndProc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPA switch (LOWORD(wParam)) { case IDC_CMDBTN_FIRST: // Run if (!didInit) { - if (!bfInit()) { - ui::messageBox(hWnd, L"Invalid input.", L"Error", MB_ICONWARNING); - break; - } + if (!bfInit()) break; didInit = true; } @@ -378,10 +321,7 @@ static LRESULT CALLBACK wndProc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPA case IDC_CMDBTN_FIRST + 1: { // Next if (!didInit) { - if (!bfInit()) { - ui::messageBox(hWnd, L"Invalid input.", L"Error", MB_ICONWARNING); - break; - } + if (!bfInit()) break; didInit = true; } @@ -590,7 +530,7 @@ static LRESULT CALLBACK wndProc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPA int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, wchar_t *lpCmdLine, int nShowCmd) { - size_t i; + int i; UNREFERENCED_PARAMETER(hPrevInstance); // Replaces slashes with backslashes. diff --git a/ui.cpp b/ui.cpp index 2b41ccd..72157bd 100644 --- a/ui.cpp +++ b/ui.cpp @@ -60,21 +60,20 @@ typedef HRESULT(__stdcall *TaskDialog_t)(HWND hwndOwner, HINSTANCE hInstance, #include "bf.hpp" #include "resource.h" #include "ui.hpp" +#include "util.hpp" namespace ui { -enum newline_t { NEWLINE_CRLF, NEWLINE_LF, NEWLINE_CR }; static const wchar_t *wcCmdBtn[CMDBTN_LEN] = {L"Run", L"Next", L"Pause", L"End"}, *wcScrKB[SCRKBD_LEN] = {L">", L"<", L"+", L"-", L".", L",", L"[", L"]", L"@"}; static HWND hEditor, hInput, hOutput, hMemView, hFocused, hCmdBtn[CMDBTN_LEN], hScrKB[SCRKBD_LEN]; static HMENU hMenu; static HFONT hBtnFont = NULL, hEditFont = NULL; static LOGFONTW editFont; -static int topPadding = 0; -static unsigned memViewStart = 0; +static int topPadding = 0, memViewStart = 0; static wchar_t *retEditBuf = NULL, *retInBuf = NULL; static std::wstring wstrFileName; static bool withBOM = false, wordwrap = true; -static enum newline_t newLine = NEWLINE_CRLF; +static enum util::newline_t newLine = util::NEWLINE_CRLF; #ifdef UNDER_CE static HWND hCmdBar; #else @@ -89,66 +88,7 @@ int speed = 10, outCharSet = IDM_BF_OUTPUT_ASCII, inCharSet = IDM_BF_INPUT_UTF8; enum Brainfuck::noinput_t noInput = Brainfuck::NOINPUT_ZERO; HWND hWnd; HINSTANCE hInst; - -// Translates newlines and returns the previous newline code. -// `_target`: A `std::wstring` to operate on, `_newLine`: A desired newline code. -static enum newline_t convertCRLF(std::wstring &_target, enum newline_t _newLine) { - std::wstring::iterator iter = _target.begin(); - std::wstring::iterator iterEnd = _target.end(); - std::wstring temp; - const wchar_t *nl; - size_t CRs = 0, LFs = 0, CRLFs = 0; - if (_newLine == NEWLINE_LF) { - nl = L"\n"; - } else if (_newLine == NEWLINE_CR) { - nl = L"\r"; - } else { - nl = L"\r\n"; - } - - if (0 < _target.size()) { - wchar_t bNextChar = *iter++; - - while (true) { - if (L'\r' == bNextChar) { - temp += nl; // Newline - if (iter == iterEnd) break; // EOF - bNextChar = *iter++; // Retrive a character - if (L'\n' == bNextChar) { - if (iter == iterEnd) break; // EOF - bNextChar = *iter++; // Retrive a character - CRLFs++; - } else { - CRs++; - } - } else if (L'\n' == bNextChar) { - temp += nl; // Newline - if (iter == iterEnd) break; // EOF - bNextChar = *iter++; // Retrive a character - if (L'\r' == bNextChar) { // Broken LFCR, so don't count - if (iter == iterEnd) break; // EOF - bNextChar = *iter++; // Retrive a character - } else { - LFs++; - } - } else { - temp += bNextChar; // Not a newline - if (iter == iterEnd) break; // EOF - bNextChar = *iter++; // Retrive a character - } - } - } - - _target = temp; - - if (LFs > CRLFs && LFs >= CRs) { - return NEWLINE_LF; - } else if (CRs > LFs && CRs > CRLFs) { - return NEWLINE_CR; - } else { - return NEWLINE_CRLF; - } -} +HMODULE comctl32 = NULL; // Enables/Disables menu items from the smaller nearest 10 multiple to `_endID`. static void enableMenus(unsigned _endID, bool _enable) { @@ -279,10 +219,9 @@ void onCreate(HWND _hWnd, HINSTANCE _hInst) { // Tries to load the TaskDialog API which is a newer substitite of MessageBoxW. // This API is per monitor DPI aware but doesn't exist before Windows Vista. // To make the system select version 6 comctl32.dll, we don't use the full path here. - dll = NULL; - dll = LoadLibraryW(L"comctl32.dll"); - if (dll) { - taskDialog = (TaskDialog_t)(void *)GetProcAddress(dll, "TaskDialog"); + comctl32 = LoadLibraryW(L"comctl32.dll"); + if (comctl32) { + taskDialog = (TaskDialog_t)(void *)GetProcAddress(comctl32, "TaskDialog"); } #endif } @@ -292,6 +231,7 @@ void onDestroy() { DeleteObject(hEditFont); if (retEditBuf) delete[] retEditBuf; if (retInBuf) delete[] retInBuf; + if (comctl32) FreeLibrary(comctl32); } void onSize() { @@ -307,7 +247,7 @@ void onSize() { } #endif - size_t i; + int i; LOGFONTW rLogfont; // Button font @@ -658,7 +598,7 @@ INT_PTR CALLBACK memViewProc(HWND hDlg, unsigned uMsg, WPARAM wParam, LPARAM lPa } void setState(enum state_t _state, bool _force) { - size_t i; + int i; if (!_force && _state == state) return; if (_state == STATE_INIT) { @@ -740,47 +680,44 @@ void selMemView(unsigned _memPtr) { SendMessageW(hMemView, EM_SETSEL, (_memPtr - memViewStart) * 3, (_memPtr - memViewStart) * 3 + 2); } -void setMemory(const unsigned char *memory, unsigned size) { +void setMemory(const unsigned char *memory, int size) { if (!memory) { SetWindowTextW(hMemView, NULL); return; } - unsigned i; + int i; std::wstring wstrOut; for (i = memViewStart; i < memViewStart + 100 && i < size; ++i) { - // Converts to a hexadecimal string. - wchar_t wcOut[] = {0, 0, L' ', 0}; - unsigned char high = memory[i] >> 4, low = memory[i] & 0xF; - if (high < 10) { - wcOut[0] = L'0' + high; - } else { - wcOut[0] = L'A' + (high - 10); - } - if (low < 10) { - wcOut[1] = L'0' + low; - } else { - wcOut[1] = L'A' + (low - 10); - } + wchar_t wcOut[4]; + util::toHex(memory[i], wcOut); wstrOut.append(wcOut); } SetWindowTextW(hMemView, wstrOut.c_str()); } wchar_t *getEditor() { + if (retEditBuf) free(retEditBuf); + int editorSize = GetWindowTextLengthW(hEditor) + 1; - if (retEditBuf) delete[] retEditBuf; - retEditBuf = new wchar_t[editorSize]; - GetWindowTextW(hEditor, retEditBuf, editorSize); + if ((retEditBuf = (wchar_t *)malloc(sizeof(wchar_t) * editorSize))) { + GetWindowTextW(hEditor, retEditBuf, editorSize); + } else { + messageBox(hWnd, L"Memory allocation failed.", L"Error", MB_ICONWARNING); + } return retEditBuf; } wchar_t *getInput() { + if (retInBuf) free(retInBuf); + int inputSize = GetWindowTextLengthW(hInput) + 1; - if (retInBuf) delete[] retInBuf; - retInBuf = new wchar_t[inputSize]; - GetWindowTextW(hInput, retInBuf, inputSize); + if ((retInBuf = (wchar_t *)malloc(sizeof(wchar_t) * inputSize))) { + GetWindowTextW(hInput, retInBuf, inputSize); + } else { + messageBox(hWnd, L"Memory allocation failed.", L"Error", MB_ICONWARNING); + } return retInBuf; } @@ -794,24 +731,39 @@ void appendOutput(const wchar_t *_str) { } void switchWordwrap() { - wordwrap = !wordwrap; - int editorSize = GetWindowTextLengthW(hEditor) + 1; - wchar_t *wcEditor = new wchar_t[editorSize]; + wchar_t *wcEditor = (wchar_t *)malloc(sizeof(wchar_t) * editorSize); + if (!wcEditor) { + messageBox(hWnd, L"Memory allocation failed.", L"Error", MB_ICONWARNING); + return; + } GetWindowTextW(hEditor, wcEditor, editorSize); bool isModified = SendMessageW(hEditor, EM_GETMODIFY, 0, 0) != 0; DestroyWindow(hEditor); int inputSize = GetWindowTextLengthW(hInput) + 1; - wchar_t *wcInput = new wchar_t[inputSize]; + wchar_t *wcInput = (wchar_t *)malloc(sizeof(wchar_t) * inputSize); + if (!wcInput) { + free(wcEditor); + messageBox(hWnd, L"Memory allocation failed.", L"Error", MB_ICONWARNING); + return; + } GetWindowTextW(hInput, wcInput, inputSize); DestroyWindow(hInput); int outputSize = GetWindowTextLengthW(hOutput) + 1; - wchar_t *wcOutput = new wchar_t[outputSize]; + wchar_t *wcOutput = (wchar_t *)malloc(sizeof(wchar_t) * outputSize); + if (!wcOutput) { + free(wcEditor); + free(wcInput); + messageBox(hWnd, L"Memory allocation failed.", L"Error", MB_ICONWARNING); + return; + } GetWindowTextW(hOutput, wcOutput, outputSize); DestroyWindow(hOutput); + wordwrap = !wordwrap; + // Program editor hEditor = CreateWindowExW(0, L"EDIT", L"", @@ -846,9 +798,9 @@ void switchWordwrap() { SetWindowTextW(hInput, wcInput); SetWindowTextW(hOutput, wcOutput); - delete[] wcEditor; - delete[] wcInput; - delete[] wcOutput; + free(wcEditor); + free(wcInput); + free(wcOutput); } void switchTheme() { @@ -937,7 +889,7 @@ void openFile(bool _newFile, const wchar_t *_fileName) { SendMessageW(hEditor, EM_SETMODIFY, FALSE, 0); wstrFileName = L""; withBOM = false; - newLine = NEWLINE_CRLF; + newLine = util::NEWLINE_CRLF; return; } @@ -968,25 +920,37 @@ void openFile(bool _newFile, const wchar_t *_fileName) { } DWORD fileSize = GetFileSize(hFile, NULL), readLen; - char *fileBuf = new char[fileSize]; - int padding = 0; + if (fileSize >= 65536) { + messageBox(hWnd, L"This file is too large.", L"Error", MB_ICONWARNING); + return; + } + + char *fileBuf = (char *)malloc(sizeof(char) * fileSize); + if (!fileBuf) { + messageBox(hWnd, L"Memory allocation failed.", L"Error", MB_ICONWARNING); + return; + } + ReadFile(hFile, fileBuf, fileSize, &readLen, NULL); CloseHandle(hFile); + + int padding = 0; if (fileBuf[0] == '\xEF' && fileBuf[1] == '\xBB' && fileBuf[2] == '\xBF') padding = 3; // BOM + int length = MultiByteToWideChar(CP_UTF8, 0, fileBuf + padding, readLen - padding, NULL, 0); - if (length >= 0x7FFFFFFE) { - messageBox(hWnd, L"This file is too large.", L"Error", MB_ICONWARNING); - delete[] fileBuf; + wchar_t *wcFileBuf = (wchar_t *)calloc(length + 1, sizeof(wchar_t)); + if (!wcFileBuf) { + free(fileBuf); + messageBox(hWnd, L"Memory allocation failed.", L"Error", MB_ICONWARNING); return; } - wchar_t *wcFileBuf = new wchar_t[length + 1]; - wcFileBuf[length] = 0; + MultiByteToWideChar(CP_UTF8, 0, fileBuf + padding, readLen - padding, wcFileBuf, length); - delete[] fileBuf; + free(fileBuf); std::wstring converted = wcFileBuf; - delete[] wcFileBuf; - newLine = convertCRLF(converted, NEWLINE_CRLF); + free(wcFileBuf); + newLine = util::convertCRLF(converted, util::NEWLINE_CRLF); SetWindowTextW(hEditor, converted.c_str()); SendMessageW(hEditor, EM_SETMODIFY, FALSE, 0); @@ -1023,14 +987,22 @@ bool saveFile(bool _isOverwrite) { } int editorSize = GetWindowTextLengthW(hEditor) + 1; - wchar_t *wcEditor = new wchar_t[editorSize]; + wchar_t *wcEditor = (wchar_t *)malloc(sizeof(wchar_t) * editorSize); + if (!wcEditor) { + messageBox(hWnd, L"Memory allocation failed.", L"Error", MB_ICONWARNING); + return false; + } GetWindowTextW(hEditor, wcEditor, editorSize); std::wstring converted = wcEditor; - delete[] wcEditor; + free(wcEditor); - if (newLine != NEWLINE_CRLF) convertCRLF(converted, newLine); + if (newLine != util::NEWLINE_CRLF) util::convertCRLF(converted, newLine); int length = WideCharToMultiByte(CP_UTF8, 0, converted.c_str(), -1, NULL, 0, NULL, NULL); - char *szEditor = new char[length]; + char *szEditor = (char *)malloc(sizeof(char) * length); + if (!szEditor) { + messageBox(hWnd, L"Memory allocation failed.", L"Error", MB_ICONWARNING); + return false; + } WideCharToMultiByte(CP_UTF8, 0, converted.c_str(), -1, szEditor, length, NULL, NULL); HANDLE hFile = CreateFileW(wcFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, @@ -1044,7 +1016,7 @@ bool saveFile(bool _isOverwrite) { if (withBOM) WriteFile(hFile, "\xEF\xBB\xBF", 3, &dwTemp, NULL); // BOM WriteFile(hFile, szEditor, length - 1, &dwTemp, NULL); CloseHandle(hFile); - delete[] szEditor; + free(szEditor); SendMessageW(hEditor, EM_SETMODIFY, FALSE, 0); wstrFileName = wcFileName; diff --git a/ui.hpp b/ui.hpp index 4798712..0ba6ed7 100644 --- a/ui.hpp +++ b/ui.hpp @@ -56,11 +56,11 @@ void selMemView(unsigned int _memPtr); // Memory view settings dialog. INT_PTR CALLBACK memViewProc(HWND hwndDlg, unsigned int uMsg, WPARAM wParam, LPARAM lParam); -// Retrieves the editor content to the returned buffer. +// Retrieves the editor content to the returned buffer. Might fail with NULL. // Previously returned pointer gets invalidated on each call. wchar_t *getEditor(); -// Retrieves the input content to the returned buffer. +// Retrieves the input content to the returned buffer. Might fail with NULL. // Previously returned pointer gets invalidated on each call. wchar_t *getInput(); @@ -71,7 +71,7 @@ void setOutput(const wchar_t *_str); void appendOutput(const wchar_t *_str); // Sets memory. Clears the memory view when NULL is given. -void setMemory(const unsigned char *memory, unsigned size = 0); +void setMemory(const unsigned char *memory, int size = 0); // Switches between wordwrap enabled and disabled for edit controls. void switchWordwrap(); diff --git a/util.cpp b/util.cpp new file mode 100644 index 0000000..8fb1b08 --- /dev/null +++ b/util.cpp @@ -0,0 +1,209 @@ +#include "util.hpp" + +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include + +#include +#include "ui.hpp" + +namespace util { +const unsigned char *UTF8Parser::flush() { + if (index == 0) return NULL; + for (int i = 0; i < index; ++i) token[i] = '?'; + state = index = 0; + return token; +} + +const unsigned char *UTF8Parser::add(char chr) { + if (state == 0) { // Reading the first character of a token. + memset(token, 0, sizeof(token)); + + // Got a later byte character, which is unexpected here. + if ((chr & 0xC0) == 0x80) { + token[0] = '?'; + return token; + } + + token[0] = chr; + + if (!(chr & 0x80)) return token; // ASCII + + char tmp = chr << 2; + for (int i = 1; true; ++i) { + if (!(tmp & 0x80)) { + state = i; + break; + } + if (i == 6) { + state = 7; + break; + } + tmp <<= 1; + } + ++index; + } else { // Reading a later byte character of a token. + // Got a first byte character, which is unexpected here. + if ((chr & 0xC0) != 0x80) { + for (int i = 0; i < index; ++i) token[i] = '?'; + state = index = 0; + return token; + } + + token[index++] = chr; + --state; + + if (state == 0) { + index = 0; + return token; + } + } + + return NULL; +} + +static inline bool isHex(wchar_t chr) { + return (chr >= L'0' && chr <= L'9') || (chr >= L'A' && chr <= L'F') || + (chr >= L'a' && chr <= L'f'); +} + +bool parseHex(HWND hWnd, const wchar_t *hexInput, unsigned char **dest) { + wchar_t hex[2]; + std::string tmp; + int hexLen = 0, i; + + for (i = 0; true; ++i) { + if (isHex(hexInput[i])) { + if (hexLen >= 2) { + ui::messageBox(hWnd, L"Each memory value must fit in 8-bit.", L"Error", MB_ICONWARNING); + *dest = NULL; + return false; + } + if (hexInput[i] > L'Z') { // Aligns to the upper case. + hex[hexLen++] = hexInput[i] - (L'a' - L'A'); + } else { + hex[hexLen++] = hexInput[i]; + } + } else if (iswspace(hexInput[i]) || !hexInput[i]) { + if (hexLen == 1) { + if (hex[0] < L'A') { + tmp += (char)(hex[0] - L'0'); + } else { + tmp += (char)(hex[0] - L'A' + 10); + } + hexLen = 0; + } else if (hexLen == 2) { + if (hex[0] < L'A') { + tmp += (char)((hex[0] - L'0') << 4); + } else { + tmp += (char)((hex[0] - L'A' + 10) << 4); + } + if (hex[1] < L'A') { + tmp[tmp.size() - 1] += hex[1] - L'0'; + } else { + tmp[tmp.size() - 1] += hex[1] - L'A' + 10; + } + hexLen = 0; + } + } else { + ui::messageBox(hWnd, L"Invalid input.", L"Error", MB_ICONWARNING); + *dest = NULL; + return false; + } + + if (hexInput[i] == L'\0') break; + } + + if (tmp.size() == 0) { + *dest = NULL; + return true; + } + + *dest = (unsigned char *)malloc(tmp.size() + 1); + if (!*dest) { + ui::messageBox(hWnd, L"Memory allocation failed.", L"Error", MB_ICONWARNING); + return false; + } + + memcpy(*dest, tmp.c_str(), tmp.size() + 1); + return true; +} + +void toHex(unsigned char num, wchar_t *dest) { + unsigned char high = num >> 4, low = num & 0xF; + + if (high < 10) { + dest[0] = L'0' + high; + } else { + dest[0] = L'A' + (high - 10); + } + + if (low < 10) { + dest[1] = L'0' + low; + } else { + dest[1] = L'A' + (low - 10); + } + + dest[2] = L' '; + dest[3] = 0; +} + +enum newline_t convertCRLF(std::wstring &_target, enum newline_t _newLine) { + std::wstring::iterator iter = _target.begin(); + std::wstring::iterator iterEnd = _target.end(); + std::wstring temp; + const wchar_t *nl; + size_t CRs = 0, LFs = 0, CRLFs = 0; + if (_newLine == NEWLINE_LF) { + nl = L"\n"; + } else if (_newLine == NEWLINE_CR) { + nl = L"\r"; + } else { + nl = L"\r\n"; + } + + if (0 < _target.size()) { + wchar_t bNextChar = *iter++; + + while (true) { + if (L'\r' == bNextChar) { + temp += nl; // Newline + if (iter == iterEnd) break; // EOF + bNextChar = *iter++; // Retrive a character + if (L'\n' == bNextChar) { + if (iter == iterEnd) break; // EOF + bNextChar = *iter++; // Retrive a character + CRLFs++; + } else { + CRs++; + } + } else if (L'\n' == bNextChar) { + temp += nl; // Newline + if (iter == iterEnd) break; // EOF + bNextChar = *iter++; // Retrive a character + if (L'\r' == bNextChar) { // Broken LFCR, so don't count + if (iter == iterEnd) break; // EOF + bNextChar = *iter++; // Retrive a character + } else { + LFs++; + } + } else { + temp += bNextChar; // Not a newline + if (iter == iterEnd) break; // EOF + bNextChar = *iter++; // Retrive a character + } + } + } + + _target = temp; + + if (LFs > CRLFs && LFs >= CRs) { + return NEWLINE_LF; + } else if (CRs > LFs && CRs > CRLFs) { + return NEWLINE_CR; + } else { + return NEWLINE_CRLF; + } +} +} // namespace util diff --git a/util.hpp b/util.hpp new file mode 100644 index 0000000..e1efe7b --- /dev/null +++ b/util.hpp @@ -0,0 +1,45 @@ +#ifndef UTIL_HPP_ +#define UTIL_HPP_ + +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include + +#include + +namespace util { +enum newline_t { NEWLINE_CRLF, NEWLINE_LF, NEWLINE_CR }; + +class UTF8Parser { + public: + UTF8Parser() : state(0), index(0){}; + + // Resets and returns the remaining bytes if any, all transformed to "?". + // Returns NULL when no bytes are left. + const unsigned char *flush(); + + // Adds a byte to the internal buffer until it becomes one token. + // Returns NULL when more bytes are required, and returns a token when completed. + // Note that this function can return multiple "?" characters when it is given a corrupted UTF-8 + // sequence. + const unsigned char *add(char chr); + + private: + int state, index; + unsigned char token[10]; +}; + +// Converts a hex string to a byte sequence. This function allocates `dest` unless it is NULL. +// It's your responsibility to `free()` it. +// Return value of true indicates success, otherwise indicates failure. +extern bool parseHex(HWND hWnd, const wchar_t *hexInput, unsigned char **dest); + +// Converts a byte to a hex string. `dest` must have at least 4 elements. +extern void toHex(unsigned char num, wchar_t *dest); + +// Translates newlines and returns the previous newline code. +// `_target`: A `std::wstring` to operate on, `_newLine`: A desired newline code. +enum newline_t convertCRLF(std::wstring &_target, enum newline_t _newLine); +} // namespace util +#endif From 5a44d0a386f131600e49c228f939647a112591b2 Mon Sep 17 00:00:00 2001 From: watamario15 Date: Wed, 29 Mar 2023 01:31:14 +0900 Subject: [PATCH 4/9] Realtime output for UTF-8 --- evc4proj/brainfuck_evc4.vcp | 12 +++ main.cpp | 160 +++++++++++++++++++----------------- resource.rc | 8 +- tokenizer.cpp | 86 +++++++++++++++++++ tokenizer.hpp | 46 +++++++++++ ui.cpp | 18 ++-- util.cpp | 59 +------------ util.hpp | 23 +----- 8 files changed, 245 insertions(+), 167 deletions(-) create mode 100644 tokenizer.cpp create mode 100644 tokenizer.hpp diff --git a/evc4proj/brainfuck_evc4.vcp b/evc4proj/brainfuck_evc4.vcp index f852d79..52f8271 100644 --- a/evc4proj/brainfuck_evc4.vcp +++ b/evc4proj/brainfuck_evc4.vcp @@ -112,12 +112,20 @@ SOURCE=..\main.cpp DEP_CPP_MAIN_=\ "..\bf.hpp"\ "..\resource.h"\ + "..\tokenizer.hpp"\ "..\ui.hpp"\ "..\util.hpp"\ # End Source File # Begin Source File +SOURCE=..\tokenizer.cpp +DEP_CPP_TOKEN=\ + "..\tokenizer.hpp"\ + +# End Source File +# Begin Source File + SOURCE=..\ui.cpp DEP_CPP_UI_CP=\ "..\bf.hpp"\ @@ -149,6 +157,10 @@ SOURCE=..\resource.h # End Source File # Begin Source File +SOURCE=..\tokenizer.hpp +# End Source File +# Begin Source File + SOURCE=..\ui.hpp # End Source File # Begin Source File diff --git a/main.cpp b/main.cpp index 74937e2..c341da1 100644 --- a/main.cpp +++ b/main.cpp @@ -1,5 +1,3 @@ -#include - #ifndef _UNICODE #define _UNICODE #endif @@ -65,24 +63,34 @@ typedef unsigned int tret_t; #include "resource.h" #include "ui.hpp" #include "util.hpp" +#include "tokenizer.hpp" enum ctrlthread_t { CTRLTHREAD_RUN, CTRLTHREAD_PAUSE, CTRLTHREAD_END }; -static class Brainfuck *g_bf; +static class Brainfuck g_bf; +static class UTF8Tokenizer g_u8Tokenizer; +static class SJISTokenizer g_sjisTokenizer; static unsigned int g_timerID = 0; static wchar_t *g_cmdLine; static bool g_prevCR = false; -static std::string g_outBuf; static HANDLE g_hThread = NULL; static volatile enum ctrlthread_t g_ctrlThread = CTRLTHREAD_RUN; +// Stops the timer identified by g_timerID if it's not 0 +static inline void stopTimer() { + if (g_timerID) { + timeKillEvent(g_timerID); + timeEndPeriod(ui::speed); + g_timerID = 0; + } +} + // Initializes the Brainfuck module. Returns false on an invalid hexadecimal input. static bool bfInit() { static unsigned char *input = NULL; ui::setOutput(L""); ui::setMemory(NULL); - g_outBuf = ""; g_prevCR = false; if (input) free(input); @@ -101,9 +109,15 @@ static bool bfInit() { inLen--; } + if (ui::outCharSet == IDM_BF_OUTPUT_UTF8) { + g_u8Tokenizer.flush(); + } else if (ui::outCharSet == IDM_BF_OUTPUT_SJIS) { + g_sjisTokenizer.flush(); + } + wchar_t *wcEditor = ui::getEditor(); if (!wcEditor) return false; - g_bf->reset(wcslen(wcEditor), wcEditor, inLen, input); + g_bf.reset(wcslen(wcEditor), wcEditor, inLen, input); return true; } @@ -112,53 +126,41 @@ static bool bfInit() { static enum Brainfuck::result_t bfNext() { unsigned char output; bool didOutput; - g_bf->setBehavior(ui::noInput, ui::wrapInt, ui::signedness, ui::breakpoint); - - enum Brainfuck::result_t result = g_bf->next(&output, &didOutput); - if (result == Brainfuck::RESULT_ERR) { - if (g_timerID) { - timeKillEvent(g_timerID); - timeEndPeriod(ui::speed); - g_timerID = 0; + + g_bf.setBehavior(ui::noInput, ui::wrapInt, ui::signedness, ui::breakpoint); + enum Brainfuck::result_t result = g_bf.next(&output, &didOutput); + + if (result == Brainfuck::RESULT_FIN || result == Brainfuck::RESULT_ERR) { + if (ui::outCharSet == IDM_BF_OUTPUT_UTF8) { + const wchar_t *rest = g_u8Tokenizer.flush(); + if (rest) ui::appendOutput(rest); + } else if (ui::outCharSet == IDM_BF_OUTPUT_SJIS) { + const wchar_t *rest = g_sjisTokenizer.flush(); + if (rest) ui::appendOutput(rest); } - ui::messageBox(ui::hWnd, g_bf->getLastError(), L"Error", MB_ICONWARNING); - result = Brainfuck::RESULT_FIN; + return result; } - if (result == Brainfuck::RESULT_FIN) { - if (!g_outBuf.empty()) { - int codePage = (ui::outCharSet == IDM_BF_OUTPUT_SJIS) ? 932 : CP_UTF8; - int outLen = MultiByteToWideChar(codePage, 0, g_outBuf.c_str(), (int)g_outBuf.size() + 1, - (wchar_t *)NULL, 0); - wchar_t *wcOut = (wchar_t *)malloc(sizeof(wchar_t) * outLen); - if (!wcOut) { - ui::messageBox(ui::hWnd, L"Memory allocation failed.", L"Error", MB_ICONWARNING); - } else { - MultiByteToWideChar(codePage, 0, g_outBuf.c_str(), (int)g_outBuf.size() + 1, wcOut, outLen); - ui::setOutput(wcOut); - free(wcOut); - } - } - } else { - if (didOutput) { + if (didOutput) { + if (ui::outCharSet == IDM_BF_OUTPUT_HEX) { + wchar_t wcOut[4]; + util::toHex(output, wcOut); + ui::appendOutput(wcOut); + } else { + // Align newlines to CRLF. + if (g_prevCR && output != '\n') ui::appendOutput(L"\n"); + if (!g_prevCR && output == '\n') ui::appendOutput(L"\r"); + g_prevCR = output == '\r'; + if (ui::outCharSet == IDM_BF_OUTPUT_ASCII) { - wchar_t wcOut[] = {0, 0}; - MultiByteToWideChar(CP_UTF8, 0, (char *)&output, 1, wcOut, 1); - // Align newlines to CRLF. - if (g_prevCR && wcOut[0] != L'\n') ui::appendOutput(L"\n"); - if (!g_prevCR && wcOut[0] == L'\n') ui::appendOutput(L"\r"); - g_prevCR = wcOut[0] == L'\r'; + wchar_t wcOut[] = {output, 0}; ui::appendOutput(wcOut); - } else if (ui::outCharSet == IDM_BF_OUTPUT_HEX) { - wchar_t wcOut[4]; - util::toHex(output, wcOut); - ui::appendOutput(wcOut); - } else { - // Align newlines to CRLF. - if (g_prevCR && output != '\n') g_outBuf += '\n'; - if (!g_prevCR && output == '\n') g_outBuf += '\r'; - g_prevCR = output == L'\r'; - g_outBuf += output; + } else if (ui::outCharSet == IDM_BF_OUTPUT_UTF8) { + const wchar_t *converted = g_u8Tokenizer.add(output); + if (converted) ui::appendOutput(converted); + } else if (ui::outCharSet == IDM_BF_OUTPUT_SJIS) { + const wchar_t *converted = g_sjisTokenizer.add(output); + if (converted) ui::appendOutput(converted); } } } @@ -170,7 +172,7 @@ static enum Brainfuck::result_t bfNext() { tret_t WINAPI threadRunner(void *lpParameter) { UNREFERENCED_PARAMETER(lpParameter); - HANDLE hEvent = 0; + HANDLE hEvent = NULL; if (ui::speed != 0) { hEvent = CreateEventW(NULL, FALSE, TRUE, NULL); if (timeBeginPeriod(ui::speed) == TIMERR_NOERROR) { @@ -192,28 +194,28 @@ tret_t WINAPI threadRunner(void *lpParameter) { if ((result = bfNext()) != Brainfuck::RESULT_RUN) break; if (ui::debug) { unsigned size; - const unsigned char *memory = g_bf->getMemory(&size); + const unsigned char *memory = g_bf.getMemory(&size); ui::setMemory(memory, size); - ui::selProg(g_bf->getProgPtr()); + ui::selProg(g_bf.getProgPtr()); } } - if (g_timerID) { - timeKillEvent(g_timerID); - timeEndPeriod(ui::speed); - CloseHandle(hEvent); - g_timerID = 0; - } - if (g_ctrlThread == CTRLTHREAD_END) { // Terminated - g_bf->reset(); - } else { // Paused, finished, or error - if (g_ctrlThread == CTRLTHREAD_RUN) { // Finished or error - ui::setState(result == Brainfuck::RESULT_FIN ? ui::STATE_FINISH : ui::STATE_PAUSE); + stopTimer(); + if (hEvent) CloseHandle(hEvent); + + if (g_ctrlThread == CTRLTHREAD_END) { + ui::setState(ui::STATE_INIT); + } else { // Paused, finished, error, or breakpoint + ui::setState(result == Brainfuck::RESULT_RUN || result == Brainfuck::RESULT_BREAK + ? ui::STATE_PAUSE + : ui::STATE_FINISH); + if (result == Brainfuck::RESULT_ERR) { + ui::messageBox(ui::hWnd, g_bf.getLastError(), L"Brainfuck Error", MB_ICONWARNING); } unsigned size; - const unsigned char *memory = g_bf->getMemory(&size); + const unsigned char *memory = g_bf.getMemory(&size); ui::setMemory(memory, size); - ui::selProg(g_bf->getProgPtr()); + ui::selProg(g_bf.getProgPtr()); } PostMessageW(ui::hWnd, WM_APP_THREADEND, 0, 0); @@ -226,7 +228,6 @@ static LRESULT CALLBACK wndProc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPA switch (uMsg) { case WM_CREATE: - g_bf = new Brainfuck(); ui::onCreate(hWnd, ((LPCREATESTRUCT)(lParam))->hInstance); if (g_cmdLine[0]) { ui::openFile(false, g_cmdLine); @@ -291,7 +292,6 @@ static LRESULT CALLBACK wndProc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPA break; case WM_DESTROY: - delete g_bf; DeleteObject(BGDark); ui::onDestroy(); PostQuitMessage(0); @@ -311,7 +311,8 @@ static LRESULT CALLBACK wndProc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPA if (g_hThread) { SetThreadPriority(g_hThread, THREAD_PRIORITY_BELOW_NORMAL); } else { - ui::messageBox(hWnd, L"Failed to create a runner thread.", L"Error", MB_ICONERROR); + ui::messageBox(hWnd, L"Failed to create a runner thread.", L"Internal Error", + MB_ICONERROR); break; } @@ -325,26 +326,31 @@ static LRESULT CALLBACK wndProc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPA didInit = true; } - ui::setState(bfNext() == Brainfuck::RESULT_FIN ? ui::STATE_FINISH : ui::STATE_PAUSE); + Brainfuck::result_t result = bfNext(); + ui::setState(result == Brainfuck::RESULT_FIN || result == Brainfuck::RESULT_ERR + ? ui::STATE_FINISH + : ui::STATE_PAUSE); + if (result == Brainfuck::RESULT_ERR) { + ui::messageBox(ui::hWnd, g_bf.getLastError(), L"Brainfuck Error", MB_ICONWARNING); + } + unsigned size; - const unsigned char *memory = g_bf->getMemory(&size); + const unsigned char *memory = g_bf.getMemory(&size); ui::setMemory(memory, size); - ui::selProg(g_bf->getProgPtr()); + ui::selProg(g_bf.getProgPtr()); break; } case IDC_CMDBTN_FIRST + 2: // Pause if (g_hThread) g_ctrlThread = CTRLTHREAD_PAUSE; - ui::setState(ui::STATE_PAUSE); break; case IDC_CMDBTN_FIRST + 3: // End if (g_hThread) { g_ctrlThread = CTRLTHREAD_END; } else { - g_bf->reset(); + ui::setState(ui::STATE_INIT); } - ui::setState(ui::STATE_INIT); didInit = false; break; @@ -446,7 +452,7 @@ static LRESULT CALLBACK wndProc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPA case IDM_BF_BREAKPOINT: ui::breakpoint = !ui::breakpoint; - g_bf->setBehavior(ui::noInput, ui::wrapInt, ui::signedness, ui::breakpoint); + g_bf.setBehavior(ui::noInput, ui::wrapInt, ui::signedness, ui::breakpoint); break; case IDM_OPT_SPEED_FASTEST: @@ -469,7 +475,7 @@ static LRESULT CALLBACK wndProc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPA if (DialogBoxW(ui::hInst, L"memviewopt", hWnd, ui::memViewProc) == IDOK && ui::state != ui::STATE_INIT) { unsigned size; - const unsigned char *memory = g_bf->getMemory(&size); + const unsigned char *memory = g_bf.getMemory(&size); ui::setMemory(memory, size); } break; @@ -479,11 +485,11 @@ static LRESULT CALLBACK wndProc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPA break; case IDM_OPT_HLTPROG: - ui::selProg(g_bf->getProgPtr()); + ui::selProg(g_bf.getProgPtr()); break; case IDM_OPT_HLTMEM: - ui::selMemView(g_bf->getMemPtr()); + ui::selMemView(g_bf.getMemPtr()); break; case IDM_OPT_DARK: diff --git a/resource.rc b/resource.rc index b37d74a..c0ede98 100644 --- a/resource.rc +++ b/resource.rc @@ -73,10 +73,10 @@ menu MENU } POPUP "&Output Charset" { - MENUITEM "&ASCII (Real Time Output)", IDM_BF_OUTPUT_ASCII - MENUITEM "&UTF-8 (Bufferred Output)", IDM_BF_OUTPUT_UTF8 - MENUITEM "Shift_&JIS (Bufferred Output)", IDM_BF_OUTPUT_SJIS - MENUITEM "&Hexadecimal (Real Time Output)", IDM_BF_OUTPUT_HEX + MENUITEM "&ASCII", IDM_BF_OUTPUT_ASCII + MENUITEM "&UTF-8", IDM_BF_OUTPUT_UTF8 + MENUITEM "Shift_&JIS", IDM_BF_OUTPUT_SJIS + MENUITEM "&Hexadecimal", IDM_BF_OUTPUT_HEX } POPUP "Input &Charset" { diff --git a/tokenizer.cpp b/tokenizer.cpp new file mode 100644 index 0000000..ac8a00c --- /dev/null +++ b/tokenizer.cpp @@ -0,0 +1,86 @@ +#include "tokenizer.hpp" + +#include + +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include + +const wchar_t *UTF8Tokenizer::flush() { + if (index == 0) return NULL; + + memset(result, 0, sizeof(result)); + for (int i = 0; i < index; ++i) result[i] = L'?'; + + state = index = 0; + + return result; +} + +const wchar_t *UTF8Tokenizer::add(unsigned char chr) { + if (state == 0) { // Reading the first character of a token. + memset(token, 0, sizeof(token)); + + // Got a later byte character, which is unexpected here. + if ((chr & 0xC0) == 0x80) { + memset(result, 0, sizeof(result)); + result[0] = L'?'; + return result; + } + + // ASCII + if (!(chr & 0x80)) { + memset(result, 0, sizeof(result)); + result[0] = chr; + return result; + } + + token[0] = chr; + + char tmp = chr << 2; + for (int i = 1; true; ++i) { + if (!(tmp & 0x80)) { + state = i; + break; + } + if (i == 6) { + state = 7; + break; + } + tmp <<= 1; + } + ++index; + } else { // Reading a later byte character of a token. + // Got a first byte character, which is unexpected here. + if ((chr & 0xC0) != 0x80) { + memset(result, 0, sizeof(result)); + for (int i = 0; i < index; ++i) result[i] = L'?'; + state = index = 0; + return result; + } + + token[index++] = chr; + --state; + + if (state == 0) { + memset(result, 0, sizeof(result)); + MultiByteToWideChar(CP_UTF8, 0, (char *)token, -1, result, + sizeof(result) / sizeof(result[0])); + index = 0; + return result; + } + } + + return NULL; +} + +const wchar_t *SJISTokenizer::flush() { + // TODO + return NULL; +} + +const wchar_t *SJISTokenizer::add(unsigned char chr) { + // TODO + return NULL; +} diff --git a/tokenizer.hpp b/tokenizer.hpp new file mode 100644 index 0000000..5e6642a --- /dev/null +++ b/tokenizer.hpp @@ -0,0 +1,46 @@ +#ifndef TOKENIZER_HPP_ +#define TOKENIZER_HPP_ + +#include + +class UTF8Tokenizer { + public: + UTF8Tokenizer() : state(0), index(0){}; + + // Resets and returns the remaining bytes if any, all transformed to "?". + // Returns NULL if no bytes are left. + const wchar_t *flush(); + + // Adds a byte to the internal buffer until it becomes a character. + // Returns NULL if more bytes are required, and returns a pointer to a string if a complete + // character is built or this function gets a invalid UTF-8 byte. Note that this function can + // return multiple "?" characters if it is given a corrupted UTF-8 sequence. + const wchar_t *add(unsigned char chr); + + private: + int state, index; + unsigned char token[16]; + wchar_t result[16]; +}; + +class SJISTokenizer { + public: + SJISTokenizer() : state(0), index(0){}; + + // Resets and returns the remaining bytes if any, all transformed to "?". + // Returns NULL if no bytes are left. + const wchar_t *flush(); + + // Adds a byte to the internal buffer until it becomes a character. + // Returns NULL if more bytes are required, and returns a pointer to a string if a complete + // character is built or this function gets a invalid Shift_JIS byte. Note that this function can + // return multiple "?" characters if it is given a corrupted Shift_JIS sequence. + const wchar_t *add(unsigned char chr); + + private: + int state, index; + unsigned char token[16]; + wchar_t result[16]; +}; + +#endif diff --git a/ui.cpp b/ui.cpp index 72157bd..afb2ad8 100644 --- a/ui.cpp +++ b/ui.cpp @@ -703,7 +703,7 @@ wchar_t *getEditor() { if ((retEditBuf = (wchar_t *)malloc(sizeof(wchar_t) * editorSize))) { GetWindowTextW(hEditor, retEditBuf, editorSize); } else { - messageBox(hWnd, L"Memory allocation failed.", L"Error", MB_ICONWARNING); + messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); } return retEditBuf; @@ -716,7 +716,7 @@ wchar_t *getInput() { if ((retInBuf = (wchar_t *)malloc(sizeof(wchar_t) * inputSize))) { GetWindowTextW(hInput, retInBuf, inputSize); } else { - messageBox(hWnd, L"Memory allocation failed.", L"Error", MB_ICONWARNING); + messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); } return retInBuf; @@ -734,7 +734,7 @@ void switchWordwrap() { int editorSize = GetWindowTextLengthW(hEditor) + 1; wchar_t *wcEditor = (wchar_t *)malloc(sizeof(wchar_t) * editorSize); if (!wcEditor) { - messageBox(hWnd, L"Memory allocation failed.", L"Error", MB_ICONWARNING); + messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); return; } GetWindowTextW(hEditor, wcEditor, editorSize); @@ -745,7 +745,7 @@ void switchWordwrap() { wchar_t *wcInput = (wchar_t *)malloc(sizeof(wchar_t) * inputSize); if (!wcInput) { free(wcEditor); - messageBox(hWnd, L"Memory allocation failed.", L"Error", MB_ICONWARNING); + messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); return; } GetWindowTextW(hInput, wcInput, inputSize); @@ -756,7 +756,7 @@ void switchWordwrap() { if (!wcOutput) { free(wcEditor); free(wcInput); - messageBox(hWnd, L"Memory allocation failed.", L"Error", MB_ICONWARNING); + messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); return; } GetWindowTextW(hOutput, wcOutput, outputSize); @@ -927,7 +927,7 @@ void openFile(bool _newFile, const wchar_t *_fileName) { char *fileBuf = (char *)malloc(sizeof(char) * fileSize); if (!fileBuf) { - messageBox(hWnd, L"Memory allocation failed.", L"Error", MB_ICONWARNING); + messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); return; } @@ -941,7 +941,7 @@ void openFile(bool _newFile, const wchar_t *_fileName) { wchar_t *wcFileBuf = (wchar_t *)calloc(length + 1, sizeof(wchar_t)); if (!wcFileBuf) { free(fileBuf); - messageBox(hWnd, L"Memory allocation failed.", L"Error", MB_ICONWARNING); + messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); return; } @@ -989,7 +989,7 @@ bool saveFile(bool _isOverwrite) { int editorSize = GetWindowTextLengthW(hEditor) + 1; wchar_t *wcEditor = (wchar_t *)malloc(sizeof(wchar_t) * editorSize); if (!wcEditor) { - messageBox(hWnd, L"Memory allocation failed.", L"Error", MB_ICONWARNING); + messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); return false; } GetWindowTextW(hEditor, wcEditor, editorSize); @@ -1000,7 +1000,7 @@ bool saveFile(bool _isOverwrite) { int length = WideCharToMultiByte(CP_UTF8, 0, converted.c_str(), -1, NULL, 0, NULL, NULL); char *szEditor = (char *)malloc(sizeof(char) * length); if (!szEditor) { - messageBox(hWnd, L"Memory allocation failed.", L"Error", MB_ICONWARNING); + messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); return false; } WideCharToMultiByte(CP_UTF8, 0, converted.c_str(), -1, szEditor, length, NULL, NULL); diff --git a/util.cpp b/util.cpp index 8fb1b08..0ea0816 100644 --- a/util.cpp +++ b/util.cpp @@ -1,68 +1,15 @@ #include "util.hpp" +#include + #ifndef NOMINMAX #define NOMINMAX #endif #include -#include #include "ui.hpp" namespace util { -const unsigned char *UTF8Parser::flush() { - if (index == 0) return NULL; - for (int i = 0; i < index; ++i) token[i] = '?'; - state = index = 0; - return token; -} - -const unsigned char *UTF8Parser::add(char chr) { - if (state == 0) { // Reading the first character of a token. - memset(token, 0, sizeof(token)); - - // Got a later byte character, which is unexpected here. - if ((chr & 0xC0) == 0x80) { - token[0] = '?'; - return token; - } - - token[0] = chr; - - if (!(chr & 0x80)) return token; // ASCII - - char tmp = chr << 2; - for (int i = 1; true; ++i) { - if (!(tmp & 0x80)) { - state = i; - break; - } - if (i == 6) { - state = 7; - break; - } - tmp <<= 1; - } - ++index; - } else { // Reading a later byte character of a token. - // Got a first byte character, which is unexpected here. - if ((chr & 0xC0) != 0x80) { - for (int i = 0; i < index; ++i) token[i] = '?'; - state = index = 0; - return token; - } - - token[index++] = chr; - --state; - - if (state == 0) { - index = 0; - return token; - } - } - - return NULL; -} - static inline bool isHex(wchar_t chr) { return (chr >= L'0' && chr <= L'9') || (chr >= L'A' && chr <= L'F') || (chr >= L'a' && chr <= L'f'); @@ -122,7 +69,7 @@ bool parseHex(HWND hWnd, const wchar_t *hexInput, unsigned char **dest) { *dest = (unsigned char *)malloc(tmp.size() + 1); if (!*dest) { - ui::messageBox(hWnd, L"Memory allocation failed.", L"Error", MB_ICONWARNING); + ui::messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); return false; } diff --git a/util.hpp b/util.hpp index e1efe7b..9ebdb96 100644 --- a/util.hpp +++ b/util.hpp @@ -1,35 +1,16 @@ #ifndef UTIL_HPP_ #define UTIL_HPP_ +#include + #ifndef NOMINMAX #define NOMINMAX #endif #include -#include - namespace util { enum newline_t { NEWLINE_CRLF, NEWLINE_LF, NEWLINE_CR }; -class UTF8Parser { - public: - UTF8Parser() : state(0), index(0){}; - - // Resets and returns the remaining bytes if any, all transformed to "?". - // Returns NULL when no bytes are left. - const unsigned char *flush(); - - // Adds a byte to the internal buffer until it becomes one token. - // Returns NULL when more bytes are required, and returns a token when completed. - // Note that this function can return multiple "?" characters when it is given a corrupted UTF-8 - // sequence. - const unsigned char *add(char chr); - - private: - int state, index; - unsigned char token[10]; -}; - // Converts a hex string to a byte sequence. This function allocates `dest` unless it is NULL. // It's your responsibility to `free()` it. // Return value of true indicates success, otherwise indicates failure. From 6bf39b0264878eae3485c11f8c45e7631790e677 Mon Sep 17 00:00:00 2001 From: watamario15 Date: Wed, 29 Mar 2023 22:58:27 +0900 Subject: [PATCH 5/9] Realtime output for Shift_JIS --- main.cpp | 12 ++-------- resource.h | 7 +++--- resource.rc | 5 ++-- tokenizer.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++--------- tokenizer.hpp | 7 +++--- ui.cpp | 6 ++--- util.cpp | 11 +++++---- util.hpp | 2 +- 8 files changed, 74 insertions(+), 40 deletions(-) diff --git a/main.cpp b/main.cpp index c341da1..3e9fd5c 100644 --- a/main.cpp +++ b/main.cpp @@ -99,8 +99,7 @@ static bool bfInit() { if (!wcInput) return false; if (ui::inCharSet == IDM_BF_INPUT_HEX) { - if (!util::parseHex(ui::hWnd, wcInput, &input)) return false; - inLen = input ? strlen((char *)input) : 0; + if (!util::parseHex(ui::hWnd, wcInput, &input, &inLen)) return false; } else { int codePage = (ui::inCharSet == IDM_BF_INPUT_SJIS) ? 932 : CP_UTF8; inLen = WideCharToMultiByte(codePage, 0, wcInput, -1, NULL, 0, NULL, NULL); @@ -152,10 +151,7 @@ static enum Brainfuck::result_t bfNext() { if (!g_prevCR && output == '\n') ui::appendOutput(L"\r"); g_prevCR = output == '\r'; - if (ui::outCharSet == IDM_BF_OUTPUT_ASCII) { - wchar_t wcOut[] = {output, 0}; - ui::appendOutput(wcOut); - } else if (ui::outCharSet == IDM_BF_OUTPUT_UTF8) { + if (ui::outCharSet == IDM_BF_OUTPUT_UTF8) { const wchar_t *converted = g_u8Tokenizer.add(output); if (converted) ui::appendOutput(converted); } else if (ui::outCharSet == IDM_BF_OUTPUT_SJIS) { @@ -402,10 +398,6 @@ static LRESULT CALLBACK wndProc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPA ui::signedness = false; break; - case IDM_BF_OUTPUT_ASCII: - ui::outCharSet = IDM_BF_OUTPUT_ASCII; - break; - case IDM_BF_OUTPUT_UTF8: ui::outCharSet = IDM_BF_OUTPUT_UTF8; break; diff --git a/resource.h b/resource.h index 387b658..35c7ad2 100644 --- a/resource.h +++ b/resource.h @@ -35,10 +35,9 @@ #define IDM_EDIT_SELALL 240 #define IDM_BF_MEMTYPE_SIGNED 300 #define IDM_BF_MEMTYPE_UNSIGNED 301 -#define IDM_BF_OUTPUT_ASCII 310 -#define IDM_BF_OUTPUT_UTF8 311 -#define IDM_BF_OUTPUT_SJIS 312 -#define IDM_BF_OUTPUT_HEX 313 +#define IDM_BF_OUTPUT_UTF8 310 +#define IDM_BF_OUTPUT_SJIS 311 +#define IDM_BF_OUTPUT_HEX 312 #define IDM_BF_INPUT_UTF8 320 #define IDM_BF_INPUT_SJIS 321 #define IDM_BF_INPUT_HEX 322 diff --git a/resource.rc b/resource.rc index c0ede98..60b7332 100644 --- a/resource.rc +++ b/resource.rc @@ -73,15 +73,14 @@ menu MENU } POPUP "&Output Charset" { - MENUITEM "&ASCII", IDM_BF_OUTPUT_ASCII MENUITEM "&UTF-8", IDM_BF_OUTPUT_UTF8 - MENUITEM "Shift_&JIS", IDM_BF_OUTPUT_SJIS + MENUITEM "Shift_&JIS (CP932)", IDM_BF_OUTPUT_SJIS MENUITEM "&Hexadecimal", IDM_BF_OUTPUT_HEX } POPUP "Input &Charset" { MENUITEM "&UTF-8", IDM_BF_INPUT_UTF8 - MENUITEM "Shift_&JIS", IDM_BF_INPUT_SJIS + MENUITEM "Shift_&JIS (CP932)", IDM_BF_INPUT_SJIS MENUITEM "&Hexadecimal", IDM_BF_INPUT_HEX } POPUP "&Input Instruction" diff --git a/tokenizer.cpp b/tokenizer.cpp index ac8a00c..28aaa99 100644 --- a/tokenizer.cpp +++ b/tokenizer.cpp @@ -12,17 +12,15 @@ const wchar_t *UTF8Tokenizer::flush() { memset(result, 0, sizeof(result)); for (int i = 0; i < index; ++i) result[i] = L'?'; - state = index = 0; - return result; } const wchar_t *UTF8Tokenizer::add(unsigned char chr) { - if (state == 0) { // Reading the first character of a token. + if (state == 0) { // Reading the first byte of a character. memset(token, 0, sizeof(token)); - // Got a later byte character, which is unexpected here. + // Got a later byte, which is unexpected here. if ((chr & 0xC0) == 0x80) { memset(result, 0, sizeof(result)); result[0] = L'?'; @@ -37,7 +35,6 @@ const wchar_t *UTF8Tokenizer::add(unsigned char chr) { } token[0] = chr; - char tmp = chr << 2; for (int i = 1; true; ++i) { if (!(tmp & 0x80)) { @@ -51,8 +48,8 @@ const wchar_t *UTF8Tokenizer::add(unsigned char chr) { tmp <<= 1; } ++index; - } else { // Reading a later byte character of a token. - // Got a first byte character, which is unexpected here. + } else { // Reading a later byte of a character. + // Got a first byte, which is unexpected here. if ((chr & 0xC0) != 0x80) { memset(result, 0, sizeof(result)); for (int i = 0; i < index; ++i) result[i] = L'?'; @@ -63,6 +60,7 @@ const wchar_t *UTF8Tokenizer::add(unsigned char chr) { token[index++] = chr; --state; + // Completed one character. if (state == 0) { memset(result, 0, sizeof(result)); MultiByteToWideChar(CP_UTF8, 0, (char *)token, -1, result, @@ -76,11 +74,55 @@ const wchar_t *UTF8Tokenizer::add(unsigned char chr) { } const wchar_t *SJISTokenizer::flush() { - // TODO - return NULL; + if (token == 0) return NULL; + + memset(result, 0, sizeof(result)); + result[0] = L'?'; + token = 0; + return result; } const wchar_t *SJISTokenizer::add(unsigned char chr) { - // TODO - return NULL; + // Reading the first byte of a character. + if (token == 0) { + // Invalid + if (chr == 0x80 || chr == 0xA0 || chr >= 0xFD) { + memset(result, 0, sizeof(result)); + result[0] = L'?'; + return result; + } + + // ASCII + if (!(chr & 0x80)) { + memset(result, 0, sizeof(result)); + result[0] = chr; + return result; + } + + // Kana + if (chr >= 0xA1 && chr <= 0xDF) { + memset(result, 0, sizeof(result)); + MultiByteToWideChar(932, 0, (char *)&token, 1, result, sizeof(result) / sizeof(result[0])); + return result; + } + + token = chr; + return NULL; + } + + // Reading the second byte of a character. + if (chr <= 0x3f || chr == 0x7F || chr >= 0xFD) { // Invalid + memset(result, 0, sizeof(result)); + result[0] = result[1] = L'?'; + token = 0; + return result; + } + + // Completed one character. + const unsigned char character[2] = {token, chr}; + memset(result, 0, sizeof(result)); + MultiByteToWideChar(932, 0, (const char *)character, 2, result, + sizeof(result) / sizeof(result[0])); + token = 0; + return result; } diff --git a/tokenizer.hpp b/tokenizer.hpp index 5e6642a..458b559 100644 --- a/tokenizer.hpp +++ b/tokenizer.hpp @@ -25,7 +25,7 @@ class UTF8Tokenizer { class SJISTokenizer { public: - SJISTokenizer() : state(0), index(0){}; + SJISTokenizer() : token(0){}; // Resets and returns the remaining bytes if any, all transformed to "?". // Returns NULL if no bytes are left. @@ -38,9 +38,8 @@ class SJISTokenizer { const wchar_t *add(unsigned char chr); private: - int state, index; - unsigned char token[16]; - wchar_t result[16]; + unsigned char token; + wchar_t result[4]; }; #endif diff --git a/ui.cpp b/ui.cpp index afb2ad8..6f0bd0d 100644 --- a/ui.cpp +++ b/ui.cpp @@ -84,7 +84,7 @@ static int dpi = 96, sysDPI = 96; enum state_t state = STATE_INIT; bool signedness = true, wrapInt = true, breakpoint = false, debug = true, dark = true, horizontal = false; -int speed = 10, outCharSet = IDM_BF_OUTPUT_ASCII, inCharSet = IDM_BF_INPUT_UTF8; +int speed = 10, outCharSet = IDM_BF_OUTPUT_UTF8, inCharSet = IDM_BF_INPUT_UTF8; enum Brainfuck::noinput_t noInput = Brainfuck::NOINPUT_ZERO; HWND hWnd; HINSTANCE hInst; @@ -315,7 +315,7 @@ void onInitMenuPopup() { signedness ? IDM_BF_MEMTYPE_SIGNED : IDM_BF_MEMTYPE_UNSIGNED, MF_BYCOMMAND); // Brainfuck -> Output Charset - CheckMenuRadioItem(hMenu, IDM_BF_OUTPUT_ASCII, IDM_BF_OUTPUT_HEX, outCharSet, MF_BYCOMMAND); + CheckMenuRadioItem(hMenu, IDM_BF_OUTPUT_UTF8, IDM_BF_OUTPUT_HEX, outCharSet, MF_BYCOMMAND); // Brainfuck -> Input Charset CheckMenuRadioItem(hMenu, IDM_BF_INPUT_UTF8, IDM_BF_INPUT_HEX, inCharSet, MF_BYCOMMAND); @@ -920,7 +920,7 @@ void openFile(bool _newFile, const wchar_t *_fileName) { } DWORD fileSize = GetFileSize(hFile, NULL), readLen; - if (fileSize >= 65536) { + if (fileSize >= 65536 * 2) { messageBox(hWnd, L"This file is too large.", L"Error", MB_ICONWARNING); return; } diff --git a/util.cpp b/util.cpp index 0ea0816..fc2ec24 100644 --- a/util.cpp +++ b/util.cpp @@ -15,7 +15,7 @@ static inline bool isHex(wchar_t chr) { (chr >= L'a' && chr <= L'f'); } -bool parseHex(HWND hWnd, const wchar_t *hexInput, unsigned char **dest) { +bool parseHex(HWND hWnd, const wchar_t *hexInput, unsigned char **dest, int *length) { wchar_t hex[2]; std::string tmp; int hexLen = 0, i; @@ -62,18 +62,21 @@ bool parseHex(HWND hWnd, const wchar_t *hexInput, unsigned char **dest) { if (hexInput[i] == L'\0') break; } - if (tmp.size() == 0) { + *length = tmp.size(); + + if (*length == 0) { *dest = NULL; return true; } - *dest = (unsigned char *)malloc(tmp.size() + 1); + *dest = (unsigned char *)malloc(*length); if (!*dest) { ui::messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); + *length = 0; return false; } - memcpy(*dest, tmp.c_str(), tmp.size() + 1); + memcpy(*dest, tmp.c_str(), *length); return true; } diff --git a/util.hpp b/util.hpp index 9ebdb96..0eeb612 100644 --- a/util.hpp +++ b/util.hpp @@ -14,7 +14,7 @@ enum newline_t { NEWLINE_CRLF, NEWLINE_LF, NEWLINE_CR }; // Converts a hex string to a byte sequence. This function allocates `dest` unless it is NULL. // It's your responsibility to `free()` it. // Return value of true indicates success, otherwise indicates failure. -extern bool parseHex(HWND hWnd, const wchar_t *hexInput, unsigned char **dest); +extern bool parseHex(HWND hWnd, const wchar_t *hexInput, unsigned char **dest, int *length); // Converts a byte to a hex string. `dest` must have at least 4 elements. extern void toHex(unsigned char num, wchar_t *dest); From 1672ce504b2dfda3a52b7930139e1daa16755ac8 Mon Sep 17 00:00:00 2001 From: watamario15 Date: Thu, 30 Mar 2023 01:42:43 +0900 Subject: [PATCH 6/9] [WIP] Improve Undo/Redo --- main.cpp | 4 ++ resource.h | 9 ++-- resource.rc | 1 + ui.cpp | 118 ++++++++++++++++++++++++++++++++++++++++++++++++---- ui.hpp | 1 + 5 files changed, 121 insertions(+), 12 deletions(-) diff --git a/main.cpp b/main.cpp index 3e9fd5c..32a8566 100644 --- a/main.cpp +++ b/main.cpp @@ -374,6 +374,10 @@ static LRESULT CALLBACK wndProc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPA ui::undo(); break; + case IDM_EDIT_REDO: + ui::redo(); + break; + case IDM_EDIT_CUT: ui::cut(); break; diff --git a/resource.h b/resource.h index 35c7ad2..30dbc13 100644 --- a/resource.h +++ b/resource.h @@ -29,10 +29,11 @@ #define IDM_FILE_SAVE_AS 130 #define IDM_FILE_EXIT 140 #define IDM_EDIT_UNDO 200 -#define IDM_EDIT_CUT 210 -#define IDM_EDIT_COPY 220 -#define IDM_EDIT_PASTE 230 -#define IDM_EDIT_SELALL 240 +#define IDM_EDIT_REDO 210 +#define IDM_EDIT_CUT 220 +#define IDM_EDIT_COPY 230 +#define IDM_EDIT_PASTE 240 +#define IDM_EDIT_SELALL 250 #define IDM_BF_MEMTYPE_SIGNED 300 #define IDM_BF_MEMTYPE_UNSIGNED 301 #define IDM_BF_OUTPUT_UTF8 310 diff --git a/resource.rc b/resource.rc index 60b7332..3580175 100644 --- a/resource.rc +++ b/resource.rc @@ -59,6 +59,7 @@ menu MENU POPUP "&Edit" { MENUITEM "&Undo", IDM_EDIT_UNDO + MENUITEM "&Redo", IDM_EDIT_REDO MENUITEM "Cu&t", IDM_EDIT_CUT MENUITEM "&Copy", IDM_EDIT_COPY MENUITEM "&Paste", IDM_EDIT_PASTE diff --git a/ui.cpp b/ui.cpp index 6f0bd0d..96c8f8a 100644 --- a/ui.cpp +++ b/ui.cpp @@ -1,4 +1,5 @@ #include +#include #ifndef _UNICODE #define _UNICODE @@ -65,18 +66,21 @@ typedef HRESULT(__stdcall *TaskDialog_t)(HWND hwndOwner, HINSTANCE hInstance, namespace ui { static const wchar_t *wcCmdBtn[CMDBTN_LEN] = {L"Run", L"Next", L"Pause", L"End"}, *wcScrKB[SCRKBD_LEN] = {L">", L"<", L"+", L"-", L".", L",", L"[", L"]", L"@"}; +static const int historyMax = 32; static HWND hEditor, hInput, hOutput, hMemView, hFocused, hCmdBtn[CMDBTN_LEN], hScrKB[SCRKBD_LEN]; static HMENU hMenu; static HFONT hBtnFont = NULL, hEditFont = NULL; static LOGFONTW editFont; -static int topPadding = 0, memViewStart = 0; +static int topPadding = 0, memViewStart = 0, historyIndex = 0, savedIndex = 0; static wchar_t *retEditBuf = NULL, *retInBuf = NULL; static std::wstring wstrFileName; -static bool withBOM = false, wordwrap = true; +static bool withBOM = false, wordwrap = true, validHistory = true; static enum util::newline_t newLine = util::NEWLINE_CRLF; +static std::deque history; #ifdef UNDER_CE static HWND hCmdBar; #else +static HMODULE comctl32 = NULL; static TaskDialog_t taskDialog = NULL; static int dpi = 96, sysDPI = 96; #endif @@ -88,7 +92,6 @@ int speed = 10, outCharSet = IDM_BF_OUTPUT_UTF8, inCharSet = IDM_BF_INPUT_UTF8; enum Brainfuck::noinput_t noInput = Brainfuck::NOINPUT_ZERO; HWND hWnd; HINSTANCE hInst; -HMODULE comctl32 = NULL; // Enables/Disables menu items from the smaller nearest 10 multiple to `_endID`. static void enableMenus(unsigned _endID, bool _enable) { @@ -98,6 +101,60 @@ static void enableMenus(unsigned _endID, bool _enable) { } } +// Hook window procedure for the program editor to manage the undo/redo buffer. +static LRESULT CALLBACK editorProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam) { + static WNDPROC prevWndProc = (WNDPROC)myGetWindowLongW(hWnd, GWL_USERDATA); + + switch (uMsg) { + case WM_CHAR: { + if (!validHistory) break; + + int editorSize = GetWindowTextLengthW(hEditor) + 1; + wchar_t *wcEditor = (wchar_t *)malloc(sizeof(wchar_t) * editorSize); + if (!wcEditor) { + messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); + while (!history.empty()) { + free(history.back()); + history.pop_back(); + } + historyIndex = 0; + savedIndex = -1; + validHistory = false; + break; + } + GetWindowTextW(hEditor, wcEditor, editorSize); + + if (!history.empty() && !wcscmp(history[historyIndex], wcEditor)) { + free(wcEditor); + break; + } + + int toRemove = history.size() - historyIndex - 1, i = 0; + for (; i < toRemove; ++i) { + free(history.back()); + history.pop_back(); + } + + if (history.size() >= historyMax) { + free(history.front()); + history.pop_front(); + --historyIndex; + if (savedIndex >= 0) --savedIndex; + } + + history.push_back(wcEditor); + ++historyIndex; + break; + } + + case WM_DESTROY: + mySetWindowLongW(hWnd, GWL_WNDPROC, prevWndProc); + return 0; + } + + return CallWindowProcW(prevWndProc, hWnd, uMsg, wParam, lParam); +} + void onCreate(HWND _hWnd, HINSTANCE _hInst) { size_t i; hWnd = _hWnd; @@ -123,6 +180,7 @@ void onCreate(HWND _hWnd, HINSTANCE _hInst) { WS_VSCROLL | (wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, 0, 0, 0, 0, hWnd, (HMENU)IDC_EDITOR, hInst, NULL); SendMessageW(hEditor, EM_SETLIMITTEXT, (WPARAM)-1, 0); + mySetWindowLongW(hEditor, GWL_USERDATA, mySetWindowLongW(hEditor, GWL_WNDPROC, editorProc)); // Program input hInput = @@ -231,7 +289,9 @@ void onDestroy() { DeleteObject(hEditFont); if (retEditBuf) delete[] retEditBuf; if (retInBuf) delete[] retInBuf; +#ifndef UNDER_CE if (comctl32) FreeLibrary(comctl32); +#endif } void onSize() { @@ -352,12 +412,11 @@ void onInitMenuPopup() { // Options -> Word Wrap CheckMenuItem(hMenu, IDM_OPT_WORDWRAP, MF_BYCOMMAND | wordwrap ? MF_CHECKED : MF_UNCHECKED); - bool undoable = SendMessageW(hEditor, EM_CANUNDO, 0, 0) != 0; - if (state == STATE_INIT) { enableMenus(IDM_FILE_NEW, true); enableMenus(IDM_FILE_OPEN, true); - enableMenus(IDM_EDIT_UNDO, undoable); + enableMenus(IDM_EDIT_UNDO, validHistory && historyIndex > 0); + enableMenus(IDM_EDIT_REDO, validHistory && historyIndex < (int)history.size() - 1); enableMenus(IDM_BF_MEMTYPE_UNSIGNED, true); enableMenus(IDM_BF_OUTPUT_HEX, true); enableMenus(IDM_BF_INPUT_HEX, true); @@ -373,6 +432,7 @@ void onInitMenuPopup() { enableMenus(IDM_FILE_NEW, false); enableMenus(IDM_FILE_OPEN, false); enableMenus(IDM_EDIT_UNDO, false); + enableMenus(IDM_EDIT_REDO, false); enableMenus(IDM_BF_MEMTYPE_UNSIGNED, false); enableMenus(IDM_BF_OUTPUT_HEX, false); enableMenus(IDM_BF_INPUT_HEX, false); @@ -388,6 +448,7 @@ void onInitMenuPopup() { enableMenus(IDM_FILE_NEW, false); enableMenus(IDM_FILE_OPEN, false); enableMenus(IDM_EDIT_UNDO, false); + enableMenus(IDM_EDIT_REDO, false); enableMenus(IDM_BF_MEMTYPE_UNSIGNED, false); enableMenus(IDM_BF_OUTPUT_HEX, false); enableMenus(IDM_BF_INPUT_HEX, false); @@ -434,7 +495,13 @@ void paste() { SendMessageW(hFocused, WM_PASTE, 0, 0); } void selAll() { SendMessageW(hFocused, EM_SETSEL, 0, -1); } -void undo() { SendMessageW(hEditor, EM_UNDO, 0, 0); } +void undo() { + // TODO +} + +void redo() { + // TODO +} int messageBox(HWND _hWnd, const wchar_t *_lpText, const wchar_t *_lpCaption, unsigned _uType) { #ifndef UNDER_CE @@ -771,6 +838,7 @@ void switchWordwrap() { WS_VSCROLL | (wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, 0, 0, 0, 0, hWnd, (HMENU)IDC_EDITOR, hInst, NULL); SendMessageW(hInput, EM_SETLIMITTEXT, (WPARAM)-1, 0); + mySetWindowLongW(hEditor, GWL_USERDATA, mySetWindowLongW(hEditor, GWL_WNDPROC, editorProc)); // Program input hInput = @@ -857,7 +925,8 @@ void chooseFont() { } bool promptSave() { - if (SendMessageW(hEditor, EM_GETMODIFY, 0, 0) == 0) return true; + if (validHistory && historyIndex == savedIndex) return true; + if (!validHistory && SendMessageW(hEditor, EM_GETMODIFY, 0, 0) == 0) return true; int ret = messageBox(hWnd, L"Unsaved data will be lost. Save changes?", L"Confirm", MB_ICONWARNING | MB_YESNOCANCEL); @@ -890,6 +959,22 @@ void openFile(bool _newFile, const wchar_t *_fileName) { wstrFileName = L""; withBOM = false; newLine = util::NEWLINE_CRLF; + + while (!history.empty()) { + free(history.back()); + history.pop_back(); + } + wchar_t *wcEditor = (wchar_t *)calloc(1, sizeof(wchar_t)); + if (!wcEditor) { + messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); + historyIndex = 0; + savedIndex = -1; + validHistory = false; + return; + } + history.push_back(wcEditor); + historyIndex = savedIndex = 0; + validHistory = true; return; } @@ -957,6 +1042,22 @@ void openFile(bool _newFile, const wchar_t *_fileName) { wstrFileName = _fileName; withBOM = padding != 0; + while (!history.empty()) { + free(history.back()); + history.pop_back(); + } + wchar_t *wcEditor = _wcsdup(converted.c_str()); + if (!wcEditor) { + messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); + historyIndex = 0; + savedIndex = -1; + validHistory = false; + return; + } + history.push_back(wcEditor); + historyIndex = savedIndex = 0; + validHistory = true; + std::wstring title = L"["; title.append(wstrFileName.substr(wstrFileName.rfind(L'\\') + 1) + L"] - " APP_NAME); SetWindowTextW(hWnd, title.c_str()); @@ -1018,6 +1119,7 @@ bool saveFile(bool _isOverwrite) { CloseHandle(hFile); free(szEditor); + if (validHistory) savedIndex = historyIndex; SendMessageW(hEditor, EM_SETMODIFY, FALSE, 0); wstrFileName = wcFileName; diff --git a/ui.hpp b/ui.hpp index 0ba6ed7..a799b60 100644 --- a/ui.hpp +++ b/ui.hpp @@ -30,6 +30,7 @@ void copy(); void paste(); void selAll(); void undo(); +void redo(); // Shows a message box with TaskDialog or MessageBoxW. // From 30ba6722545aa4162e44dccfc29d5192dc5a3077 Mon Sep 17 00:00:00 2001 From: watamario15 Date: Fri, 31 Mar 2023 01:50:34 +0900 Subject: [PATCH 7/9] Refactor --- bf.cpp | 44 +- bf.hpp | 48 +- evc4proj/brainfuck_evc4.vcp | 61 +- main.cpp | 573 +-------- main.hpp | 140 +++ msg.cpp | 624 +++++++++ msg.hpp | 22 + resource.h | 4 +- runner.cpp | 180 +++ runner.hpp | 32 + ui.cpp | 1115 ++++------------- ui.hpp | 72 +- util.cpp | 123 +- util.hpp | 22 +- vs2022proj-pc/brainfuck_vs2022.vcxproj | 11 + .../brainfuck_vs2022.vcxproj.filters | 33 + wproc.cpp | 224 ++++ wproc.hpp | 22 + 18 files changed, 1795 insertions(+), 1555 deletions(-) create mode 100644 main.hpp create mode 100644 msg.cpp create mode 100644 msg.hpp create mode 100644 runner.cpp create mode 100644 runner.hpp create mode 100644 wproc.cpp create mode 100644 wproc.hpp diff --git a/bf.cpp b/bf.cpp index 394f2ae..b4dda0c 100644 --- a/bf.cpp +++ b/bf.cpp @@ -1,10 +1,11 @@ #include "bf.hpp" +#include + #ifndef NOMINMAX #define NOMINMAX #endif #include -#include void Brainfuck::reset() { progIndex = 0; @@ -14,23 +15,22 @@ void Brainfuck::reset() { memLen = 1; } -void Brainfuck::reset(unsigned _progLen, const wchar_t *_program, unsigned _inLen, - const void *_input) { - if (_progLen == 0 || !_program) { - program = NULL; - progLen = 0; +void Brainfuck::reset(unsigned progLen, const wchar_t *program, unsigned inLen, const void *input) { + if (progLen == 0 || !program) { + Brainfuck::program = NULL; + Brainfuck::progLen = 0; } else { - program = _program; - progLen = _progLen; + Brainfuck::program = program; + Brainfuck::progLen = progLen; } progIndex = 0; - if (_inLen == 0 || !_input) { - input = NULL; - inLen = 0; + if (inLen == 0 || !input) { + Brainfuck::input = NULL; + Brainfuck::inLen = 0; } else { - input = (const unsigned char *)_input; - inLen = _inLen; + Brainfuck::input = (const unsigned char *)input; + Brainfuck::inLen = inLen; } inIndex = 0; @@ -39,16 +39,16 @@ void Brainfuck::reset(unsigned _progLen, const wchar_t *_program, unsigned _inLe memLen = 1; } -void Brainfuck::setBehavior(enum noinput_t _noInput, bool _wrapInt, bool _signedness, bool _debug) { - noInput = _noInput; - wrapInt = _wrapInt; - signedness = _signedness; - debug = _debug; +void Brainfuck::setBehavior(enum noinput_t noInput, bool wrapInt, bool signedness, bool debug) { + Brainfuck::noInput = noInput; + Brainfuck::wrapInt = wrapInt; + Brainfuck::signedness = signedness; + Brainfuck::debug = debug; } -enum Brainfuck::result_t Brainfuck::next(unsigned char *_output, bool *_didOutput) { +enum Brainfuck::result_t Brainfuck::next(unsigned char *output, bool *didOutput) { enum result_t result = RESULT_RUN; - *_didOutput = false; + *didOutput = false; if (progIndex >= progLen) { return RESULT_FIN; @@ -122,8 +122,8 @@ enum Brainfuck::result_t Brainfuck::next(unsigned char *_output, bool *_didOutpu break; case L'.': - *_output = memory[memIndex]; - *_didOutput = true; + *output = memory[memIndex]; + *didOutput = true; break; case L',': diff --git a/bf.hpp b/bf.hpp index d921b7a..45cabc3 100644 --- a/bf.hpp +++ b/bf.hpp @@ -8,7 +8,7 @@ class Brainfuck { public: - // Defines a desired behavior for input instructions with no input. + // Used to define a desired behavior for the input instruction when no input is given. enum noinput_t { NOINPUT_ERROR, NOINPUT_ZERO, NOINPUT_FF }; // Execution result returned from the next() function. @@ -26,9 +26,9 @@ class Brainfuck { reset(); } - // Initializes the internal state and sets `_program` and `_input`. - // DO NOT CHANGE THE CONTENTS OF THEM. You must call reset() when you update them. - Brainfuck(unsigned _progLen, const wchar_t *_program, unsigned _inLen, const void *_input) + // Initializes the internal state and registers a program and an _input. + // You must call reset() whenever you want to modify the program and the input. + Brainfuck(unsigned progLen, const wchar_t *program, unsigned inLen, const void *input) : program(NULL), input(NULL), memLen(1), @@ -36,47 +36,47 @@ class Brainfuck { signedness(true), debug(false), noInput(NOINPUT_ZERO) { - reset(_progLen, _program, _inLen, _input); + reset(progLen, program, inLen, input); } // Resets the internal state. void reset(); - // Resets the internal state and copies `_program` and `_input`. - // DO NOT CHANGE THE CONTENTS OF THEM. You must call reset() when you update them. - void reset(unsigned _progLen, const wchar_t *_program, unsigned _inLen, const void *_input); + // Resets the internal state and registers a program and an _input. + // You must call reset() whenever you want to modify the program and the input. + void reset(unsigned progLen, const wchar_t *program, unsigned inLen, const void *input); // Change implementation-defined behaviors. - // When wrapInt is false, `next` throws an exception on an overflow/underflow. - // When wrapInt is true, signedness doen't have any effect. - // When debug is true, breakpoint instruction ("@") is enabled. - // Default options are, zero for no input, wrap around integer, signed integer (no effects in - // this case), and no debug. - void setBehavior(enum noinput_t _noInput = NOINPUT_ZERO, bool _wrapInt = true, - bool _signedness = true, bool _debug = false); + // If wrapInt is false, next() throws an exception when overflowed or underflowed. + // If wrapInt is true, signedness doen't have any effect. + // If debug is true, breakpoint instruction ("@") is enabled. + // The default behavior is [zero for no input, wrap around integer, signed integer (no effects in + // this case), no debug]. + void setBehavior(enum noinput_t noInput = NOINPUT_ZERO, bool wrapInt = true, + bool signedness = true, bool debug = false); - // Executes the next code, and writes its output on `output` if any. - // A return value is the result of an execution, which is running, breakpoint, finished, and + // Executes the next code, and writes its output on `_output` if any. + // Returns the result of an execution, which is running, breakpoint, finished, and // error. - enum result_t next(unsigned char *_output, bool *_didOutput); + enum result_t next(unsigned char *output, bool *didOutput); - // Returns the memory. The returned content becomes invalid on reset/next. - const unsigned char *getMemory(unsigned *_size) { - *_size = memLen; + // Returns the memory and writes its size to `_size`. + // The returned content becomes invalid on reset() and next(). + const unsigned char *getMemory(unsigned *size) { + *size = memLen; return memory; } - // Returns the memory pointer. + // Returns the value of memory pointer. unsigned getMemPtr() { return memIndex; } - // Returns the program pointer. + // Returns the value of program pointer. unsigned getProgPtr() { return progIndex; } // Returns the last error. const wchar_t *getLastError() { return lastError; } private: - // All initializations are in constructor, as old C++ doesn't allow them on the declaration. const wchar_t *program; // Program const unsigned char *input; // Input stream unsigned char memory[65536]; // Memory diff --git a/evc4proj/brainfuck_evc4.vcp b/evc4proj/brainfuck_evc4.vcp index 52f8271..3e6fa40 100644 --- a/evc4proj/brainfuck_evc4.vcp +++ b/evc4proj/brainfuck_evc4.vcp @@ -111,7 +111,36 @@ DEP_CPP_BF_CP=\ SOURCE=..\main.cpp DEP_CPP_MAIN_=\ "..\bf.hpp"\ + "..\main.hpp"\ + "..\msg.hpp"\ "..\resource.h"\ + "..\runner.hpp"\ + "..\util.hpp"\ + "..\wproc.hpp"\ + +# End Source File +# Begin Source File + +SOURCE=..\msg.cpp +DEP_CPP_MSG_C=\ + "..\bf.hpp"\ + "..\main.hpp"\ + "..\msg.hpp"\ + "..\resource.h"\ + "..\runner.hpp"\ + "..\ui.hpp"\ + "..\util.hpp"\ + "..\wproc.hpp"\ + +# End Source File +# Begin Source File + +SOURCE=..\runner.cpp +DEP_CPP_RUNNE=\ + "..\bf.hpp"\ + "..\main.hpp"\ + "..\resource.h"\ + "..\runner.hpp"\ "..\tokenizer.hpp"\ "..\ui.hpp"\ "..\util.hpp"\ @@ -129,18 +158,32 @@ DEP_CPP_TOKEN=\ SOURCE=..\ui.cpp DEP_CPP_UI_CP=\ "..\bf.hpp"\ + "..\main.hpp"\ + "..\msg.hpp"\ "..\resource.h"\ "..\ui.hpp"\ "..\util.hpp"\ + "..\wproc.hpp"\ # End Source File # Begin Source File SOURCE=..\util.cpp DEP_CPP_UTIL_=\ + "..\util.hpp"\ + +# End Source File +# Begin Source File + +SOURCE=..\wproc.cpp +DEP_CPP_WPROC=\ "..\bf.hpp"\ - "..\ui.hpp"\ + "..\main.hpp"\ + "..\msg.hpp"\ + "..\resource.h"\ + "..\runner.hpp"\ "..\util.hpp"\ + "..\wproc.hpp"\ # End Source File # End Group @@ -153,10 +196,22 @@ SOURCE=..\bf.hpp # End Source File # Begin Source File +SOURCE=..\main.hpp +# End Source File +# Begin Source File + +SOURCE=..\msg.hpp +# End Source File +# Begin Source File + SOURCE=..\resource.h # End Source File # Begin Source File +SOURCE=..\runner.hpp +# End Source File +# Begin Source File + SOURCE=..\tokenizer.hpp # End Source File # Begin Source File @@ -167,6 +222,10 @@ SOURCE=..\ui.hpp SOURCE=..\util.hpp # End Source File +# Begin Source File + +SOURCE=..\wproc.hpp +# End Source File # End Group # Begin Group "Resource Files" diff --git a/main.cpp b/main.cpp index 32a8566..cac28bf 100644 --- a/main.cpp +++ b/main.cpp @@ -1,550 +1,67 @@ -#ifndef _UNICODE -#define _UNICODE -#endif -#ifndef UNICODE -#define UNICODE -#endif - -#ifndef NOMINMAX -#define NOMINMAX -#endif -#include -#ifndef WS_OVERLAPPEDWINDOW -#define WS_OVERLAPPEDWINDOW \ - WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX -#endif - -#ifdef UNDER_CE -// Workaround for wrong macro definitions in CeGCC. -#if SW_MAXIMIZE != 12 -#undef SW_MAXIMIZE -#define SW_MAXIMIZE 12 -#endif -#if SW_MINIMIZE != 6 -#undef SW_MINIMIZE -#define SW_MINIMIZE 6 -#endif -#if WS_MINIMIZEBOX != 0x00010000L -#undef WS_MINIMIZEBOX -#define WS_MINIMIZEBOX 0x00010000L -#endif -#if WS_MAXIMIZEBOX != 0x00020000L -#undef WS_MAXIMIZEBOX -#define WS_MAXIMIZEBOX 0x00020000L -#endif - -// WinMain is already unicode on Windows CE. -#define wWinMain WinMain - -// Expands to _beginthreadex on Windows PC and CreateThread on Windows CE. -#define myCreateThread(lpsa, cbStack, lpStartAddr, lpvThreadParam, fdwCreate, lpIDThread) \ - CreateThread(lpsa, cbStack, lpStartAddr, lpvThreadParam, fdwCreate, lpIDThread) - -// The return type of a thread function. -typedef DWORD tret_t; - -#include -#include -#else -#include +#include "main.hpp" -// Expands to _beginthreadex on Windows PC and CreateThread on Windows CE. -#define myCreateThread(lpsa, cbStack, lpStartAddr, lpvThreadParam, fdwCreate, lpIDThread) \ - (HANDLE) _beginthreadex(lpsa, cbStack, lpStartAddr, lpvThreadParam, fdwCreate, lpIDThread) - -// The return type of a thread function. -typedef unsigned int tret_t; -#endif +#include "msg.hpp" +#include "runner.hpp" +#include "wproc.hpp" #define WND_CLASS_NAME L"brainfuck-main" -#define WM_APP_THREADEND WM_APP - -#include "bf.hpp" -#include "resource.h" -#include "ui.hpp" -#include "util.hpp" -#include "tokenizer.hpp" - -enum ctrlthread_t { CTRLTHREAD_RUN, CTRLTHREAD_PAUSE, CTRLTHREAD_END }; - -static class Brainfuck g_bf; -static class UTF8Tokenizer g_u8Tokenizer; -static class SJISTokenizer g_sjisTokenizer; -static unsigned int g_timerID = 0; -static wchar_t *g_cmdLine; -static bool g_prevCR = false; -static HANDLE g_hThread = NULL; -static volatile enum ctrlthread_t g_ctrlThread = CTRLTHREAD_RUN; - -// Stops the timer identified by g_timerID if it's not 0 -static inline void stopTimer() { - if (g_timerID) { - timeKillEvent(g_timerID); - timeEndPeriod(ui::speed); - g_timerID = 0; - } -} - -// Initializes the Brainfuck module. Returns false on an invalid hexadecimal input. -static bool bfInit() { - static unsigned char *input = NULL; - - ui::setOutput(L""); - ui::setMemory(NULL); - g_prevCR = false; - if (input) free(input); - - int inLen; - wchar_t *wcInput = ui::getInput(); - if (!wcInput) return false; - - if (ui::inCharSet == IDM_BF_INPUT_HEX) { - if (!util::parseHex(ui::hWnd, wcInput, &input, &inLen)) return false; - } else { - int codePage = (ui::inCharSet == IDM_BF_INPUT_SJIS) ? 932 : CP_UTF8; - inLen = WideCharToMultiByte(codePage, 0, wcInput, -1, NULL, 0, NULL, NULL); - input = (unsigned char *)malloc(inLen); - WideCharToMultiByte(codePage, 0, wcInput, -1, (char *)input, inLen, NULL, NULL); - inLen--; - } - - if (ui::outCharSet == IDM_BF_OUTPUT_UTF8) { - g_u8Tokenizer.flush(); - } else if (ui::outCharSet == IDM_BF_OUTPUT_SJIS) { - g_sjisTokenizer.flush(); - } - - wchar_t *wcEditor = ui::getEditor(); - if (!wcEditor) return false; - g_bf.reset(wcslen(wcEditor), wcEditor, inLen, input); - - return true; -} - -// Executes the next instruction. -static enum Brainfuck::result_t bfNext() { - unsigned char output; - bool didOutput; - - g_bf.setBehavior(ui::noInput, ui::wrapInt, ui::signedness, ui::breakpoint); - enum Brainfuck::result_t result = g_bf.next(&output, &didOutput); - - if (result == Brainfuck::RESULT_FIN || result == Brainfuck::RESULT_ERR) { - if (ui::outCharSet == IDM_BF_OUTPUT_UTF8) { - const wchar_t *rest = g_u8Tokenizer.flush(); - if (rest) ui::appendOutput(rest); - } else if (ui::outCharSet == IDM_BF_OUTPUT_SJIS) { - const wchar_t *rest = g_sjisTokenizer.flush(); - if (rest) ui::appendOutput(rest); - } - return result; - } - - if (didOutput) { - if (ui::outCharSet == IDM_BF_OUTPUT_HEX) { - wchar_t wcOut[4]; - util::toHex(output, wcOut); - ui::appendOutput(wcOut); - } else { - // Align newlines to CRLF. - if (g_prevCR && output != '\n') ui::appendOutput(L"\n"); - if (!g_prevCR && output == '\n') ui::appendOutput(L"\r"); - g_prevCR = output == '\r'; - - if (ui::outCharSet == IDM_BF_OUTPUT_UTF8) { - const wchar_t *converted = g_u8Tokenizer.add(output); - if (converted) ui::appendOutput(converted); - } else if (ui::outCharSet == IDM_BF_OUTPUT_SJIS) { - const wchar_t *converted = g_sjisTokenizer.add(output); - if (converted) ui::appendOutput(converted); - } - } - } - - return result; -} - -// Executes an Brainfuck program until it completes. -tret_t WINAPI threadRunner(void *lpParameter) { - UNREFERENCED_PARAMETER(lpParameter); - - HANDLE hEvent = NULL; - if (ui::speed != 0) { - hEvent = CreateEventW(NULL, FALSE, TRUE, NULL); - if (timeBeginPeriod(ui::speed) == TIMERR_NOERROR) { - g_timerID = timeSetEvent(ui::speed, ui::speed, (LPTIMECALLBACK)hEvent, 0, - TIME_PERIODIC | TIME_CALLBACK_EVENT_SET); - } - if (!g_timerID) { - ui::messageBox(ui::hWnd, L"This speed is not supported on your device. Try slowing down.", - L"Error", MB_ICONERROR); - ui::setState(ui::STATE_INIT); - PostMessageW(ui::hWnd, WM_APP_THREADEND, 0, 0); - return 1; - } - } - - enum Brainfuck::result_t result; - while (g_ctrlThread == CTRLTHREAD_RUN) { - if (ui::speed != 0) WaitForSingleObject(hEvent, INFINITE); - if ((result = bfNext()) != Brainfuck::RESULT_RUN) break; - if (ui::debug) { - unsigned size; - const unsigned char *memory = g_bf.getMemory(&size); - ui::setMemory(memory, size); - ui::selProg(g_bf.getProgPtr()); - } - } - - stopTimer(); - if (hEvent) CloseHandle(hEvent); - - if (g_ctrlThread == CTRLTHREAD_END) { - ui::setState(ui::STATE_INIT); - } else { // Paused, finished, error, or breakpoint - ui::setState(result == Brainfuck::RESULT_RUN || result == Brainfuck::RESULT_BREAK - ? ui::STATE_PAUSE - : ui::STATE_FINISH); - if (result == Brainfuck::RESULT_ERR) { - ui::messageBox(ui::hWnd, g_bf.getLastError(), L"Brainfuck Error", MB_ICONWARNING); - } - unsigned size; - const unsigned char *memory = g_bf.getMemory(&size); - ui::setMemory(memory, size); - ui::selProg(g_bf.getProgPtr()); - } - - PostMessageW(ui::hWnd, WM_APP_THREADEND, 0, 0); - return 0; -} - -static LRESULT CALLBACK wndProc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam) { - static bool didInit = false; - static HBRUSH BGDark = CreateSolidBrush(0x3f3936); - - switch (uMsg) { - case WM_CREATE: - ui::onCreate(hWnd, ((LPCREATESTRUCT)(lParam))->hInstance); - if (g_cmdLine[0]) { - ui::openFile(false, g_cmdLine); - } else { - ui::openFile(true); - } - break; - - case WM_SIZE: - ui::onSize(); - break; - - case WM_INITMENUPOPUP: - ui::onInitMenuPopup(); - break; - - case WM_ACTIVATE: - if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) ui::updateFocus(); - break; - - case WM_CTLCOLOREDIT: - if (ui::dark) { - SetTextColor((HDC)wParam, 0x00ff00); - SetBkColor((HDC)wParam, 0x3f3936); - return (LRESULT)BGDark; - } else { - return DefWindowProcW(hWnd, uMsg, wParam, lParam); - } - - case WM_CTLCOLORSTATIC: - if (ui::dark) { - SetTextColor((HDC)wParam, 0x00ff00); - SetBkColor((HDC)wParam, 0x000000); - return (LRESULT)GetStockObject(BLACK_BRUSH); - } else { - return DefWindowProcW(hWnd, uMsg, wParam, lParam); - } +namespace global { +HINSTANCE hInst; // Handle to the current instance. +HWND hWnd; // Handle to the main window. +HWND hEditor; // Handle to the program editor. +HWND hInput; // Handle to the stdin editor. +HWND hOutput; // Handle to the stdout editor. +HWND hMemView; // Handle to the memory view. +HWND hFocused; // Handle to the currently focused control. +HWND hCmdBtn[CMDBTN_LEN]; // Handles to command buttons. +HWND hScrKB[SCRKBD_LEN]; // Handles to screen keyboard buttons. +HMENU hMenu; // Handle to the menu bar. +LOGFONTW editFont; // Font information which is used in whole this app. +int speed = 10; // Brainfuck execution speed. +int outCharSet = IDM_BF_OUTPUT_UTF8; // Brainfuck stdout charset. +int inCharSet = IDM_BF_INPUT_UTF8; // Brainfuck stdin charset. +int memViewStart = 0; // First address of the memory view (0-indexed byte). +int historyIndex = 0; // Index to the current state in the undo/redo history. +int savedIndex = 0; // Index to the saved state in the undo/redo history. +bool validHistory = true; // Whether the undo/redo history is valid. +bool signedness = true; // Signedness of the Brainfuck memory. True for signed. +bool wrapInt = true; // Whether to wrap around an integer in Brainfuck. +bool breakpoint = false; // Whether to enable breakpoints in Brainfuck. +bool debug = true; // Whether to enable the real time debugging. +bool dark = true; // Theme for this app. True for dark. +bool horizontal = false; // Layout for this app. +bool withBOM = false; // Whether the opened file contained an UTF-8 BOM. +bool wordwrap = true; // Whether to enable the word wrap on editors. +wchar_t *cmdLine; // Pointer to the command line. +std::deque history; // Undo/Redo history. +std::wstring wstrFileName; // Current file name. +enum Brainfuck::noinput_t noInput = Brainfuck::NOINPUT_ZERO; // Brainfuck behavior on no input. +enum util::newline_t newLine = util::NEWLINE_CRLF; // Newline code for the current file. +enum state_t state = STATE_INIT; // Current state of this app. #ifndef UNDER_CE - case WM_DROPFILES: - ui::onDropFiles((HDROP)wParam); - break; - - case WM_GETMINMAXINFO: - ui::onGetMinMaxInfo((MINMAXINFO *)lParam); - break; - - case 0x2e0: // WM_DPICHANGED - ui::onDPIChanged(HIWORD(wParam), (const RECT *)lParam); - break; +int sysDPI = 96; // The value of system DPI. #endif - - case WM_APP_THREADEND: - WaitForSingleObject(g_hThread, INFINITE); - CloseHandle(g_hThread); - g_hThread = NULL; - g_ctrlThread = CTRLTHREAD_RUN; - break; - - case WM_CLOSE: - if (ui::promptSave()) DestroyWindow(hWnd); - break; - - case WM_DESTROY: - DeleteObject(BGDark); - ui::onDestroy(); - PostQuitMessage(0); - break; - - case WM_COMMAND: - ui::updateFocus(LOWORD(wParam)); - - switch (LOWORD(wParam)) { - case IDC_CMDBTN_FIRST: // Run - if (!didInit) { - if (!bfInit()) break; - didInit = true; - } - - g_hThread = myCreateThread(NULL, 0, threadRunner, NULL, 0, NULL); - if (g_hThread) { - SetThreadPriority(g_hThread, THREAD_PRIORITY_BELOW_NORMAL); - } else { - ui::messageBox(hWnd, L"Failed to create a runner thread.", L"Internal Error", - MB_ICONERROR); - break; - } - - ui::setMemory(NULL); - ui::setState(ui::STATE_RUN); - break; - - case IDC_CMDBTN_FIRST + 1: { // Next - if (!didInit) { - if (!bfInit()) break; - didInit = true; - } - - Brainfuck::result_t result = bfNext(); - ui::setState(result == Brainfuck::RESULT_FIN || result == Brainfuck::RESULT_ERR - ? ui::STATE_FINISH - : ui::STATE_PAUSE); - if (result == Brainfuck::RESULT_ERR) { - ui::messageBox(ui::hWnd, g_bf.getLastError(), L"Brainfuck Error", MB_ICONWARNING); - } - - unsigned size; - const unsigned char *memory = g_bf.getMemory(&size); - ui::setMemory(memory, size); - ui::selProg(g_bf.getProgPtr()); - break; - } - - case IDC_CMDBTN_FIRST + 2: // Pause - if (g_hThread) g_ctrlThread = CTRLTHREAD_PAUSE; - break; - - case IDC_CMDBTN_FIRST + 3: // End - if (g_hThread) { - g_ctrlThread = CTRLTHREAD_END; - } else { - ui::setState(ui::STATE_INIT); - } - didInit = false; - break; - - case IDM_FILE_NEW: - ui::openFile(true); - break; - - case IDM_FILE_OPEN: - ui::openFile(false); - break; - - case IDM_FILE_SAVE: - ui::saveFile(true); - break; - - case IDM_FILE_SAVE_AS: - ui::saveFile(false); - break; - - case IDM_FILE_EXIT: - SendMessageW(hWnd, WM_CLOSE, 0, 0); - break; - - case IDM_EDIT_UNDO: - ui::undo(); - break; - - case IDM_EDIT_REDO: - ui::redo(); - break; - - case IDM_EDIT_CUT: - ui::cut(); - break; - - case IDM_EDIT_COPY: - ui::copy(); - break; - - case IDM_EDIT_PASTE: - ui::paste(); - break; - - case IDM_EDIT_SELALL: - ui::selAll(); - break; - - case IDM_BF_MEMTYPE_SIGNED: - ui::signedness = true; - break; - - case IDM_BF_MEMTYPE_UNSIGNED: - ui::signedness = false; - break; - - case IDM_BF_OUTPUT_UTF8: - ui::outCharSet = IDM_BF_OUTPUT_UTF8; - break; - - case IDM_BF_OUTPUT_SJIS: - ui::outCharSet = IDM_BF_OUTPUT_SJIS; - break; - - case IDM_BF_OUTPUT_HEX: - ui::outCharSet = IDM_BF_OUTPUT_HEX; - break; - - case IDM_BF_INPUT_UTF8: - ui::inCharSet = IDM_BF_INPUT_UTF8; - break; - - case IDM_BF_INPUT_SJIS: - ui::inCharSet = IDM_BF_INPUT_SJIS; - break; - - case IDM_BF_INPUT_HEX: - ui::inCharSet = IDM_BF_INPUT_HEX; - break; - - case IDM_BF_NOINPUT_ERROR: - ui::noInput = Brainfuck::NOINPUT_ERROR; - break; - - case IDM_BF_NOINPUT_ZERO: - ui::noInput = Brainfuck::NOINPUT_ZERO; - break; - - case IDM_BF_NOINPUT_FF: - ui::noInput = Brainfuck::NOINPUT_FF; - break; - - case IDM_BF_INTOVF_ERROR: - ui::wrapInt = false; - break; - - case IDM_BF_INTOVF_WRAPAROUND: - ui::wrapInt = true; - break; - - case IDM_BF_BREAKPOINT: - ui::breakpoint = !ui::breakpoint; - g_bf.setBehavior(ui::noInput, ui::wrapInt, ui::signedness, ui::breakpoint); - break; - - case IDM_OPT_SPEED_FASTEST: - ui::speed = 0; - break; - - case IDM_OPT_SPEED_1MS: - ui::speed = 1; - break; - - case IDM_OPT_SPEED_10MS: - ui::speed = 10; - break; - - case IDM_OPT_SPEED_100MS: - ui::speed = 100; - break; - - case IDM_OPT_MEMVIEW: - if (DialogBoxW(ui::hInst, L"memviewopt", hWnd, ui::memViewProc) == IDOK && - ui::state != ui::STATE_INIT) { - unsigned size; - const unsigned char *memory = g_bf.getMemory(&size); - ui::setMemory(memory, size); - } - break; - - case IDM_OPT_TRACK: - ui::debug = !ui::debug; - break; - - case IDM_OPT_HLTPROG: - ui::selProg(g_bf.getProgPtr()); - break; - - case IDM_OPT_HLTMEM: - ui::selMemView(g_bf.getMemPtr()); - break; - - case IDM_OPT_DARK: - ui::switchTheme(); - break; - - case IDM_OPT_LAYOUT: - ui::switchLayout(); - break; - - case IDM_OPT_FONT: - ui::chooseFont(); - break; - - case IDM_OPT_WORDWRAP: - ui::switchWordwrap(); - break; - - case IDM_ABOUT: - ui::messageBox(hWnd, - APP_NAME L" version " APP_VERSION L"\r\n" - APP_DESCRIPTION L"\r\n\r\n" - APP_COPYRIGHT L"\r\n" - L"Licensed under the MIT License.", - L"About this software", - 0 - ); - break; - - default: - if (LOWORD(wParam) >= IDC_SCRKBD_FIRST && - LOWORD(wParam) < IDC_SCRKBD_FIRST + SCRKBD_LEN) { - ui::onScreenKeyboard(LOWORD(wParam) - IDC_SCRKBD_FIRST); - } - } - break; - - default: - return DefWindowProcW(hWnd, uMsg, wParam, lParam); - } - - return 0; -} +} // namespace global int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, wchar_t *lpCmdLine, int nShowCmd) { int i; UNREFERENCED_PARAMETER(hPrevInstance); + global::hInst = hInstance; + // Replaces slashes with backslashes. for (i = 0; lpCmdLine[i]; ++i) { if (lpCmdLine[i] == L'/') lpCmdLine[i] = L'\\'; } - g_cmdLine = lpCmdLine; + global::cmdLine = lpCmdLine; WNDCLASSW wcl; + memset(&wcl, 0, sizeof(WNDCLASSW)); wcl.hInstance = hInstance; wcl.lpszClassName = WND_CLASS_NAME; - wcl.lpfnWndProc = wndProc; + wcl.lpfnWndProc = wproc::wndProc; wcl.style = 0; #ifdef UNDER_CE wcl.hIcon = NULL; @@ -565,7 +82,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, wchar_t *lpCmd ShowWindow(hWnd, nShowCmd); #ifdef UNDER_CE - ShowWindow(hWnd, SW_MAXIMIZE); // Maximizes as most Windows CE devices have a small display. + ShowWindow(hWnd, SW_MAXIMIZE); #else DragAcceptFiles(hWnd, TRUE); #endif diff --git a/main.hpp b/main.hpp new file mode 100644 index 0000000..54e2320 --- /dev/null +++ b/main.hpp @@ -0,0 +1,140 @@ +#ifndef MAIN_HPP_ +#define MAIN_HPP_ + +#ifndef _UNICODE +#define _UNICODE +#endif +#ifndef UNICODE +#define UNICODE +#endif + +// Microsoft provided min/max macros conflict with C++ STL and even some Windows SDKs lack them. +// So we disable them here and redefine ourself. +#ifndef NOMINMAX +#define NOMINMAX +#endif +#define mymin(a, b) (((a) < (b)) ? (a) : (b)) +#define mymax(a, b) (((a) > (b)) ? (a) : (b)) + +#include + +#ifdef UNDER_CE +#include +#include +#else +#include +#endif + +#include +#include + +#include "bf.hpp" +#include "resource.h" +#include "util.hpp" + +// Workaround for wrong macro definitions in CeGCC. +#ifdef UNDER_CE +#if SW_MAXIMIZE != 12 +#undef SW_MAXIMIZE +#define SW_MAXIMIZE 12 +#endif +#if SW_MINIMIZE != 6 +#undef SW_MINIMIZE +#define SW_MINIMIZE 6 +#endif +#if WS_MINIMIZEBOX != 0x00010000L +#undef WS_MINIMIZEBOX +#define WS_MINIMIZEBOX 0x00010000L +#endif +#if WS_MAXIMIZEBOX != 0x00020000L +#undef WS_MAXIMIZEBOX +#define WS_MAXIMIZEBOX 0x00020000L +#endif + +// Expands to _beginthreadex() on Windows PC and CreateThread() on Windows CE. +#define myCreateThread(lpsa, cbStack, lpStartAddr, lpvThreadParam, fdwCreate, lpIDThread) \ + CreateThread(lpsa, cbStack, lpStartAddr, lpvThreadParam, fdwCreate, lpIDThread) + +// WinMain is already unicode on Windows CE. +#define wWinMain WinMain +#else +// Expands to _beginthreadex() on Windows PC and CreateThread() on Windows CE. +#define myCreateThread(lpsa, cbStack, lpStartAddr, lpvThreadParam, fdwCreate, lpIDThread) \ + (HANDLE) _beginthreadex(lpsa, cbStack, lpStartAddr, lpvThreadParam, fdwCreate, lpIDThread) + +// We define this enum manually as old SDKs don't have it. +typedef enum MONITOR_DPI_TYPE { + MDT_EFFECTIVE_DPI = 0, + MDT_ANGULAR_DPI = 1, + MDT_RAW_DPI = 2, + MDT_DEFAULT = MDT_EFFECTIVE_DPI +} MONITOR_DPI_TYPE; + +// The function pointer type for GetDpiForMonitor API. +typedef HRESULT(CALLBACK *GetDpiForMonitor_t)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, + unsigned *dpiX, unsigned *dpiY); +#endif + +// Making SetWindowLongW and GetWindowLongW compatible for both 32-bit and 64-bit system. +#ifdef _WIN64 +#define mySetWindowLongW(hWnd, index, data) SetWindowLongPtrW(hWnd, index, (LRESULT)(data)) +#define myGetWindowLongW(hWnd, index) GetWindowLongPtrW(hWnd, index) +#ifndef GWL_WNDPROC +#define GWL_WNDPROC GWLP_WNDPROC +#endif +#ifndef GWL_USERDATA +#define GWL_USERDATA GWLP_USERDATA +#endif +#else +#define mySetWindowLongW(hWnd, index, data) SetWindowLongW(hWnd, index, (LONG)(data)) +#define myGetWindowLongW(hWnd, index) GetWindowLongW(hWnd, index) +#endif + +#ifndef WS_OVERLAPPEDWINDOW +#define WS_OVERLAPPEDWINDOW \ + WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX +#endif +#define WM_APP_THREADEND WM_APP +#define MAX_HISTORY 32 + +namespace global { +enum state_t { STATE_INIT, STATE_RUN, STATE_PAUSE, STATE_FINISH }; + +extern HINSTANCE hInst; +extern HWND hWnd; +extern HWND hEditor; +extern HWND hInput; +extern HWND hOutput; +extern HWND hMemView; +extern HWND hFocused; +extern HWND hCmdBtn[CMDBTN_LEN]; +extern HWND hScrKB[SCRKBD_LEN]; +extern HMENU hMenu; +extern LOGFONTW editFont; +extern int speed; +extern int outCharSet; +extern int inCharSet; +extern int memViewStart; +extern int historyIndex; +extern int savedIndex; +extern bool validHistory; +extern bool signedness; +extern bool wrapInt; +extern bool breakpoint; +extern bool debug; +extern bool dark; +extern bool horizontal; +extern bool withBOM; +extern bool wordwrap; +extern wchar_t *cmdLine; +extern std::deque history; +extern std::wstring wstrFileName; +extern Brainfuck::noinput_t noInput; +extern enum util::newline_t newLine; +extern enum state_t state; +#ifndef UNDER_CE +extern int sysDPI; +#endif +} // namespace global + +#endif diff --git a/msg.cpp b/msg.cpp new file mode 100644 index 0000000..1a5f784 --- /dev/null +++ b/msg.cpp @@ -0,0 +1,624 @@ +#include "msg.hpp" + +#include "runner.hpp" +#include "ui.hpp" +#include "wproc.hpp" + +namespace msg { +static const wchar_t *wcCmdBtn[CMDBTN_LEN] = {L"Run", L"Next", L"Pause", L"End"}, + *wcScrKB[SCRKBD_LEN] = {L">", L"<", L"+", L"-", L".", L",", L"[", L"]", L"@"}; + +static HFONT hBtnFont = NULL, hEditFont = NULL; +static int topPadding = 0; +static bool didInit = false; + +#ifdef UNDER_CE +static HWND hCmdBar; +#else +static int dpi = 96; +#endif + +#ifdef UNDER_CE +// Adjust the coordinate for the current DPI. +static inline int adjust(int coord) { return coord; } +#else +// Adjust the coordinate for the current DPI. +static inline int adjust(int coord) { return coord * dpi / 96; } +#endif + +void onActivate(HWND hWnd, UINT state, HWND hWndActDeact, BOOL fMinimized) { + UNREFERENCED_PARAMETER(hWnd); + UNREFERENCED_PARAMETER(hWndActDeact); + UNREFERENCED_PARAMETER(fMinimized); + + if (state == WA_ACTIVE || state == WA_CLICKACTIVE) ui::updateFocus(); +} + +void onClose(HWND hWnd) { + if (ui::promptSave()) DestroyWindow(hWnd); +} + +void onCommand(HWND hWnd, int id, HWND hWndCtl, UINT codeNotify) { + UNREFERENCED_PARAMETER(codeNotify); + + ui::updateFocus(hWndCtl); + + switch (id) { + case IDC_CMDBTN_FIRST: // Run + if (!didInit) { + if (!runner::bfInit()) break; + didInit = true; + } + + runner::hThread = myCreateThread(NULL, 0, runner::threadRunner, NULL, 0, NULL); + if (runner::hThread) { + SetThreadPriority(runner::hThread, THREAD_PRIORITY_BELOW_NORMAL); + } else { + util::messageBox(hWnd, global::hInst, L"Failed to create a runner thread.", + L"Internal Error", MB_ICONERROR); + break; + } + + SetWindowTextW(global::hMemView, NULL); + ui::setState(global::STATE_RUN); + break; + + case IDC_CMDBTN_FIRST + 1: { // Next + if (!didInit) { + if (!runner::bfInit()) break; + didInit = true; + } + + Brainfuck::result_t result = runner::bfNext(); + ui::setState(result == Brainfuck::RESULT_FIN || result == Brainfuck::RESULT_ERR + ? global::STATE_FINISH + : global::STATE_PAUSE); + if (result == Brainfuck::RESULT_ERR) { + util::messageBox(hWnd, global::hInst, runner::bf.getLastError(), L"Brainfuck Error", + MB_ICONWARNING); + } + + unsigned size; + const unsigned char *memory = runner::bf.getMemory(&size); + ui::setMemory(memory, size); + ui::selProg(runner::bf.getProgPtr()); + break; + } + + case IDC_CMDBTN_FIRST + 2: // Pause + if (runner::hThread) runner::ctrlThread = runner::CTRLTHREAD_PAUSE; + break; + + case IDC_CMDBTN_FIRST + 3: // End + if (runner::hThread) { + runner::ctrlThread = runner::CTRLTHREAD_END; + } else { + ui::setState(global::STATE_INIT); + } + didInit = false; + break; + + case IDM_FILE_NEW: + ui::openFile(true); + break; + + case IDM_FILE_OPEN: + ui::openFile(false); + break; + + case IDM_FILE_SAVE: + ui::saveFile(true); + break; + + case IDM_FILE_SAVE_AS: + ui::saveFile(false); + break; + + case IDM_FILE_EXIT: + SendMessageW(hWnd, WM_CLOSE, 0, 0); + break; + + case IDM_EDIT_UNDO: + ui::undo(); + break; + + case IDM_EDIT_REDO: + ui::redo(); + break; + + case IDM_EDIT_CUT: + SendMessageW(global::hFocused, WM_CUT, 0, 0); + break; + + case IDM_EDIT_COPY: + SendMessageW(global::hFocused, WM_COPY, 0, 0); + break; + + case IDM_EDIT_PASTE: + SendMessageW(global::hFocused, WM_PASTE, 0, 0); + break; + + case IDM_EDIT_SELALL: + SendMessageW(global::hFocused, EM_SETSEL, 0, -1); + break; + + case IDM_BF_MEMTYPE_SIGNED: + global::signedness = true; + break; + + case IDM_BF_MEMTYPE_UNSIGNED: + global::signedness = false; + break; + + case IDM_BF_OUTPUT_UTF8: + global::outCharSet = IDM_BF_OUTPUT_UTF8; + break; + + case IDM_BF_OUTPUT_SJIS: + global::outCharSet = IDM_BF_OUTPUT_SJIS; + break; + + case IDM_BF_OUTPUT_HEX: + global::outCharSet = IDM_BF_OUTPUT_HEX; + break; + + case IDM_BF_INPUT_UTF8: + global::inCharSet = IDM_BF_INPUT_UTF8; + break; + + case IDM_BF_INPUT_SJIS: + global::inCharSet = IDM_BF_INPUT_SJIS; + break; + + case IDM_BF_INPUT_HEX: + global::inCharSet = IDM_BF_INPUT_HEX; + break; + + case IDM_BF_NOINPUT_ERROR: + global::noInput = Brainfuck::NOINPUT_ERROR; + break; + + case IDM_BF_NOINPUT_ZERO: + global::noInput = Brainfuck::NOINPUT_ZERO; + break; + + case IDM_BF_NOINPUT_FF: + global::noInput = Brainfuck::NOINPUT_FF; + break; + + case IDM_BF_INTOVF_ERROR: + global::wrapInt = false; + break; + + case IDM_BF_INTOVF_WRAPAROUND: + global::wrapInt = true; + break; + + case IDM_BF_BREAKPOINT: + global::breakpoint = !global::breakpoint; + runner::bf.setBehavior(global::noInput, global::wrapInt, global::signedness, + global::breakpoint); + break; + + case IDM_OPT_SPEED_FASTEST: + global::speed = 0; + break; + + case IDM_OPT_SPEED_1MS: + global::speed = 1; + break; + + case IDM_OPT_SPEED_10MS: + global::speed = 10; + break; + + case IDM_OPT_SPEED_100MS: + global::speed = 100; + break; + + case IDM_OPT_MEMVIEW: + if (DialogBoxW(global::hInst, L"memviewopt", hWnd, wproc::memViewProc) == IDOK && + global::state != global::STATE_INIT) { + unsigned size; + const unsigned char *memory = runner::bf.getMemory(&size); + ui::setMemory(memory, size); + } + break; + + case IDM_OPT_TRACK: + global::debug = !global::debug; + break; + + case IDM_OPT_HLTPROG: + ui::selProg(runner::bf.getProgPtr()); + break; + + case IDM_OPT_HLTMEM: + ui::selMemView(runner::bf.getMemPtr()); + break; + + case IDM_OPT_DARK: + ui::switchTheme(); + break; + + case IDM_OPT_LAYOUT: + ui::switchLayout(); + break; + + case IDM_OPT_FONT: + ui::chooseFont(); + break; + + case IDM_OPT_WORDWRAP: + ui::switchWordwrap(); + break; + + case IDM_ABOUT: + util::messageBox(hWnd, global::hInst, + APP_NAME L" version " APP_VERSION L"\r\n" + APP_DESCRIPTION L"\r\n\r\n" + APP_COPYRIGHT L"\r\n" + L"Licensed under the MIT License.", + L"About this software", + 0 + ); + break; + + default: + if (id >= IDC_SCRKBD_FIRST && id < IDC_SCRKBD_FIRST + SCRKBD_LEN && + global::hFocused == global::hEditor) { + SendMessageW(global::hEditor, EM_REPLACESEL, 0, (WPARAM)wcScrKB[id - IDC_SCRKBD_FIRST]); + } + } +} + +BOOL onCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct) { + UNREFERENCED_PARAMETER(lpCreateStruct); + + size_t i; + global::hWnd = hWnd; + +#ifdef UNDER_CE + wchar_t wcMenu[] = L"menu"; // CommandBar_InsertMenubarEx requires non-const value. + InitCommonControls(); + hCmdBar = CommandBar_Create(global::hInst, hWnd, 1); + CommandBar_InsertMenubarEx(hCmdBar, global::hInst, wcMenu, 0); + CommandBar_Show(hCmdBar, TRUE); + topPadding = CommandBar_Height(hCmdBar); + global::hMenu = CommandBar_GetMenu(hCmdBar, 0); +#else + global::hMenu = LoadMenu(global::hInst, L"menu"); + SetMenu(hWnd, global::hMenu); +#endif + + // Program editor + global::hEditor = CreateWindowExW( + 0, L"EDIT", L"", + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL | + (global::wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, + 0, 0, 0, 0, hWnd, (HMENU)IDC_EDITOR, global::hInst, NULL); + SendMessageW(global::hEditor, EM_SETLIMITTEXT, (WPARAM)-1, 0); + mySetWindowLongW(global::hEditor, GWL_USERDATA, + mySetWindowLongW(global::hEditor, GWL_WNDPROC, wproc::editorProc)); + + // Program input + global::hInput = CreateWindowExW( + 0, L"EDIT", L"", + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL | + (global::wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, + 0, 0, 0, 0, hWnd, (HMENU)IDC_INPUT, global::hInst, NULL); + SendMessageW(global::hInput, EM_SETLIMITTEXT, (WPARAM)-1, 0); + + // Program output + global::hOutput = CreateWindowExW( + 0, L"EDIT", L"", + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL | + WS_VSCROLL | (global::wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, + 0, 0, 0, 0, hWnd, (HMENU)IDC_OUTPUT, global::hInst, NULL); + SendMessageW(global::hOutput, EM_SETLIMITTEXT, (WPARAM)-1, 0); + + // Memory view + global::hMemView = CreateWindowExW(0, L"EDIT", L"", + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | + ES_READONLY | ES_AUTOVSCROLL | WS_VSCROLL | ES_NOHIDESEL, + 0, 0, 0, 0, hWnd, (HMENU)IDC_MEMVIEW, global::hInst, NULL); + SendMessageW(global::hMemView, EM_SETLIMITTEXT, (WPARAM)-1, 0); + + // Command button + for (i = 0; i < CMDBTN_LEN; ++i) { + global::hCmdBtn[i] = CreateWindowExW( + 0, L"BUTTON", wcCmdBtn[i], + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | (i == 2 || i == 3 ? WS_DISABLED : 0), 0, 0, 0, 0, + hWnd, (HMENU)(IDC_CMDBTN_FIRST + i), global::hInst, NULL); + } + + // Screen keyboard + for (i = 0; i < SCRKBD_LEN; ++i) { + global::hScrKB[i] = + CreateWindowExW(0, L"BUTTON", wcScrKB[i], WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 0, 0, 0, 0, + hWnd, (HMENU)(IDC_SCRKBD_FIRST + i), global::hInst, NULL); + } + + global::hFocused = global::hEditor; + + // Default configurations for the editor font. + // We create the actual font object on onSize function. + ZeroMemory(&global::editFont, sizeof(global::editFont)); + global::editFont.lfCharSet = DEFAULT_CHARSET; + global::editFont.lfQuality = ANTIALIASED_QUALITY; + global::editFont.lfHeight = -15; // Represents font size 11 in 96 DPI. +#ifdef UNDER_CE + // Sets a pre-installed font on Windows CE, as it doesn't have "MS Shell Dlg". + lstrcpyW(global::editFont.lfFaceName, L"Tahoma"); +#else + // Sets a logical font face name for localization. + // It maps to a default shell font associated with the current culture/locale. + lstrcpyW(global::editFont.lfFaceName, L"MS Shell Dlg"); + + // Obtains the "system DPI" value. We use this as the fallback value on older Windows versions + // and to calculate the appropriate font height value for ChooseFontW. + HDC hDC = GetDC(hWnd); + global::sysDPI = GetDeviceCaps(hDC, LOGPIXELSX); + ReleaseDC(hWnd, hDC); + + // Tries to load the GetDpiForMonitor API. Use of the full path improves security. We avoid a + // direct call to keep this program compatible with Windows 7 and earlier. + // + // Microsoft recommends the use of GetDpiForWindow API instead of this API according to their + // documentation. However, it requires Windows 10 1607 or later, which makes this compatibility + // keeping code more complicated, and GetDpiForMonitor API still works for programs that only use + // the process-wide DPI awareness. Here, as we only use the process-wide DPI awareness, we are + // going to use GetDpiForMonitor API. + // + // References: + // https://learn.microsoft.com/en-us/windows/win32/api/shellscalingapi/nf-shellscalingapi-getdpiformonitor + // https://mariusbancila.ro/blog/2021/05/19/how-to-build-high-dpi-aware-native-desktop-applications/ + GetDpiForMonitor_t getDpiForMonitor = NULL; + HMODULE dll = LoadLibraryW(L"C:\\Windows\\System32\\Shcore.dll"); + if (dll) { + getDpiForMonitor = (GetDpiForMonitor_t)(void *)GetProcAddress(dll, "GetDpiForMonitor"); + } + + // Tests whether it successfully got the GetDpiForMonitor API. + if (getDpiForMonitor) { // It got (the system is presumably Windows 8.1 or later). + unsigned tmpX, tmpY; + getDpiForMonitor(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST), MDT_EFFECTIVE_DPI, &tmpX, + &tmpY); + dpi = tmpX; + } else { // It failed (the system is presumably older than Windows 8.1). + dpi = global::sysDPI; + } + if (dll) FreeLibrary(dll); + + // Adjusts the windows size according to the DPI value. + SetWindowPos(hWnd, NULL, 0, 0, adjust(480), adjust(320), + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE); +#endif + + if (global::cmdLine[0]) { + ui::openFile(false, global::cmdLine); + } else { + ui::openFile(true); + } + + return TRUE; +} + +void onDestroy() { + DeleteObject(hBtnFont); + DeleteObject(hEditFont); +} + +void onInitMenuPopup(HWND hWnd, HMENU hMenu, UINT item, BOOL fSystemMenu) { + UNREFERENCED_PARAMETER(hWnd); + UNREFERENCED_PARAMETER(item); + UNREFERENCED_PARAMETER(fSystemMenu); + + // Brainfuck -> Memory Type + CheckMenuRadioItem(hMenu, IDM_BF_MEMTYPE_SIGNED, IDM_BF_MEMTYPE_UNSIGNED, + global::signedness ? IDM_BF_MEMTYPE_SIGNED : IDM_BF_MEMTYPE_UNSIGNED, + MF_BYCOMMAND); + + // Brainfuck -> Output Charset + CheckMenuRadioItem(hMenu, IDM_BF_OUTPUT_UTF8, IDM_BF_OUTPUT_HEX, global::outCharSet, + MF_BYCOMMAND); + + // Brainfuck -> Input Charset + CheckMenuRadioItem(hMenu, IDM_BF_INPUT_UTF8, IDM_BF_INPUT_HEX, global::inCharSet, MF_BYCOMMAND); + + // Brainfuck -> Input Instruction + CheckMenuRadioItem(hMenu, IDM_BF_NOINPUT_ERROR, IDM_BF_NOINPUT_FF, + IDM_BF_NOINPUT_ERROR + global::noInput, MF_BYCOMMAND); + + // Brainfuck -> Integer Overflow + CheckMenuRadioItem(hMenu, IDM_BF_INTOVF_ERROR, IDM_BF_INTOVF_WRAPAROUND, + global::wrapInt ? IDM_BF_INTOVF_WRAPAROUND : IDM_BF_INTOVF_ERROR, + MF_BYCOMMAND); + + // Brainfuck -> Breakpoint + CheckMenuItem(hMenu, IDM_BF_BREAKPOINT, + MF_BYCOMMAND | global::breakpoint ? MF_CHECKED : MF_UNCHECKED); + + // Options -> Speed + if (global::speed == 0) { + CheckMenuRadioItem(hMenu, IDM_OPT_SPEED_FASTEST, IDM_OPT_SPEED_100MS, IDM_OPT_SPEED_FASTEST, + MF_BYCOMMAND); + } else if (global::speed == 1) { + CheckMenuRadioItem(hMenu, IDM_OPT_SPEED_FASTEST, IDM_OPT_SPEED_100MS, IDM_OPT_SPEED_1MS, + MF_BYCOMMAND); + } else if (global::speed == 10) { + CheckMenuRadioItem(hMenu, IDM_OPT_SPEED_FASTEST, IDM_OPT_SPEED_100MS, IDM_OPT_SPEED_10MS, + MF_BYCOMMAND); + } else if (global::speed == 100) { + CheckMenuRadioItem(hMenu, IDM_OPT_SPEED_FASTEST, IDM_OPT_SPEED_100MS, IDM_OPT_SPEED_100MS, + MF_BYCOMMAND); + } + + // Options -> Debug + CheckMenuItem(hMenu, IDM_OPT_TRACK, MF_BYCOMMAND | global::debug ? MF_CHECKED : MF_UNCHECKED); + + // Options -> Word Wrap + CheckMenuItem(hMenu, IDM_OPT_WORDWRAP, + MF_BYCOMMAND | global::wordwrap ? MF_CHECKED : MF_UNCHECKED); + + if (global::state == global::STATE_INIT) { + ui::enableMenus(IDM_FILE_NEW, true); + ui::enableMenus(IDM_FILE_OPEN, true); + ui::enableMenus(IDM_EDIT_UNDO, global::validHistory && global::historyIndex > 0); + ui::enableMenus(IDM_EDIT_REDO, + global::validHistory && global::historyIndex < (int)global::history.size() - 1); + ui::enableMenus(IDM_BF_MEMTYPE_UNSIGNED, true); + ui::enableMenus(IDM_BF_OUTPUT_HEX, true); + ui::enableMenus(IDM_BF_INPUT_HEX, true); + ui::enableMenus(IDM_BF_NOINPUT_FF, true); + ui::enableMenus(IDM_BF_INTOVF_WRAPAROUND, true); + ui::enableMenus(IDM_BF_BREAKPOINT, true); + ui::enableMenus(IDM_OPT_SPEED_100MS, true); + ui::enableMenus(IDM_OPT_MEMVIEW, true); + ui::enableMenus(IDM_OPT_TRACK, true); + ui::enableMenus(IDM_OPT_HLTPROG, false); + ui::enableMenus(IDM_OPT_HLTMEM, false); + } else if (global::state == global::STATE_RUN) { + ui::enableMenus(IDM_FILE_NEW, false); + ui::enableMenus(IDM_FILE_OPEN, false); + ui::enableMenus(IDM_EDIT_UNDO, false); + ui::enableMenus(IDM_EDIT_REDO, false); + ui::enableMenus(IDM_BF_MEMTYPE_UNSIGNED, false); + ui::enableMenus(IDM_BF_OUTPUT_HEX, false); + ui::enableMenus(IDM_BF_INPUT_HEX, false); + ui::enableMenus(IDM_BF_NOINPUT_FF, false); + ui::enableMenus(IDM_BF_INTOVF_WRAPAROUND, false); + ui::enableMenus(IDM_BF_BREAKPOINT, false); + ui::enableMenus(IDM_OPT_SPEED_100MS, false); + ui::enableMenus(IDM_OPT_MEMVIEW, false); + ui::enableMenus(IDM_OPT_TRACK, false); + ui::enableMenus(IDM_OPT_HLTPROG, false); + ui::enableMenus(IDM_OPT_HLTMEM, false); + } else if (global::state == global::STATE_PAUSE || global::state == global::STATE_FINISH) { + ui::enableMenus(IDM_FILE_NEW, false); + ui::enableMenus(IDM_FILE_OPEN, false); + ui::enableMenus(IDM_EDIT_UNDO, false); + ui::enableMenus(IDM_EDIT_REDO, false); + ui::enableMenus(IDM_BF_MEMTYPE_UNSIGNED, false); + ui::enableMenus(IDM_BF_OUTPUT_HEX, false); + ui::enableMenus(IDM_BF_INPUT_HEX, false); + ui::enableMenus(IDM_BF_NOINPUT_FF, false); + ui::enableMenus(IDM_BF_INTOVF_WRAPAROUND, false); + ui::enableMenus(IDM_BF_BREAKPOINT, true); + ui::enableMenus(IDM_OPT_SPEED_100MS, true); + ui::enableMenus(IDM_OPT_MEMVIEW, true); + ui::enableMenus(IDM_OPT_TRACK, true); + ui::enableMenus(IDM_OPT_HLTPROG, true); + ui::enableMenus(IDM_OPT_HLTMEM, true); + } +} + +void onSize(HWND hWnd, UINT state, int cx, int cy) { + UNREFERENCED_PARAMETER(state); + UNREFERENCED_PARAMETER(cx); + UNREFERENCED_PARAMETER(cy); + + onSize(hWnd); +} + +void onSize(HWND hWnd) { + RECT rect; + +#ifdef UNDER_CE + // Manually forces the minimum window size as Windows CE doesn't support WM_GETMINMAXINFO. + // Seemingly, Windows CE doesn't re-send WM_SIZE on SetWindowPos calls inside a WM_SIZE handler. + GetWindowRect(hWnd, &rect); + if (rect.right - rect.left < 480 || rect.bottom - rect.top < 320) { + SetWindowPos(hWnd, NULL, 0, 0, mymax(480, rect.right - rect.left), + mymax(320, rect.bottom - rect.top), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE); + } +#endif + + int i; + LOGFONTW rLogfont; + + // Button font + ZeroMemory(&rLogfont, sizeof(rLogfont)); + rLogfont.lfHeight = adjust(-15); + rLogfont.lfWeight = FW_BOLD; + rLogfont.lfCharSet = DEFAULT_CHARSET; + rLogfont.lfQuality = ANTIALIASED_QUALITY; + lstrcpyW(rLogfont.lfFaceName, global::editFont.lfFaceName); // Syncs with editors. + HFONT newBtnFont = CreateFontIndirectW(&rLogfont); + + // Editor/Output font + memcpy(&rLogfont, &global::editFont, sizeof(LOGFONTW)); + rLogfont.lfHeight = adjust(global::editFont.lfHeight); + HFONT newEditFont = CreateFontIndirectW(&rLogfont); + +#ifdef UNDER_CE + // We must "move" the command bar to prevent a glitch. + MoveWindow(hCmdBar, 0, 0, 0, 0, TRUE); +#endif + + // Moves and resizes controls, and applies the newly created fonts for them. + int curX = 0; + for (i = 0; i < CMDBTN_LEN; ++i) { + MoveWindow(global::hCmdBtn[i], curX, topPadding, adjust(46), adjust(32), TRUE); + SendMessageW(global::hCmdBtn[i], WM_SETFONT, (WPARAM)newBtnFont, MAKELPARAM(TRUE, 0)); + curX += adjust(46); + } + for (i = 0; i < SCRKBD_LEN; ++i) { + MoveWindow(global::hScrKB[i], curX, topPadding, adjust(30), adjust(32), TRUE); + SendMessageW(global::hScrKB[i], WM_SETFONT, (WPARAM)newBtnFont, MAKELPARAM(TRUE, 0)); + curX += adjust(30); + } + + int topEditor = topPadding + adjust(32); + GetClientRect(hWnd, &rect); + int scrX = rect.right, scrY = rect.bottom; + int halfY = (scrY - topEditor) / 2, centerY = (scrY + topEditor) / 2; + if (global::horizontal) { + MoveWindow(global::hEditor, 0, topEditor, scrX / 3, scrY - topEditor, TRUE); + MoveWindow(global::hInput, scrX / 3, topEditor, scrX / 3, halfY, TRUE); + MoveWindow(global::hOutput, scrX / 3, halfY + topEditor, scrX / 3, scrY - halfY - topEditor, + TRUE); + MoveWindow(global::hMemView, scrX * 2 / 3, topEditor, scrX - scrX * 2 / 3, scrY - topEditor, + TRUE); + } else { + MoveWindow(global::hEditor, 0, topEditor, scrX, halfY, TRUE); + MoveWindow(global::hInput, 0, centerY, scrX / 2, halfY / 2, TRUE); + MoveWindow(global::hOutput, scrX / 2, centerY, scrX - scrX / 2, halfY / 2, TRUE); + MoveWindow(global::hMemView, 0, centerY + halfY / 2, scrX, scrY - centerY - halfY / 2, TRUE); + } + SendMessageW(global::hEditor, WM_SETFONT, (WPARAM)newEditFont, MAKELPARAM(TRUE, 0)); + SendMessageW(global::hInput, WM_SETFONT, (WPARAM)newEditFont, MAKELPARAM(TRUE, 0)); + SendMessageW(global::hOutput, WM_SETFONT, (WPARAM)newEditFont, MAKELPARAM(TRUE, 0)); + SendMessageW(global::hMemView, WM_SETFONT, (WPARAM)newEditFont, MAKELPARAM(TRUE, 0)); + + if (hBtnFont) DeleteObject(hBtnFont); + if (hEditFont) DeleteObject(hEditFont); + hBtnFont = newBtnFont; + hEditFont = newEditFont; + InvalidateRect(hWnd, NULL, FALSE); +} + +#ifndef UNDER_CE +void onDropFiles(HWND hWnd, HDROP hDrop) { + UNREFERENCED_PARAMETER(hWnd); + + wchar_t wcFileName[MAX_PATH]; + DragQueryFileW(hDrop, 0, wcFileName, MAX_PATH); + DragFinish(hDrop); + ui::openFile(false, wcFileName); +} + +void onGetMinMaxInfo(HWND hWnd, LPMINMAXINFO lpMinMaxInfo) { + UNREFERENCED_PARAMETER(hWnd); + + lpMinMaxInfo->ptMinTrackSize.x = adjust(480); + lpMinMaxInfo->ptMinTrackSize.y = adjust(320); +} + +void onDPIChanged(HWND hWnd, int dpi, const RECT *rect) { + msg::dpi = dpi; + MoveWindow(hWnd, rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top, + FALSE); +} +#endif +} // namespace msg diff --git a/msg.hpp b/msg.hpp new file mode 100644 index 0000000..929c0f6 --- /dev/null +++ b/msg.hpp @@ -0,0 +1,22 @@ +#ifndef MSG_HPP_ +#define MSG_HPP_ + +#include "main.hpp" + +namespace msg { +extern void onActivate(HWND hWnd, UINT state, HWND hWndActDeact, BOOL fMinimized); +extern void onClose(HWND hWnd); +extern void onCommand(HWND hWnd, int id, HWND hWndCtl, UINT codeNotify); +extern BOOL onCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct); +extern void onDestroy(); +extern void onInitMenuPopup(HWND hWnd, HMENU hMenu, UINT item, BOOL fSystemMenu); +extern void onSize(HWND hWnd, UINT state, int cx, int cy); +extern void onSize(HWND hWnd); +#ifndef UNDER_CE +extern void onDropFiles(HWND hWnd, HDROP hdrop); +extern void onGetMinMaxInfo(HWND hWnd, LPMINMAXINFO lpMinMaxInfo); +extern void onDPIChanged(HWND hWnd, int dpi, const RECT *rect); +#endif +} // namespace msg + +#endif diff --git a/resource.h b/resource.h index 30dbc13..9b5258b 100644 --- a/resource.h +++ b/resource.h @@ -3,14 +3,14 @@ #define APP_NAME L"Brain fuck" #define APP_DESCRIPTION L"Brainfuck interpreter for SHARP Brain." -#define APP_COPYRIGHT L"(C) 2022 watamario15 (https://github.com/watamario15)" +#define APP_COPYRIGHT L"(C) 2022-2023 watamario15 (https://github.com/watamario15)" #define APP_VERSION L"1.0" #define VER_STR_COMPANYNAME "watamario15" #define VER_STR_DESCRIPTION "Brainfuck interpreter for SHARP Brain." #define VER_STR_FILEVERSION 1, 0, 0, 0 #define VER_STR_APPNAME "Brain fuck" -#define VER_STR_COPYRIGHT "(C) 2022 watamario15 (https://github.com/watamario15)" +#define VER_STR_COPYRIGHT "(C) 2022-2023 watamario15 (https://github.com/watamario15)" #define VER_STR_ORIGINALFILENAME "Brainfuck.exe" #define VER_STR_VERSION "1.0" diff --git a/runner.cpp b/runner.cpp new file mode 100644 index 0000000..f41bc48 --- /dev/null +++ b/runner.cpp @@ -0,0 +1,180 @@ +#include "runner.hpp" + +#include "tokenizer.hpp" +#include "ui.hpp" + +namespace runner { +class Brainfuck bf; +unsigned int timerID = 0; +HANDLE hThread = NULL; +volatile enum ctrlthread_t ctrlThread = CTRLTHREAD_RUN; + +static class UTF8Tokenizer u8Tokenizer; +static class SJISTokenizer sjisTokenizer; +static bool prevCR = false; + +bool bfInit() { + static unsigned char *input = NULL; + static wchar_t *program = NULL; + int inLen; + + int inputSize = GetWindowTextLengthW(global::hInput) + 1; + wchar_t *wcInput = (wchar_t *)malloc(sizeof(wchar_t) * inputSize); + if (wcInput) { + GetWindowTextW(global::hInput, wcInput, inputSize); + } else { + util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", + MB_ICONWARNING); + return false; + } + + unsigned char *newInput; + if (global::inCharSet == IDM_BF_INPUT_HEX) { + if (!util::parseHex(global::hWnd, global::hInst, wcInput, &newInput, &inLen)) return false; + } else { + int codePage = (global::inCharSet == IDM_BF_INPUT_SJIS) ? 932 : CP_UTF8; + inLen = WideCharToMultiByte(codePage, 0, wcInput, -1, NULL, 0, NULL, NULL); + newInput = (unsigned char *)malloc(inLen); + if (!newInput) { + util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", + MB_ICONWARNING); + free(wcInput); + return false; + } + WideCharToMultiByte(codePage, 0, wcInput, -1, (char *)newInput, inLen, NULL, NULL); + inLen--; + } + + free(wcInput); + + if (global::outCharSet == IDM_BF_OUTPUT_UTF8) { + u8Tokenizer.flush(); + } else if (global::outCharSet == IDM_BF_OUTPUT_SJIS) { + sjisTokenizer.flush(); + } + + int editorSize = GetWindowTextLengthW(global::hEditor) + 1; + wchar_t *wcEditor = (wchar_t *)malloc(sizeof(wchar_t) * editorSize); + if (wcEditor) { + GetWindowTextW(global::hEditor, wcEditor, editorSize); + } else { + util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", + MB_ICONWARNING); + free(newInput); + return false; + } + + bf.reset((unsigned int)wcslen(wcEditor), wcEditor, inLen, input); + + SetWindowTextW(global::hOutput, NULL); + SetWindowTextW(global::hMemView, NULL); + prevCR = false; + if (input) free(input); + if (program) free(program); + input = newInput; + program = wcEditor; + + return true; +} + +enum Brainfuck::result_t bfNext() { + unsigned char output; + bool didOutput; + + bf.setBehavior(global::noInput, global::wrapInt, global::signedness, global::breakpoint); + enum Brainfuck::result_t result = bf.next(&output, &didOutput); + + if (result == Brainfuck::RESULT_FIN || result == Brainfuck::RESULT_ERR) { + if (global::outCharSet == IDM_BF_OUTPUT_UTF8) { + const wchar_t *rest = u8Tokenizer.flush(); + if (rest) ui::appendOutput(rest); + } else if (global::outCharSet == IDM_BF_OUTPUT_SJIS) { + const wchar_t *rest = sjisTokenizer.flush(); + if (rest) ui::appendOutput(rest); + } + return result; + } + + if (didOutput) { + if (global::outCharSet == IDM_BF_OUTPUT_HEX) { + wchar_t wcOut[4]; + util::toHex(output, wcOut); + ui::appendOutput(wcOut); + } else { + // Align newlines to CRLF. + if (prevCR && output != '\n') ui::appendOutput(L"\n"); + if (!prevCR && output == '\n') ui::appendOutput(L"\r"); + prevCR = output == '\r'; + + if (global::outCharSet == IDM_BF_OUTPUT_UTF8) { + const wchar_t *converted = u8Tokenizer.add(output); + if (converted) ui::appendOutput(converted); + } else if (global::outCharSet == IDM_BF_OUTPUT_SJIS) { + const wchar_t *converted = sjisTokenizer.add(output); + if (converted) ui::appendOutput(converted); + } + } + } + + return result; +} + +tret_t WINAPI threadRunner(void *lpParameter) { + UNREFERENCED_PARAMETER(lpParameter); + + HANDLE hEvent = NULL; + if (global::speed != 0) { + hEvent = CreateEventW(NULL, FALSE, TRUE, NULL); + if (timeBeginPeriod(global::speed) == TIMERR_NOERROR) { + timerID = timeSetEvent(global::speed, global::speed, (LPTIMECALLBACK)hEvent, 0, + TIME_PERIODIC | TIME_CALLBACK_EVENT_SET); + } + if (!timerID) { + util::messageBox(global::hWnd, global::hInst, + L"This speed is not supported on your device. Try slowing down.", L"Error", + MB_ICONERROR); + ui::setState(global::STATE_INIT); + PostMessageW(global::hWnd, WM_APP_THREADEND, 0, 0); + return 1; + } + } + + enum Brainfuck::result_t result; + while (ctrlThread == CTRLTHREAD_RUN) { + if (global::speed != 0) WaitForSingleObject(hEvent, INFINITE); + if ((result = bfNext()) != Brainfuck::RESULT_RUN) break; + if (global::debug) { + unsigned size; + const unsigned char *memory = bf.getMemory(&size); + ui::setMemory(memory, size); + ui::selProg(bf.getProgPtr()); + } + } + + if (timerID) { + timeKillEvent(timerID); + timeEndPeriod(global::speed); + timerID = 0; + } + if (hEvent) CloseHandle(hEvent); + + if (ctrlThread == CTRLTHREAD_END) { + ui::setState(global::STATE_INIT); + } else { // Paused, finished, error, or breakpoint + ui::setState(result == Brainfuck::RESULT_RUN || result == Brainfuck::RESULT_BREAK + ? global::STATE_PAUSE + : global::STATE_FINISH); + if (result == Brainfuck::RESULT_ERR) { + util::messageBox(global::hWnd, global::hInst, bf.getLastError(), L"Brainfuck Error", + MB_ICONWARNING); + } + unsigned size; + const unsigned char *memory = bf.getMemory(&size); + ui::setMemory(memory, size); + ui::selProg(bf.getProgPtr()); + } + + PostMessageW(global::hWnd, WM_APP_THREADEND, 0, 0); + return 0; +} +} // namespace runner diff --git a/runner.hpp b/runner.hpp new file mode 100644 index 0000000..1ca5b35 --- /dev/null +++ b/runner.hpp @@ -0,0 +1,32 @@ +#ifndef RUNNER_HPP_ +#define RUNNER_HPP_ + +#include "main.hpp" + +#ifdef UNDER_CE +// The return type of a thread function. +typedef DWORD tret_t; +#else +// The return type of a thread function. +typedef unsigned int tret_t; +#endif + +namespace runner { +enum ctrlthread_t { CTRLTHREAD_RUN, CTRLTHREAD_PAUSE, CTRLTHREAD_END }; + +extern class Brainfuck bf; +extern unsigned int timerID; +extern HANDLE hThread; +extern volatile enum ctrlthread_t ctrlThread; + +// Initializes the Brainfuck module. Returns false on an invalid hexadecimal input. +bool bfInit(); + +// Executes the next instruction. +enum Brainfuck::result_t bfNext(); + +// Executes an Brainfuck program until it completes. +tret_t WINAPI threadRunner(void *lpParameter); +} // namespace runner + +#endif diff --git a/ui.cpp b/ui.cpp index 96c8f8a..9f402cc 100644 --- a/ui.cpp +++ b/ui.cpp @@ -1,500 +1,9 @@ -#include -#include - -#ifndef _UNICODE -#define _UNICODE -#endif -#ifndef UNICODE -#define UNICODE -#endif - -// Since they conflict with the C++ STL and some SDKs don't have them, -// disables and defines them manually with different names. -#ifndef NOMINMAX -#define NOMINMAX -#endif -#include -#define mymin(a, b) (((a) < (b)) ? (a) : (b)) -#define mymax(a, b) (((a) > (b)) ? (a) : (b)) - -#ifdef UNDER_CE -#include -#include -#define adjust(coord) (coord) // DPI scaling isn't needed for Windows CE. -#else -#define adjust(coord) ((coord)*dpi / 96) -// Since old SDKs don't have this enum, defines it manually. -typedef enum MONITOR_DPI_TYPE { - MDT_EFFECTIVE_DPI = 0, - MDT_ANGULAR_DPI = 1, - MDT_RAW_DPI = 2, - MDT_DEFAULT = MDT_EFFECTIVE_DPI -} MONITOR_DPI_TYPE; - -// Function pointer type for GetDpiForMonitor API. -typedef HRESULT(CALLBACK *GetDpiForMonitor_t)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, - unsigned *dpiX, unsigned *dpiY); - -// Function pointer type for TaskDialog API. -typedef HRESULT(__stdcall *TaskDialog_t)(HWND hwndOwner, HINSTANCE hInstance, - const wchar_t *pszWindowTitle, - const wchar_t *pszMainInstruction, - const wchar_t *pszContent, int dwCommonButtons, - const wchar_t *pszIcon, int *pnButton); -#endif - -// Makes SetWindowLongW/GetWindowLongW compatible for both 32-bit and 64-bit system. -#ifdef _WIN64 -#define mySetWindowLongW(hWnd, index, data) SetWindowLongPtrW(hWnd, index, (LRESULT)(data)) -#define myGetWindowLongW(hWnd, index) GetWindowLongPtrW(hWnd, index) -#ifndef GWL_WNDPROC -#define GWL_WNDPROC GWLP_WNDPROC -#endif -#ifndef GWL_USERDATA -#define GWL_USERDATA GWLP_USERDATA -#endif -#else -#define mySetWindowLongW(hWnd, index, data) SetWindowLongW(hWnd, index, (LONG)(data)) -#define myGetWindowLongW(hWnd, index) GetWindowLongW(hWnd, index) -#endif - -#include "bf.hpp" -#include "resource.h" #include "ui.hpp" -#include "util.hpp" - -namespace ui { -static const wchar_t *wcCmdBtn[CMDBTN_LEN] = {L"Run", L"Next", L"Pause", L"End"}, - *wcScrKB[SCRKBD_LEN] = {L">", L"<", L"+", L"-", L".", L",", L"[", L"]", L"@"}; -static const int historyMax = 32; -static HWND hEditor, hInput, hOutput, hMemView, hFocused, hCmdBtn[CMDBTN_LEN], hScrKB[SCRKBD_LEN]; -static HMENU hMenu; -static HFONT hBtnFont = NULL, hEditFont = NULL; -static LOGFONTW editFont; -static int topPadding = 0, memViewStart = 0, historyIndex = 0, savedIndex = 0; -static wchar_t *retEditBuf = NULL, *retInBuf = NULL; -static std::wstring wstrFileName; -static bool withBOM = false, wordwrap = true, validHistory = true; -static enum util::newline_t newLine = util::NEWLINE_CRLF; -static std::deque history; -#ifdef UNDER_CE -static HWND hCmdBar; -#else -static HMODULE comctl32 = NULL; -static TaskDialog_t taskDialog = NULL; -static int dpi = 96, sysDPI = 96; -#endif - -enum state_t state = STATE_INIT; -bool signedness = true, wrapInt = true, breakpoint = false, debug = true, dark = true, - horizontal = false; -int speed = 10, outCharSet = IDM_BF_OUTPUT_UTF8, inCharSet = IDM_BF_INPUT_UTF8; -enum Brainfuck::noinput_t noInput = Brainfuck::NOINPUT_ZERO; -HWND hWnd; -HINSTANCE hInst; - -// Enables/Disables menu items from the smaller nearest 10 multiple to `_endID`. -static void enableMenus(unsigned _endID, bool _enable) { - unsigned i; - for (i = (_endID / 10) * 10; i <= _endID; ++i) { - EnableMenuItem(hMenu, i, MF_BYCOMMAND | (_enable ? MF_ENABLED : MF_GRAYED)); - } -} - -// Hook window procedure for the program editor to manage the undo/redo buffer. -static LRESULT CALLBACK editorProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam) { - static WNDPROC prevWndProc = (WNDPROC)myGetWindowLongW(hWnd, GWL_USERDATA); - - switch (uMsg) { - case WM_CHAR: { - if (!validHistory) break; - - int editorSize = GetWindowTextLengthW(hEditor) + 1; - wchar_t *wcEditor = (wchar_t *)malloc(sizeof(wchar_t) * editorSize); - if (!wcEditor) { - messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); - while (!history.empty()) { - free(history.back()); - history.pop_back(); - } - historyIndex = 0; - savedIndex = -1; - validHistory = false; - break; - } - GetWindowTextW(hEditor, wcEditor, editorSize); - - if (!history.empty() && !wcscmp(history[historyIndex], wcEditor)) { - free(wcEditor); - break; - } - - int toRemove = history.size() - historyIndex - 1, i = 0; - for (; i < toRemove; ++i) { - free(history.back()); - history.pop_back(); - } - - if (history.size() >= historyMax) { - free(history.front()); - history.pop_front(); - --historyIndex; - if (savedIndex >= 0) --savedIndex; - } - - history.push_back(wcEditor); - ++historyIndex; - break; - } - - case WM_DESTROY: - mySetWindowLongW(hWnd, GWL_WNDPROC, prevWndProc); - return 0; - } - - return CallWindowProcW(prevWndProc, hWnd, uMsg, wParam, lParam); -} - -void onCreate(HWND _hWnd, HINSTANCE _hInst) { - size_t i; - hWnd = _hWnd; - hInst = _hInst; - -#ifdef UNDER_CE - wchar_t wcMenu[] = L"menu"; // CommandBar_InsertMenubarEx requires non-const value. - InitCommonControls(); - hCmdBar = CommandBar_Create(hInst, hWnd, 1); - CommandBar_InsertMenubarEx(hCmdBar, hInst, wcMenu, 0); - CommandBar_Show(hCmdBar, TRUE); - topPadding = CommandBar_Height(hCmdBar); - hMenu = CommandBar_GetMenu(hCmdBar, 0); -#else - hMenu = LoadMenu(hInst, L"menu"); - SetMenu(hWnd, hMenu); -#endif - - // Program editor - hEditor = - CreateWindowExW(0, L"EDIT", L"", - WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | - WS_VSCROLL | (wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, - 0, 0, 0, 0, hWnd, (HMENU)IDC_EDITOR, hInst, NULL); - SendMessageW(hEditor, EM_SETLIMITTEXT, (WPARAM)-1, 0); - mySetWindowLongW(hEditor, GWL_USERDATA, mySetWindowLongW(hEditor, GWL_WNDPROC, editorProc)); - - // Program input - hInput = - CreateWindowExW(0, L"EDIT", L"", - WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | - WS_VSCROLL | (wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, - 0, 0, 0, 0, hWnd, (HMENU)IDC_INPUT, hInst, NULL); - SendMessageW(hInput, EM_SETLIMITTEXT, (WPARAM)-1, 0); - - // Program output - hOutput = CreateWindowExW(0, L"EDIT", L"", - WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | - ES_READONLY | ES_AUTOVSCROLL | WS_VSCROLL | - (wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, - 0, 0, 0, 0, hWnd, (HMENU)IDC_OUTPUT, hInst, NULL); - SendMessageW(hOutput, EM_SETLIMITTEXT, (WPARAM)-1, 0); - - // Memory view - hMemView = CreateWindowExW(0, L"EDIT", L"", - WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | - ES_READONLY | ES_AUTOVSCROLL | WS_VSCROLL | ES_NOHIDESEL, - 0, 0, 0, 0, hWnd, (HMENU)IDC_MEMVIEW, hInst, NULL); - SendMessageW(hMemView, EM_SETLIMITTEXT, (WPARAM)-1, 0); - - // Command button - for (i = 0; i < CMDBTN_LEN; ++i) { - hCmdBtn[i] = CreateWindowExW( - 0, L"BUTTON", wcCmdBtn[i], - WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | (i == 2 || i == 3 ? WS_DISABLED : 0), 0, 0, 0, 0, - hWnd, (HMENU)(IDC_CMDBTN_FIRST + i), hInst, NULL); - } - - // Screen keyboard - for (i = 0; i < SCRKBD_LEN; ++i) { - hScrKB[i] = CreateWindowExW(0, L"BUTTON", wcScrKB[i], WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 0, - 0, 0, 0, hWnd, (HMENU)(IDC_SCRKBD_FIRST + i), hInst, NULL); - } - - hFocused = hEditor; - - // Default configurations for the editor font. - // We create the actual font object on onSize function. - ZeroMemory(&editFont, sizeof(editFont)); - editFont.lfCharSet = DEFAULT_CHARSET; - editFont.lfQuality = ANTIALIASED_QUALITY; - editFont.lfHeight = -15; // Represents font size 11 in 96 DPI. -#ifdef UNDER_CE - // Sets a pre-installed font on Windows CE, as it doesn't have "MS Shell Dlg". - lstrcpyW(editFont.lfFaceName, L"Tahoma"); -#else - // Sets a logical font face name for localization. - // It maps to a default shell font associated with the current culture/locale. - lstrcpyW(editFont.lfFaceName, L"MS Shell Dlg"); - - // Obtains the "system DPI" value. We use this as the fallback value on older Windows versions - // and to calculate the appropriate font height value for ChooseFontW. - HDC hDC = GetDC(hWnd); - sysDPI = GetDeviceCaps(hDC, LOGPIXELSX); - ReleaseDC(hWnd, hDC); - - // Tries to load the GetDpiForMonitor API. Use of the full path improves security. We avoid a - // direct call to keep this program compatible with Windows 7 and earlier. - // - // Microsoft recommends the use of GetDpiForWindow API instead of this API according to their - // documentation. However, it requires Windows 10 1607 or later, which makes this compatibility - // keeping code more complicated, and GetDpiForMonitor API still works for programs that only use - // the process-wide DPI awareness. Here, as we only use the process-wide DPI awareness, we are - // going to use GetDpiForMonitor API. - // - // References: - // https://learn.microsoft.com/en-us/windows/win32/api/shellscalingapi/nf-shellscalingapi-getdpiformonitor - // https://mariusbancila.ro/blog/2021/05/19/how-to-build-high-dpi-aware-native-desktop-applications/ - GetDpiForMonitor_t getDpiForMonitor = NULL; - HMODULE dll = LoadLibraryW(L"C:\\Windows\\System32\\Shcore.dll"); - if (dll) { - getDpiForMonitor = (GetDpiForMonitor_t)(void *)GetProcAddress(dll, "GetDpiForMonitor"); - } - - // Tests whether it successfully got the GetDpiForMonitor API. - if (getDpiForMonitor) { // It got (the system is presumably Windows 8.1 or later). - unsigned tmpX, tmpY; - getDpiForMonitor(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST), MDT_EFFECTIVE_DPI, &tmpX, - &tmpY); - dpi = tmpX; - } else { // It failed (the system is presumably older than Windows 8.1). - dpi = sysDPI; - } - if (dll) FreeLibrary(dll); - - // Adjusts the windows size according to the DPI value. - SetWindowPos(hWnd, NULL, 0, 0, adjust(480), adjust(320), - SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE); - - // Tries to load the TaskDialog API which is a newer substitite of MessageBoxW. - // This API is per monitor DPI aware but doesn't exist before Windows Vista. - // To make the system select version 6 comctl32.dll, we don't use the full path here. - comctl32 = LoadLibraryW(L"comctl32.dll"); - if (comctl32) { - taskDialog = (TaskDialog_t)(void *)GetProcAddress(comctl32, "TaskDialog"); - } -#endif -} - -void onDestroy() { - DeleteObject(hBtnFont); - DeleteObject(hEditFont); - if (retEditBuf) delete[] retEditBuf; - if (retInBuf) delete[] retInBuf; -#ifndef UNDER_CE - if (comctl32) FreeLibrary(comctl32); -#endif -} - -void onSize() { - RECT rect; - -#ifdef UNDER_CE - // Manually forces the minimum window size as Windows CE doesn't support WM_GETMINMAXINFO. - // Seemingly, Windows CE doesn't re-send WM_SIZE on SetWindowPos calls inside a WM_SIZE handler. - GetWindowRect(hWnd, &rect); - if (rect.right - rect.left < 480 || rect.bottom - rect.top < 320) { - SetWindowPos(hWnd, NULL, 0, 0, mymax(480, rect.right - rect.left), - mymax(320, rect.bottom - rect.top), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE); - } -#endif - - int i; - LOGFONTW rLogfont; - - // Button font - ZeroMemory(&rLogfont, sizeof(rLogfont)); - rLogfont.lfHeight = adjust(-15); - rLogfont.lfWeight = FW_BOLD; - rLogfont.lfCharSet = DEFAULT_CHARSET; - rLogfont.lfQuality = ANTIALIASED_QUALITY; - lstrcpyW(rLogfont.lfFaceName, editFont.lfFaceName); // Syncs with editors. - HFONT newBtnFont = CreateFontIndirectW(&rLogfont); - - // Editor/Output font - memcpy(&rLogfont, &editFont, sizeof(LOGFONTW)); - rLogfont.lfHeight = adjust(editFont.lfHeight); - HFONT newEditFont = CreateFontIndirectW(&rLogfont); - -#ifdef UNDER_CE - // We must "move" the command bar to prevent a glitch. - MoveWindow(hCmdBar, 0, 0, 0, 0, TRUE); -#endif - - // Moves and resizes controls, and applies the newly created fonts for them. - int curX = 0; - for (i = 0; i < CMDBTN_LEN; ++i) { - MoveWindow(hCmdBtn[i], curX, topPadding, adjust(46), adjust(32), TRUE); - SendMessageW(hCmdBtn[i], WM_SETFONT, (WPARAM)newBtnFont, MAKELPARAM(TRUE, 0)); - curX += adjust(46); - } - for (i = 0; i < SCRKBD_LEN; ++i) { - MoveWindow(hScrKB[i], curX, topPadding, adjust(30), adjust(32), TRUE); - SendMessageW(hScrKB[i], WM_SETFONT, (WPARAM)newBtnFont, MAKELPARAM(TRUE, 0)); - curX += adjust(30); - } - - int topEditor = topPadding + adjust(32); - GetClientRect(hWnd, &rect); - int scrX = rect.right, scrY = rect.bottom; - int halfY = (scrY - topEditor) / 2, centerY = (scrY + topEditor) / 2; - if (horizontal) { - MoveWindow(hEditor, 0, topEditor, scrX / 3, scrY - topEditor, TRUE); - MoveWindow(hInput, scrX / 3, topEditor, scrX / 3, halfY, TRUE); - MoveWindow(hOutput, scrX / 3, halfY + topEditor, scrX / 3, scrY - halfY - topEditor, TRUE); - MoveWindow(hMemView, scrX * 2 / 3, topEditor, scrX - scrX * 2 / 3, scrY - topEditor, TRUE); - } else { - MoveWindow(hEditor, 0, topEditor, scrX, halfY, TRUE); - MoveWindow(hInput, 0, centerY, scrX / 2, halfY / 2, TRUE); - MoveWindow(hOutput, scrX / 2, centerY, scrX - scrX / 2, halfY / 2, TRUE); - MoveWindow(hMemView, 0, centerY + halfY / 2, scrX, scrY - centerY - halfY / 2, TRUE); - } - SendMessageW(hEditor, WM_SETFONT, (WPARAM)newEditFont, MAKELPARAM(TRUE, 0)); - SendMessageW(hInput, WM_SETFONT, (WPARAM)newEditFont, MAKELPARAM(TRUE, 0)); - SendMessageW(hOutput, WM_SETFONT, (WPARAM)newEditFont, MAKELPARAM(TRUE, 0)); - SendMessageW(hMemView, WM_SETFONT, (WPARAM)newEditFont, MAKELPARAM(TRUE, 0)); - - if (hBtnFont) DeleteObject(hBtnFont); - if (hEditFont) DeleteObject(hEditFont); - hBtnFont = newBtnFont; - hEditFont = newEditFont; - InvalidateRect(hWnd, NULL, FALSE); -} -void onInitMenuPopup() { - // Brainfuck -> Memory Type - CheckMenuRadioItem(hMenu, IDM_BF_MEMTYPE_SIGNED, IDM_BF_MEMTYPE_UNSIGNED, - signedness ? IDM_BF_MEMTYPE_SIGNED : IDM_BF_MEMTYPE_UNSIGNED, MF_BYCOMMAND); - - // Brainfuck -> Output Charset - CheckMenuRadioItem(hMenu, IDM_BF_OUTPUT_UTF8, IDM_BF_OUTPUT_HEX, outCharSet, MF_BYCOMMAND); - - // Brainfuck -> Input Charset - CheckMenuRadioItem(hMenu, IDM_BF_INPUT_UTF8, IDM_BF_INPUT_HEX, inCharSet, MF_BYCOMMAND); - - // Brainfuck -> Input Instruction - CheckMenuRadioItem(hMenu, IDM_BF_NOINPUT_ERROR, IDM_BF_NOINPUT_FF, IDM_BF_NOINPUT_ERROR + noInput, - MF_BYCOMMAND); - - // Brainfuck -> Integer Overflow - CheckMenuRadioItem(hMenu, IDM_BF_INTOVF_ERROR, IDM_BF_INTOVF_WRAPAROUND, - wrapInt ? IDM_BF_INTOVF_WRAPAROUND : IDM_BF_INTOVF_ERROR, MF_BYCOMMAND); - - // Brainfuck -> Breakpoint - CheckMenuItem(hMenu, IDM_BF_BREAKPOINT, MF_BYCOMMAND | breakpoint ? MF_CHECKED : MF_UNCHECKED); - - // Options -> Speed - if (speed == 0) { - CheckMenuRadioItem(hMenu, IDM_OPT_SPEED_FASTEST, IDM_OPT_SPEED_100MS, IDM_OPT_SPEED_FASTEST, - MF_BYCOMMAND); - } else if (speed == 1) { - CheckMenuRadioItem(hMenu, IDM_OPT_SPEED_FASTEST, IDM_OPT_SPEED_100MS, IDM_OPT_SPEED_1MS, - MF_BYCOMMAND); - } else if (speed == 10) { - CheckMenuRadioItem(hMenu, IDM_OPT_SPEED_FASTEST, IDM_OPT_SPEED_100MS, IDM_OPT_SPEED_10MS, - MF_BYCOMMAND); - } else if (speed == 100) { - CheckMenuRadioItem(hMenu, IDM_OPT_SPEED_FASTEST, IDM_OPT_SPEED_100MS, IDM_OPT_SPEED_100MS, - MF_BYCOMMAND); - } - - // Options -> Debug - CheckMenuItem(hMenu, IDM_OPT_TRACK, MF_BYCOMMAND | debug ? MF_CHECKED : MF_UNCHECKED); - - // Options -> Word Wrap - CheckMenuItem(hMenu, IDM_OPT_WORDWRAP, MF_BYCOMMAND | wordwrap ? MF_CHECKED : MF_UNCHECKED); - - if (state == STATE_INIT) { - enableMenus(IDM_FILE_NEW, true); - enableMenus(IDM_FILE_OPEN, true); - enableMenus(IDM_EDIT_UNDO, validHistory && historyIndex > 0); - enableMenus(IDM_EDIT_REDO, validHistory && historyIndex < (int)history.size() - 1); - enableMenus(IDM_BF_MEMTYPE_UNSIGNED, true); - enableMenus(IDM_BF_OUTPUT_HEX, true); - enableMenus(IDM_BF_INPUT_HEX, true); - enableMenus(IDM_BF_NOINPUT_FF, true); - enableMenus(IDM_BF_INTOVF_WRAPAROUND, true); - enableMenus(IDM_BF_BREAKPOINT, true); - enableMenus(IDM_OPT_SPEED_100MS, true); - enableMenus(IDM_OPT_MEMVIEW, true); - enableMenus(IDM_OPT_TRACK, true); - enableMenus(IDM_OPT_HLTPROG, false); - enableMenus(IDM_OPT_HLTMEM, false); - } else if (state == STATE_RUN) { - enableMenus(IDM_FILE_NEW, false); - enableMenus(IDM_FILE_OPEN, false); - enableMenus(IDM_EDIT_UNDO, false); - enableMenus(IDM_EDIT_REDO, false); - enableMenus(IDM_BF_MEMTYPE_UNSIGNED, false); - enableMenus(IDM_BF_OUTPUT_HEX, false); - enableMenus(IDM_BF_INPUT_HEX, false); - enableMenus(IDM_BF_NOINPUT_FF, false); - enableMenus(IDM_BF_INTOVF_WRAPAROUND, false); - enableMenus(IDM_BF_BREAKPOINT, false); - enableMenus(IDM_OPT_SPEED_100MS, false); - enableMenus(IDM_OPT_MEMVIEW, false); - enableMenus(IDM_OPT_TRACK, false); - enableMenus(IDM_OPT_HLTPROG, false); - enableMenus(IDM_OPT_HLTMEM, false); - } else if (state == STATE_PAUSE || state == STATE_FINISH) { - enableMenus(IDM_FILE_NEW, false); - enableMenus(IDM_FILE_OPEN, false); - enableMenus(IDM_EDIT_UNDO, false); - enableMenus(IDM_EDIT_REDO, false); - enableMenus(IDM_BF_MEMTYPE_UNSIGNED, false); - enableMenus(IDM_BF_OUTPUT_HEX, false); - enableMenus(IDM_BF_INPUT_HEX, false); - enableMenus(IDM_BF_NOINPUT_FF, false); - enableMenus(IDM_BF_INTOVF_WRAPAROUND, false); - enableMenus(IDM_BF_BREAKPOINT, true); - enableMenus(IDM_OPT_SPEED_100MS, true); - enableMenus(IDM_OPT_MEMVIEW, true); - enableMenus(IDM_OPT_TRACK, true); - enableMenus(IDM_OPT_HLTPROG, true); - enableMenus(IDM_OPT_HLTMEM, true); - } -} - -#ifndef UNDER_CE -void onDropFiles(HDROP hDrop) { - wchar_t wcFileName[MAX_PATH]; - DragQueryFileW(hDrop, 0, wcFileName, MAX_PATH); - DragFinish(hDrop); - openFile(false, wcFileName); -} - -void onGetMinMaxInfo(MINMAXINFO *_minMaxInfo) { - _minMaxInfo->ptMinTrackSize.x = adjust(480); - _minMaxInfo->ptMinTrackSize.y = adjust(320); -} - -void onDPIChanged(int _dpi, const RECT *_rect) { - dpi = _dpi; - MoveWindow(hWnd, _rect->left, _rect->top, _rect->right - _rect->left, _rect->bottom - _rect->top, - FALSE); -} -#endif - -void onScreenKeyboard(int _key) { - if (hFocused == hEditor) SendMessageW(hEditor, EM_REPLACESEL, 0, (WPARAM)wcScrKB[_key]); -} - -void cut() { SendMessageW(hFocused, WM_CUT, 0, 0); } - -void copy() { SendMessageW(hFocused, WM_COPY, 0, 0); } - -void paste() { SendMessageW(hFocused, WM_PASTE, 0, 0); } - -void selAll() { SendMessageW(hFocused, EM_SETSEL, 0, -1); } +#include "msg.hpp" +#include "wproc.hpp" +namespace ui { void undo() { // TODO } @@ -503,368 +12,184 @@ void redo() { // TODO } -int messageBox(HWND _hWnd, const wchar_t *_lpText, const wchar_t *_lpCaption, unsigned _uType) { -#ifndef UNDER_CE - if (taskDialog) { - // Tests whether _uType uses some features that TaskDialog doesn't support. - if (_uType & ~(MB_ICONMASK | MB_TYPEMASK)) goto mbfallback; - - int buttons; - switch (_uType & MB_TYPEMASK) { - case MB_OK: - buttons = 1; - break; - case MB_OKCANCEL: - buttons = 1 + 8; - break; - case MB_RETRYCANCEL: - buttons = 16 + 8; - break; - case MB_YESNO: - buttons = 2 + 4; - break; - case MB_YESNOCANCEL: - buttons = 2 + 4 + 8; - break; - default: // Not supported by TaskDialog. - goto mbfallback; - } - - wchar_t *icon; - switch (_uType & MB_ICONMASK) { - case 0: - icon = NULL; - break; - case MB_ICONWARNING: // Same value as MB_ICONEXCLAMATION. - icon = MAKEINTRESOURCEW(-1); - break; - case MB_ICONERROR: // Same value as MB_ICONSTOP and MB_ICONHAND. - icon = MAKEINTRESOURCEW(-2); - break; - default: // Fallbacks everything else for Information icon. - icon = MAKEINTRESOURCEW(-3); - } - - int result; - taskDialog(_hWnd, hInst, _lpCaption, L"", _lpText, buttons, icon, &result); - return result; - } -mbfallback: -#endif - return MessageBoxW(_hWnd, _lpText, _lpCaption, _uType); -} - -// Hook window procedure for the edit control in the memory view options dialog. -// This procedure translates top row character keys to numbers according to the keyboard layout of -// SHARP Brain. -static LRESULT CALLBACK memViewDlgEditor(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam) { - static WNDPROC prevWndProc = (WNDPROC)myGetWindowLongW(hWnd, GWL_USERDATA); - - switch (uMsg) { - case WM_CHAR: - switch ((wchar_t)wParam) { - case L'q': - SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"1"); - return 0; - - case L'w': - SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"2"); - return 0; - - case L'e': - SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"3"); - return 0; - - case L'r': - SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"4"); - return 0; - - case L't': - SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"5"); - return 0; - - case L'y': - SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"6"); - return 0; - - case L'u': - SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"7"); - return 0; - - case L'i': - SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"8"); - return 0; - - case L'o': - SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"9"); - return 0; - - case L'p': - SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"0"); - return 0; - } - break; - - case WM_DESTROY: - mySetWindowLongW(hWnd, GWL_WNDPROC, prevWndProc); - return 0; - } - - return CallWindowProcW(prevWndProc, hWnd, uMsg, wParam, lParam); -} - -// Window procedure for the memory view options dialog. -INT_PTR CALLBACK memViewProc(HWND hDlg, unsigned uMsg, WPARAM wParam, LPARAM lParam) { - UNREFERENCED_PARAMETER(lParam); - - switch (uMsg) { - case WM_INITDIALOG: { - // Puts the dialog at the center of the parent window. - RECT wndSize, wndRect, dlgRect; - GetWindowRect(hDlg, &dlgRect); - GetWindowRect(hWnd, &wndRect); - GetClientRect(hWnd, &wndSize); - int newPosX = wndRect.left + (wndSize.right - (dlgRect.right - dlgRect.left)) / 2, - newPosY = wndRect.top + (wndSize.bottom - (dlgRect.bottom - dlgRect.top)) / 2; - if (newPosX < 0) newPosX = 0; - if (newPosY < 0) newPosY = 0; - SetWindowPos(hDlg, NULL, newPosX, newPosY, 0, 0, SWP_NOZORDER | SWP_NOSIZE); - - wchar_t editBuf[10]; - HWND hEdit = GetDlgItem(hDlg, 3); - wsprintfW(editBuf, L"%u", memViewStart <= 999999999 ? memViewStart : 999999999); - SendDlgItemMessageW(hDlg, 3, EM_SETLIMITTEXT, 9, 0); - SetDlgItemTextW(hDlg, 3, editBuf); - mySetWindowLongW(hEdit, GWL_USERDATA, mySetWindowLongW(hEdit, GWL_WNDPROC, memViewDlgEditor)); - return TRUE; - } - - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDOK: { - wchar_t editBuf[10]; - long temp; - GetDlgItemTextW(hDlg, 3, (wchar_t *)editBuf, 10); - temp = wcstol(editBuf, NULL, 10); - if (temp < 0) { - messageBox(hDlg, L"Invalid input.", L"Error", MB_ICONWARNING); - } else { - memViewStart = temp; - EndDialog(hDlg, IDOK); - } - return TRUE; - } - - case IDCANCEL: - EndDialog(hDlg, IDCANCEL); - return TRUE; - } - return FALSE; +void enableMenus(unsigned endID, bool enable) { + unsigned i; + for (i = (endID / 10) * 10; i <= endID; ++i) { + EnableMenuItem(global::hMenu, i, MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED)); } - return FALSE; } -void setState(enum state_t _state, bool _force) { +void setState(enum global::state_t state, bool force) { int i; - if (!_force && _state == state) return; - - if (_state == STATE_INIT) { - EnableWindow(hCmdBtn[0], TRUE); // run button - EnableWindow(hCmdBtn[1], TRUE); // next button - EnableWindow(hCmdBtn[2], FALSE); // pause button - EnableWindow(hCmdBtn[3], FALSE); // end button - SendMessageW(hEditor, EM_SETREADONLY, (WPARAM)FALSE, (LPARAM)NULL); - SendMessageW(hInput, EM_SETREADONLY, (WPARAM)FALSE, (LPARAM)NULL); - SendMessageW(hOutput, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); + if (!force && state == global::state) return; + + if (state == global::STATE_INIT) { + EnableWindow(global::hCmdBtn[0], TRUE); // run button + EnableWindow(global::hCmdBtn[1], TRUE); // next button + EnableWindow(global::hCmdBtn[2], FALSE); // pause button + EnableWindow(global::hCmdBtn[3], FALSE); // end button + SendMessageW(global::hEditor, EM_SETREADONLY, (WPARAM)FALSE, (LPARAM)NULL); + SendMessageW(global::hInput, EM_SETREADONLY, (WPARAM)FALSE, (LPARAM)NULL); + SendMessageW(global::hOutput, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); for (i = 0; i < SCRKBD_LEN; ++i) { - EnableWindow(hScrKB[i], TRUE); + EnableWindow(global::hScrKB[i], TRUE); } - } else if (_state == STATE_RUN) { - EnableWindow(hCmdBtn[0], FALSE); // run button - EnableWindow(hCmdBtn[1], FALSE); // next button - EnableWindow(hCmdBtn[2], TRUE); // pause button - EnableWindow(hCmdBtn[3], TRUE); // end button - SendMessageW(hEditor, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); - SendMessageW(hInput, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); - SendMessageW(hOutput, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); +#ifndef UNDER_CE + DragAcceptFiles(global::hWnd, TRUE); +#endif + } else if (state == global::STATE_RUN) { + EnableWindow(global::hCmdBtn[0], FALSE); // run button + EnableWindow(global::hCmdBtn[1], FALSE); // next button + EnableWindow(global::hCmdBtn[2], TRUE); // pause button + EnableWindow(global::hCmdBtn[3], TRUE); // end button + SendMessageW(global::hEditor, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); + SendMessageW(global::hInput, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); + SendMessageW(global::hOutput, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); for (i = 0; i < SCRKBD_LEN; ++i) { - EnableWindow(hScrKB[i], FALSE); + EnableWindow(global::hScrKB[i], FALSE); } - } else if (_state == STATE_PAUSE) { - EnableWindow(hCmdBtn[0], TRUE); // run button - EnableWindow(hCmdBtn[1], TRUE); // next button - EnableWindow(hCmdBtn[2], FALSE); // pause button - EnableWindow(hCmdBtn[3], TRUE); // end button - SendMessageW(hEditor, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); - SendMessageW(hInput, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); - SendMessageW(hOutput, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); +#ifndef UNDER_CE + DragAcceptFiles(global::hWnd, FALSE); +#endif + } else if (state == global::STATE_PAUSE) { + EnableWindow(global::hCmdBtn[0], TRUE); // run button + EnableWindow(global::hCmdBtn[1], TRUE); // next button + EnableWindow(global::hCmdBtn[2], FALSE); // pause button + EnableWindow(global::hCmdBtn[3], TRUE); // end button + SendMessageW(global::hEditor, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); + SendMessageW(global::hInput, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); + SendMessageW(global::hOutput, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); for (i = 0; i < SCRKBD_LEN; ++i) { - EnableWindow(hScrKB[i], FALSE); + EnableWindow(global::hScrKB[i], FALSE); } - } else if (_state == STATE_FINISH) { - EnableWindow(hCmdBtn[0], FALSE); // run button - EnableWindow(hCmdBtn[1], FALSE); // next button - EnableWindow(hCmdBtn[2], FALSE); // pause button - EnableWindow(hCmdBtn[3], TRUE); // end button - SendMessageW(hEditor, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); - SendMessageW(hInput, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); - SendMessageW(hOutput, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); +#ifndef UNDER_CE + DragAcceptFiles(global::hWnd, FALSE); +#endif + } else if (state == global::STATE_FINISH) { + EnableWindow(global::hCmdBtn[0], FALSE); // run button + EnableWindow(global::hCmdBtn[1], FALSE); // next button + EnableWindow(global::hCmdBtn[2], FALSE); // pause button + EnableWindow(global::hCmdBtn[3], TRUE); // end button + SendMessageW(global::hEditor, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); + SendMessageW(global::hInput, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); + SendMessageW(global::hOutput, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); for (i = 0; i < SCRKBD_LEN; ++i) { - EnableWindow(hScrKB[i], FALSE); + EnableWindow(global::hScrKB[i], FALSE); } +#ifndef UNDER_CE + DragAcceptFiles(global::hWnd, FALSE); +#endif } - state = _state; - InvalidateRect(hWnd, NULL, FALSE); + global::state = state; + InvalidateRect(global::hWnd, NULL, FALSE); } -void updateFocus(int _id) { - switch (_id) { - case IDC_EDITOR: - hFocused = hEditor; - break; - - case IDC_INPUT: - hFocused = hInput; - break; - - case IDC_OUTPUT: - hFocused = hOutput; - break; - - case IDC_MEMVIEW: - hFocused = hMemView; - break; - - default: - SetFocus(hFocused); +void updateFocus(HWND hWndCtl) { + if (hWndCtl == global::hEditor || hWndCtl == global::hInput || hWndCtl == global::hOutput || + hWndCtl == global::hMemView) { + global::hFocused = hWndCtl; + } else { + SetFocus(global::hFocused); } } -void selProg(unsigned _progPtr) { SendMessageW(hEditor, EM_SETSEL, _progPtr, _progPtr + 1); } +void selProg(unsigned progPtr) { SendMessageW(global::hEditor, EM_SETSEL, progPtr, progPtr + 1); } -void selMemView(unsigned _memPtr) { - SendMessageW(hMemView, EM_SETSEL, (_memPtr - memViewStart) * 3, (_memPtr - memViewStart) * 3 + 2); +void selMemView(unsigned memPtr) { + SendMessageW(global::hMemView, EM_SETSEL, (memPtr - global::memViewStart) * 3, + (memPtr - global::memViewStart) * 3 + 2); } void setMemory(const unsigned char *memory, int size) { - if (!memory) { - SetWindowTextW(hMemView, NULL); - return; - } - int i; std::wstring wstrOut; - for (i = memViewStart; i < memViewStart + 100 && i < size; ++i) { + for (i = global::memViewStart; i < global::memViewStart + 100 && i < size; ++i) { wchar_t wcOut[4]; util::toHex(memory[i], wcOut); wstrOut.append(wcOut); } - SetWindowTextW(hMemView, wstrOut.c_str()); + SetWindowTextW(global::hMemView, wstrOut.c_str()); } -wchar_t *getEditor() { - if (retEditBuf) free(retEditBuf); - - int editorSize = GetWindowTextLengthW(hEditor) + 1; - if ((retEditBuf = (wchar_t *)malloc(sizeof(wchar_t) * editorSize))) { - GetWindowTextW(hEditor, retEditBuf, editorSize); - } else { - messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); - } - - return retEditBuf; -} - -wchar_t *getInput() { - if (retInBuf) free(retInBuf); - - int inputSize = GetWindowTextLengthW(hInput) + 1; - if ((retInBuf = (wchar_t *)malloc(sizeof(wchar_t) * inputSize))) { - GetWindowTextW(hInput, retInBuf, inputSize); - } else { - messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); - } - - return retInBuf; -} - -void setOutput(const wchar_t *_str) { SetWindowTextW(hOutput, _str); } - -void appendOutput(const wchar_t *_str) { - int editLen = (int)SendMessageW(hOutput, WM_GETTEXTLENGTH, 0, 0); - SendMessageW(hOutput, EM_SETSEL, editLen, editLen); - SendMessageW(hOutput, EM_REPLACESEL, 0, (WPARAM)_str); +void appendOutput(const wchar_t *str) { + int editLen = (int)SendMessageW(global::hOutput, WM_GETTEXTLENGTH, 0, 0); + SendMessageW(global::hOutput, EM_SETSEL, editLen, editLen); + SendMessageW(global::hOutput, EM_REPLACESEL, 0, (WPARAM)str); } void switchWordwrap() { - int editorSize = GetWindowTextLengthW(hEditor) + 1; + int editorSize = GetWindowTextLengthW(global::hEditor) + 1; wchar_t *wcEditor = (wchar_t *)malloc(sizeof(wchar_t) * editorSize); if (!wcEditor) { - messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); + util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", + MB_ICONWARNING); return; } - GetWindowTextW(hEditor, wcEditor, editorSize); - bool isModified = SendMessageW(hEditor, EM_GETMODIFY, 0, 0) != 0; - DestroyWindow(hEditor); + GetWindowTextW(global::hEditor, wcEditor, editorSize); + bool isModified = SendMessageW(global::hEditor, EM_GETMODIFY, 0, 0) != 0; + DestroyWindow(global::hEditor); - int inputSize = GetWindowTextLengthW(hInput) + 1; + int inputSize = GetWindowTextLengthW(global::hInput) + 1; wchar_t *wcInput = (wchar_t *)malloc(sizeof(wchar_t) * inputSize); if (!wcInput) { free(wcEditor); - messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); + util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", + MB_ICONWARNING); return; } - GetWindowTextW(hInput, wcInput, inputSize); - DestroyWindow(hInput); + GetWindowTextW(global::hInput, wcInput, inputSize); + DestroyWindow(global::hInput); - int outputSize = GetWindowTextLengthW(hOutput) + 1; + int outputSize = GetWindowTextLengthW(global::hOutput) + 1; wchar_t *wcOutput = (wchar_t *)malloc(sizeof(wchar_t) * outputSize); if (!wcOutput) { free(wcEditor); free(wcInput); - messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); + util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", + MB_ICONWARNING); return; } - GetWindowTextW(hOutput, wcOutput, outputSize); - DestroyWindow(hOutput); + GetWindowTextW(global::hOutput, wcOutput, outputSize); + DestroyWindow(global::hOutput); - wordwrap = !wordwrap; + global::wordwrap = !global::wordwrap; // Program editor - hEditor = - CreateWindowExW(0, L"EDIT", L"", - WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | - WS_VSCROLL | (wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, - 0, 0, 0, 0, hWnd, (HMENU)IDC_EDITOR, hInst, NULL); - SendMessageW(hInput, EM_SETLIMITTEXT, (WPARAM)-1, 0); - mySetWindowLongW(hEditor, GWL_USERDATA, mySetWindowLongW(hEditor, GWL_WNDPROC, editorProc)); + global::hEditor = CreateWindowExW( + 0, L"EDIT", L"", + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL | + (global::wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, + 0, 0, 0, 0, global::hWnd, (HMENU)IDC_EDITOR, global::hInst, NULL); + SendMessageW(global::hInput, EM_SETLIMITTEXT, (WPARAM)-1, 0); + mySetWindowLongW(global::hEditor, GWL_USERDATA, + mySetWindowLongW(global::hEditor, GWL_WNDPROC, wproc::editorProc)); // Program input - hInput = - CreateWindowExW(0, L"EDIT", L"", - WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | - WS_VSCROLL | (wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, - 0, 0, 0, 0, hWnd, (HMENU)IDC_INPUT, hInst, NULL); - SendMessageW(hInput, EM_SETLIMITTEXT, (WPARAM)-1, 0); + global::hInput = CreateWindowExW( + 0, L"EDIT", L"", + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL | + (global::wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, + 0, 0, 0, 0, global::hWnd, (HMENU)IDC_INPUT, global::hInst, NULL); + SendMessageW(global::hInput, EM_SETLIMITTEXT, (WPARAM)-1, 0); // Program output - hOutput = CreateWindowExW(0, L"EDIT", L"", - WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | - ES_READONLY | ES_AUTOVSCROLL | WS_VSCROLL | - (wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, - 0, 0, 0, 0, hWnd, (HMENU)IDC_OUTPUT, hInst, NULL); - SendMessageW(hOutput, EM_SETLIMITTEXT, (WPARAM)-1, 0); - - hFocused = hEditor; - updateFocus(); - onSize(); - setState(state, true); - - SetWindowTextW(hEditor, wcEditor); - SendMessageW(hEditor, EM_SETMODIFY, isModified, 0); - SetWindowTextW(hInput, wcInput); - SetWindowTextW(hOutput, wcOutput); + global::hOutput = CreateWindowExW( + 0, L"EDIT", L"", + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL | + WS_VSCROLL | (global::wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, + 0, 0, 0, 0, global::hWnd, (HMENU)IDC_OUTPUT, global::hInst, NULL); + SendMessageW(global::hOutput, EM_SETLIMITTEXT, (WPARAM)-1, 0); + + global::hFocused = global::hEditor; + ui::updateFocus(); + msg::onSize(global::hWnd); + ui::setState(global::state, true); + + SetWindowTextW(global::hEditor, wcEditor); + SendMessageW(global::hEditor, EM_SETMODIFY, isModified, 0); + SetWindowTextW(global::hInput, wcInput); + SetWindowTextW(global::hOutput, wcOutput); free(wcEditor); free(wcInput); @@ -872,64 +197,66 @@ void switchWordwrap() { } void switchTheme() { - dark = !dark; + global::dark = !global::dark; - InvalidateRect(hEditor, NULL, TRUE); - InvalidateRect(hInput, NULL, TRUE); - InvalidateRect(hOutput, NULL, TRUE); - InvalidateRect(hMemView, NULL, TRUE); + InvalidateRect(global::hEditor, NULL, TRUE); + InvalidateRect(global::hInput, NULL, TRUE); + InvalidateRect(global::hOutput, NULL, TRUE); + InvalidateRect(global::hMemView, NULL, TRUE); } void switchLayout() { - horizontal = !horizontal; + global::horizontal = !global::horizontal; - onSize(); + msg::onSize(global::hWnd); } void chooseFont() { #ifndef UNDER_CE - LONG origHeight = editFont.lfHeight; + LONG origHeight = global::editFont.lfHeight; // Temporarilly sets to the "System DPI scaled" value since ChooseFontW expects it. // Subtracting by 96 - 1 makes this division to behave like a ceiling function. // Note that editFont.lfHeight is negative. - editFont.lfHeight = (editFont.lfHeight * sysDPI - (96 - 1)) / 96; + global::editFont.lfHeight = (global::editFont.lfHeight * global::sysDPI - (96 - 1)) / 96; #endif CHOOSEFONTW cf; cf.lStructSize = sizeof(CHOOSEFONTW); - cf.hwndOwner = hWnd; - cf.lpLogFont = &editFont; - cf.hDC = GetDC(hWnd); // Required for CF_BOTH. + cf.hwndOwner = global::hWnd; + cf.lpLogFont = &global::editFont; + cf.hDC = GetDC(global::hWnd); // Required for CF_BOTH. // We won't get any fonts if we omit CF_BOTH on Windows CE. cf.Flags = CF_INITTOLOGFONTSTRUCT | CF_FORCEFONTEXIST | CF_BOTH; BOOL ret = ChooseFontW(&cf); - ReleaseDC(hWnd, cf.hDC); + ReleaseDC(global::hWnd, cf.hDC); #ifdef UNDER_CE if (ret) { - editFont.lfQuality = ANTIALIASED_QUALITY; - onSize(); + global::editFont.lfQuality = ANTIALIASED_QUALITY; + msg::onSize(global::hWnd); } #else if (ret) { // Re-converts to the 96 DPI value as we properly adjust it on the fly. - editFont.lfHeight = (editFont.lfHeight * 96 - (sysDPI - 1)) / sysDPI; - editFont.lfQuality = ANTIALIASED_QUALITY; - onSize(); + global::editFont.lfHeight = + (global::editFont.lfHeight * 96 - (global::sysDPI - 1)) / global::sysDPI; + global::editFont.lfQuality = ANTIALIASED_QUALITY; + msg::onSize(global::hWnd); } else { // Rewrites the original value instead of re-converting. // Re-converting can results in a different value. - editFont.lfHeight = origHeight; + global::editFont.lfHeight = origHeight; } #endif } bool promptSave() { - if (validHistory && historyIndex == savedIndex) return true; - if (!validHistory && SendMessageW(hEditor, EM_GETMODIFY, 0, 0) == 0) return true; + if (global::validHistory && global::historyIndex == global::savedIndex) return true; + if (!global::validHistory && SendMessageW(global::hEditor, EM_GETMODIFY, 0, 0) == 0) return true; - int ret = messageBox(hWnd, L"Unsaved data will be lost. Save changes?", L"Confirm", - MB_ICONWARNING | MB_YESNOCANCEL); + int ret = + util::messageBox(global::hWnd, global::hInst, L"Unsaved data will be lost. Save changes?", + L"Confirm", MB_ICONWARNING | MB_YESNOCANCEL); if (ret == IDCANCEL) { return false; @@ -947,46 +274,47 @@ bool promptSave() { return true; } -void openFile(bool _newFile, const wchar_t *_fileName) { +void openFile(bool newFile, const wchar_t *fileName) { wchar_t wcFileName[MAX_PATH] = {0}; if (!promptSave()) return; - if (_newFile) { // new - SetWindowTextW(hEditor, L""); - SetWindowTextW(hWnd, APP_NAME); - SendMessageW(hEditor, EM_SETMODIFY, FALSE, 0); - wstrFileName = L""; - withBOM = false; - newLine = util::NEWLINE_CRLF; - - while (!history.empty()) { - free(history.back()); - history.pop_back(); + if (newFile) { // new + SetWindowTextW(global::hEditor, L""); + SetWindowTextW(global::hWnd, APP_NAME); + SendMessageW(global::hEditor, EM_SETMODIFY, FALSE, 0); + global::wstrFileName = L""; + global::withBOM = false; + global::newLine = util::NEWLINE_CRLF; + + while (!global::history.empty()) { + free(global::history.back()); + global::history.pop_back(); } wchar_t *wcEditor = (wchar_t *)calloc(1, sizeof(wchar_t)); if (!wcEditor) { - messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); - historyIndex = 0; - savedIndex = -1; - validHistory = false; + util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", + MB_ICONWARNING); + global::historyIndex = 0; + global::savedIndex = -1; + global::validHistory = false; return; } - history.push_back(wcEditor); - historyIndex = savedIndex = 0; - validHistory = true; + global::history.push_back(wcEditor); + global::historyIndex = global::savedIndex = 0; + global::validHistory = true; return; } - if (!_fileName) { + if (!fileName) { wchar_t initDir[MAX_PATH] = {0}; - if (!wstrFileName.empty()) { - wstrFileName.substr(0, wstrFileName.rfind(L'\\')).copy(initDir, MAX_PATH - 1); + if (!global::wstrFileName.empty()) { + global::wstrFileName.substr(0, global::wstrFileName.rfind(L'\\')).copy(initDir, MAX_PATH - 1); } OPENFILENAMEW ofn; ZeroMemory(&ofn, sizeof(OPENFILENAMEW)); ofn.lStructSize = sizeof(OPENFILENAMEW); - ofn.hwndOwner = hWnd; + ofn.hwndOwner = global::hWnd; ofn.lpstrFilter = L"Brainfuck source (*.bf;*.b;*.txt)\0*.bf;*.b;*.txt\0All files (*.*)\0*.*\0"; ofn.lpstrFile = wcFileName; ofn.nMaxFile = MAX_PATH; @@ -994,25 +322,27 @@ void openFile(bool _newFile, const wchar_t *_fileName) { ofn.lpstrInitialDir = initDir[0] ? initDir : NULL; ofn.Flags = OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_FILEMUSTEXIST; if (!GetOpenFileNameW(&ofn)) return; - _fileName = wcFileName; + fileName = wcFileName; } - HANDLE hFile = CreateFileW(_fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, + HANDLE hFile = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { - messageBox(hWnd, L"Open failed.", L"Error", MB_ICONWARNING); + util::messageBox(global::hWnd, global::hInst, L"Open failed.", L"Error", MB_ICONWARNING); return; } DWORD fileSize = GetFileSize(hFile, NULL), readLen; if (fileSize >= 65536 * 2) { - messageBox(hWnd, L"This file is too large.", L"Error", MB_ICONWARNING); + util::messageBox(global::hWnd, global::hInst, L"This file is too large.", L"Error", + MB_ICONWARNING); return; } char *fileBuf = (char *)malloc(sizeof(char) * fileSize); if (!fileBuf) { - messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); + util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", + MB_ICONWARNING); return; } @@ -1026,7 +356,8 @@ void openFile(bool _newFile, const wchar_t *_fileName) { wchar_t *wcFileBuf = (wchar_t *)calloc(length + 1, sizeof(wchar_t)); if (!wcFileBuf) { free(fileBuf); - messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); + util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", + MB_ICONWARNING); return; } @@ -1035,47 +366,54 @@ void openFile(bool _newFile, const wchar_t *_fileName) { std::wstring converted = wcFileBuf; free(wcFileBuf); - newLine = util::convertCRLF(converted, util::NEWLINE_CRLF); + global::newLine = util::convertCRLF(converted, util::NEWLINE_CRLF); - SetWindowTextW(hEditor, converted.c_str()); - SendMessageW(hEditor, EM_SETMODIFY, FALSE, 0); - wstrFileName = _fileName; - withBOM = padding != 0; + SetWindowTextW(global::hEditor, converted.c_str()); + SendMessageW(global::hEditor, EM_SETMODIFY, FALSE, 0); + global::wstrFileName = fileName; + global::withBOM = padding != 0; - while (!history.empty()) { - free(history.back()); - history.pop_back(); + while (!global::history.empty()) { + free(global::history.back()); + global::history.pop_back(); } - wchar_t *wcEditor = _wcsdup(converted.c_str()); + wchar_t *wcEditor = (wchar_t *)calloc(converted.size() + 1, sizeof(wchar_t)); if (!wcEditor) { - messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); - historyIndex = 0; - savedIndex = -1; - validHistory = false; + util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", + MB_ICONWARNING); + global::historyIndex = 0; + global::savedIndex = -1; + global::validHistory = false; return; } - history.push_back(wcEditor); - historyIndex = savedIndex = 0; - validHistory = true; + wcscpy(wcEditor, converted.c_str()); + global::history.push_back(wcEditor); + global::historyIndex = global::savedIndex = 0; + global::validHistory = true; + + SetWindowTextW(global::hOutput, NULL); + SetWindowTextW(global::hMemView, NULL); std::wstring title = L"["; - title.append(wstrFileName.substr(wstrFileName.rfind(L'\\') + 1) + L"] - " APP_NAME); - SetWindowTextW(hWnd, title.c_str()); + title.append(global::wstrFileName.substr(global::wstrFileName.rfind(L'\\') + 1) + + L"] - " APP_NAME); + SetWindowTextW(global::hWnd, title.c_str()); } -bool saveFile(bool _isOverwrite) { +bool saveFile(bool isOverwrite) { wchar_t wcFileName[MAX_PATH] = {0}; - if (!_isOverwrite || wstrFileName.empty()) { + if (!isOverwrite || global::wstrFileName.empty()) { wchar_t initDir[MAX_PATH] = {0}; - if (!wstrFileName.empty()) { - wstrFileName.substr(wstrFileName.rfind(L'\\') + 1).copy(wcFileName, MAX_PATH - 1); - wstrFileName.substr(0, wstrFileName.rfind(L'\\')).copy(initDir, MAX_PATH - 1); + if (!global::wstrFileName.empty()) { + global::wstrFileName.substr(global::wstrFileName.rfind(L'\\') + 1) + .copy(wcFileName, MAX_PATH - 1); + global::wstrFileName.substr(0, global::wstrFileName.rfind(L'\\')).copy(initDir, MAX_PATH - 1); } OPENFILENAMEW ofn; ZeroMemory(&ofn, sizeof(OPENFILENAMEW)); ofn.lStructSize = sizeof(OPENFILENAMEW); - ofn.hwndOwner = hWnd; + ofn.hwndOwner = global::hWnd; ofn.lpstrFilter = L"Brainfuck source (*.bf;*.b;*.txt)\0*.bf;*.b;*.txt\0All files (*.*)\0*.*\0"; ofn.lpstrFile = wcFileName; ofn.nMaxFile = MAX_PATH; @@ -1084,24 +422,26 @@ bool saveFile(bool _isOverwrite) { ofn.Flags = OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT; if (!GetSaveFileNameW(&ofn)) return false; } else { - wstrFileName.copy(wcFileName, MAX_PATH); + global::wstrFileName.copy(wcFileName, MAX_PATH); } - int editorSize = GetWindowTextLengthW(hEditor) + 1; + int editorSize = GetWindowTextLengthW(global::hEditor) + 1; wchar_t *wcEditor = (wchar_t *)malloc(sizeof(wchar_t) * editorSize); if (!wcEditor) { - messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); + util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", + MB_ICONWARNING); return false; } - GetWindowTextW(hEditor, wcEditor, editorSize); + GetWindowTextW(global::hEditor, wcEditor, editorSize); std::wstring converted = wcEditor; free(wcEditor); - if (newLine != util::NEWLINE_CRLF) util::convertCRLF(converted, newLine); + if (global::newLine != util::NEWLINE_CRLF) util::convertCRLF(converted, global::newLine); int length = WideCharToMultiByte(CP_UTF8, 0, converted.c_str(), -1, NULL, 0, NULL, NULL); char *szEditor = (char *)malloc(sizeof(char) * length); if (!szEditor) { - messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); + util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", + MB_ICONWARNING); return false; } WideCharToMultiByte(CP_UTF8, 0, converted.c_str(), -1, szEditor, length, NULL, NULL); @@ -1109,23 +449,24 @@ bool saveFile(bool _isOverwrite) { HANDLE hFile = CreateFileW(wcFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { - messageBox(hWnd, L"Open failed.", L"Error", MB_ICONWARNING); + util::messageBox(global::hWnd, global::hInst, L"Open failed.", L"Error", MB_ICONWARNING); return false; } DWORD dwTemp; - if (withBOM) WriteFile(hFile, "\xEF\xBB\xBF", 3, &dwTemp, NULL); // BOM + if (global::withBOM) WriteFile(hFile, "\xEF\xBB\xBF", 3, &dwTemp, NULL); // BOM WriteFile(hFile, szEditor, length - 1, &dwTemp, NULL); CloseHandle(hFile); free(szEditor); - if (validHistory) savedIndex = historyIndex; - SendMessageW(hEditor, EM_SETMODIFY, FALSE, 0); - wstrFileName = wcFileName; + if (global::validHistory) global::savedIndex = global::historyIndex; + SendMessageW(global::hEditor, EM_SETMODIFY, FALSE, 0); + global::wstrFileName = wcFileName; std::wstring title = L"["; - title.append(wstrFileName.substr(wstrFileName.rfind(L'\\') + 1) + L"] - " APP_NAME); - SetWindowTextW(hWnd, title.c_str()); + title.append(global::wstrFileName.substr(global::wstrFileName.rfind(L'\\') + 1) + + L"] - " APP_NAME); + SetWindowTextW(global::hWnd, title.c_str()); return true; } diff --git a/ui.hpp b/ui.hpp index a799b60..0a84e03 100644 --- a/ui.hpp +++ b/ui.hpp @@ -1,77 +1,32 @@ #ifndef UI_HPP_ #define UI_HPP_ -#include - -#include "bf.hpp" +#include "main.hpp" namespace ui { -enum state_t { STATE_INIT, STATE_RUN, STATE_PAUSE, STATE_FINISH }; - -extern enum state_t state; -extern bool signedness, wrapInt, breakpoint, debug, dark; -extern int speed, outCharSet, inCharSet; -extern enum Brainfuck::noinput_t noInput; -extern HWND hWnd; -extern HINSTANCE hInst; - -void onCreate(HWND _hWnd, HINSTANCE _hInst); -void onDestroy(); -void onSize(); -void onInitMenuPopup(); -void onScreenKeyboard(int _key); -#ifndef UNDER_CE -void onDropFiles(HDROP hDrop); -void onGetMinMaxInfo(MINMAXINFO *_minMaxInfo); -void onDPIChanged(int _DPI, const RECT *_rect); -#endif -void cut(); -void copy(); -void paste(); -void selAll(); void undo(); void redo(); -// Shows a message box with TaskDialog or MessageBoxW. -// -// When the system this program is running on supports TaskDialog, and no features that are -// MessageBoxW specific (MB_ABORTRETRYIGNORE, MB_CANCELTRYCONTINUE, MB_HELP, default selection, etc) -// is used, this function uses TaskDialog. In other cases, uses MessageBoxW. -// This function substitutes MB_ICONQUESTION with MB_ICONINFORMATION on TaskDialog, as it doesn't -// support it. -int messageBox(HWND _hWnd, const wchar_t *_lpText, const wchar_t *_lpCaption, unsigned int _uType); +// Enables/Disables menu items from the smaller nearest 10 multiple to `_endID`. +void enableMenus(unsigned endID, bool enable); // Sets a UI state. -void setState(enum state_t _state, bool _force = false); +void setState(enum global::state_t state, bool force = false); -// Focuses on a recently focused edit control if a non-edit ID is given. -// Updates internal information if an edit control ID is given. -void updateFocus(int _id = -1); +// Focuses on a recently focused edit control if a non-edit control is given. +// Updates internal information if an edit control is given. +void updateFocus(HWND hWndCtl = NULL); // Sets a selection on the editor. -void selProg(unsigned int _progPtr); +void selProg(unsigned progPtr); // Sets a selection on the memory view. -void selMemView(unsigned int _memPtr); - -// Memory view settings dialog. -INT_PTR CALLBACK memViewProc(HWND hwndDlg, unsigned int uMsg, WPARAM wParam, LPARAM lParam); - -// Retrieves the editor content to the returned buffer. Might fail with NULL. -// Previously returned pointer gets invalidated on each call. -wchar_t *getEditor(); - -// Retrieves the input content to the returned buffer. Might fail with NULL. -// Previously returned pointer gets invalidated on each call. -wchar_t *getInput(); - -// Sets an null-terminated string to the output box. -void setOutput(const wchar_t *_str); +void selMemView(unsigned memPtr); // Outputs an null-terminated string to the output box. -void appendOutput(const wchar_t *_str); +void appendOutput(const wchar_t *str); -// Sets memory. Clears the memory view when NULL is given. +// Sets memory. void setMemory(const unsigned char *memory, int size = 0); // Switches between wordwrap enabled and disabled for edit controls. @@ -92,10 +47,11 @@ bool promptSave(); // Opens a file to the editor. Give it `NULL` or empty string to create new. // Nothing will be changed when canceled by the user. -void openFile(bool _newFile, const wchar_t *_fileName = NULL); +void openFile(bool newFile, const wchar_t *fileName = NULL); // Saves the editor content to a file. // Return value of `true` means successfully saved and `false` means not saved. -bool saveFile(bool _isOverwrite); +bool saveFile(bool isOverwrite); } // namespace ui + #endif diff --git a/util.cpp b/util.cpp index fc2ec24..4b03280 100644 --- a/util.cpp +++ b/util.cpp @@ -1,21 +1,87 @@ #include "util.hpp" -#include +namespace util { +#ifdef UNDER_CE +int messageBox(HWND hWnd, HINSTANCE hInst, const wchar_t *lpText, const wchar_t *lpCaption, + unsigned uType) { + UNREFERENCED_PARAMETER(hInst); + return MessageBoxW(hWnd, lpText, lpCaption, uType); +} +#else +// The function pointer type for TaskDialog API. +typedef HRESULT(__stdcall *TaskDialog_t)(HWND hwndOwner, HINSTANCE hInstance, + const wchar_t *pszWindowTitle, + const wchar_t *pszMainInstruction, + const wchar_t *pszContent, int dwCommonButtons, + const wchar_t *pszIcon, int *pnButton); + +int messageBox(HWND hWnd, HINSTANCE hInst, const wchar_t *lpText, const wchar_t *lpCaption, + unsigned uType) { + // Tests whether uType uses some features that TaskDialog doesn't support. + if (uType & ~(MB_ICONMASK | MB_TYPEMASK)) goto mbfallback; + + int buttons; + switch (uType & MB_TYPEMASK) { + case MB_OK: + buttons = 1; + break; + case MB_OKCANCEL: + buttons = 1 + 8; + break; + case MB_RETRYCANCEL: + buttons = 16 + 8; + break; + case MB_YESNO: + buttons = 2 + 4; + break; + case MB_YESNOCANCEL: + buttons = 2 + 4 + 8; + break; + default: // Not supported by TaskDialog. + goto mbfallback; + } -#ifndef NOMINMAX -#define NOMINMAX -#endif -#include + wchar_t *icon; + switch (uType & MB_ICONMASK) { + case 0: + icon = NULL; + break; + case MB_ICONWARNING: // Same value as MB_ICONEXCLAMATION. + icon = MAKEINTRESOURCEW(-1); + break; + case MB_ICONERROR: // Same value as MB_ICONSTOP and MB_ICONHAND. + icon = MAKEINTRESOURCEW(-2); + break; + default: // Substitute anything else for the information icon. + icon = MAKEINTRESOURCEW(-3); + } -#include "ui.hpp" + { + // Tries to load the TaskDialog API which is a newer substitite of MessageBoxW. + // This API is per monitor DPI aware but doesn't exist before Windows Vista. + // To make the system select version 6 comctl32.dll, we don't use the full path here. + HMODULE comctl32 = LoadLibraryW(L"comctl32.dll"); + if (!comctl32) goto mbfallback; + + TaskDialog_t taskDialog = (TaskDialog_t)(void *)GetProcAddress(comctl32, "TaskDialog"); + if (!taskDialog) { + FreeLibrary(comctl32); + goto mbfallback; + } -namespace util { -static inline bool isHex(wchar_t chr) { - return (chr >= L'0' && chr <= L'9') || (chr >= L'A' && chr <= L'F') || - (chr >= L'a' && chr <= L'f'); + int result; + taskDialog(hWnd, hInst, lpCaption, L"", lpText, buttons, icon, &result); + FreeLibrary(comctl32); + return result; + } + +mbfallback: + return MessageBoxW(hWnd, lpText, lpCaption, uType); } +#endif -bool parseHex(HWND hWnd, const wchar_t *hexInput, unsigned char **dest, int *length) { +bool parseHex(HWND hWnd, HINSTANCE hInst, const wchar_t *hexInput, unsigned char **dest, + int *length) { wchar_t hex[2]; std::string tmp; int hexLen = 0, i; @@ -23,7 +89,7 @@ bool parseHex(HWND hWnd, const wchar_t *hexInput, unsigned char **dest, int *len for (i = 0; true; ++i) { if (isHex(hexInput[i])) { if (hexLen >= 2) { - ui::messageBox(hWnd, L"Each memory value must fit in 8-bit.", L"Error", MB_ICONWARNING); + messageBox(hWnd, hInst, L"Each memory value must fit in 8-bit.", L"Error", MB_ICONWARNING); *dest = NULL; return false; } @@ -54,7 +120,7 @@ bool parseHex(HWND hWnd, const wchar_t *hexInput, unsigned char **dest, int *len hexLen = 0; } } else { - ui::messageBox(hWnd, L"Invalid input.", L"Error", MB_ICONWARNING); + messageBox(hWnd, hInst, L"Invalid input.", L"Error", MB_ICONWARNING); *dest = NULL; return false; } @@ -62,7 +128,7 @@ bool parseHex(HWND hWnd, const wchar_t *hexInput, unsigned char **dest, int *len if (hexInput[i] == L'\0') break; } - *length = tmp.size(); + *length = (int)tmp.size(); if (*length == 0) { *dest = NULL; @@ -71,7 +137,7 @@ bool parseHex(HWND hWnd, const wchar_t *hexInput, unsigned char **dest, int *len *dest = (unsigned char *)malloc(*length); if (!*dest) { - ui::messageBox(hWnd, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); + messageBox(hWnd, hInst, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); *length = 0; return false; } @@ -99,21 +165,22 @@ void toHex(unsigned char num, wchar_t *dest) { dest[3] = 0; } -enum newline_t convertCRLF(std::wstring &_target, enum newline_t _newLine) { - std::wstring::iterator iter = _target.begin(); - std::wstring::iterator iterEnd = _target.end(); +enum newline_t convertCRLF(std::wstring &target, enum newline_t newLine) { + std::wstring::iterator iter = target.begin(); + std::wstring::iterator iterEnd = target.end(); std::wstring temp; - const wchar_t *nl; size_t CRs = 0, LFs = 0, CRLFs = 0; - if (_newLine == NEWLINE_LF) { + + const wchar_t *nl; + if (newLine == NEWLINE_LF) { nl = L"\n"; - } else if (_newLine == NEWLINE_CR) { + } else if (newLine == NEWLINE_CR) { nl = L"\r"; } else { nl = L"\r\n"; } - if (0 < _target.size()) { + if (0 < target.size()) { wchar_t bNextChar = *iter++; while (true) { @@ -146,14 +213,10 @@ enum newline_t convertCRLF(std::wstring &_target, enum newline_t _newLine) { } } - _target = temp; + target = temp; - if (LFs > CRLFs && LFs >= CRs) { - return NEWLINE_LF; - } else if (CRs > LFs && CRs > CRLFs) { - return NEWLINE_CR; - } else { - return NEWLINE_CRLF; - } + return LFs > CRLFs && LFs >= CRs ? NEWLINE_LF + : CRs > LFs && CRs > CRLFs ? NEWLINE_CR + : NEWLINE_CRLF; } } // namespace util diff --git a/util.hpp b/util.hpp index 0eeb612..af822cc 100644 --- a/util.hpp +++ b/util.hpp @@ -11,16 +11,32 @@ namespace util { enum newline_t { NEWLINE_CRLF, NEWLINE_LF, NEWLINE_CR }; +static inline bool isHex(wchar_t chr) { + return (chr >= L'0' && chr <= L'9') || (chr >= L'A' && chr <= L'F') || + (chr >= L'a' && chr <= L'f'); +} + +// Shows a message box with TaskDialog or MessageBoxW. +// +// When the system this program is running on supports TaskDialog, and no features that are +// MessageBoxW specific (MB_ABORTRETRYIGNORE, MB_CANCELTRYCONTINUE, MB_HELP, default selection, etc) +// is used, this function uses TaskDialog. In other cases, uses MessageBoxW. +// This function substitutes MB_ICONQUESTION with MB_ICONINFORMATION on TaskDialog, as it doesn't +// support it. +int messageBox(HWND hWnd, HINSTANCE hInst, const wchar_t *lpText, const wchar_t *lpCaption, + unsigned uType); + // Converts a hex string to a byte sequence. This function allocates `dest` unless it is NULL. // It's your responsibility to `free()` it. // Return value of true indicates success, otherwise indicates failure. -extern bool parseHex(HWND hWnd, const wchar_t *hexInput, unsigned char **dest, int *length); +bool parseHex(HWND hWnd, HINSTANCE hInst, const wchar_t *hexInput, unsigned char **dest, + int *length); // Converts a byte to a hex string. `dest` must have at least 4 elements. -extern void toHex(unsigned char num, wchar_t *dest); +void toHex(unsigned char num, wchar_t *dest); // Translates newlines and returns the previous newline code. // `_target`: A `std::wstring` to operate on, `_newLine`: A desired newline code. -enum newline_t convertCRLF(std::wstring &_target, enum newline_t _newLine); +enum newline_t convertCRLF(std::wstring &target, enum newline_t newLine); } // namespace util #endif diff --git a/vs2022proj-pc/brainfuck_vs2022.vcxproj b/vs2022proj-pc/brainfuck_vs2022.vcxproj index 4646095..1e0f714 100644 --- a/vs2022proj-pc/brainfuck_vs2022.vcxproj +++ b/vs2022proj-pc/brainfuck_vs2022.vcxproj @@ -355,12 +355,23 @@ + + + + + + + + + + + diff --git a/vs2022proj-pc/brainfuck_vs2022.vcxproj.filters b/vs2022proj-pc/brainfuck_vs2022.vcxproj.filters index 37fc875..dc469c9 100644 --- a/vs2022proj-pc/brainfuck_vs2022.vcxproj.filters +++ b/vs2022proj-pc/brainfuck_vs2022.vcxproj.filters @@ -24,6 +24,21 @@ ソース ファイル + + ソース ファイル + + + ソース ファイル + + + ソース ファイル + + + ソース ファイル + + + ソース ファイル + @@ -35,6 +50,24 @@ ヘッダー ファイル + + ヘッダー ファイル + + + ヘッダー ファイル + + + ヘッダー ファイル + + + ヘッダー ファイル + + + ヘッダー ファイル + + + ヘッダー ファイル + diff --git a/wproc.cpp b/wproc.cpp new file mode 100644 index 0000000..e61371a --- /dev/null +++ b/wproc.cpp @@ -0,0 +1,224 @@ +#include "wproc.hpp" + +#include + +#include "msg.hpp" +#include "runner.hpp" + +namespace wproc { +LRESULT CALLBACK wndProc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam) { + static HBRUSH BGDark = CreateSolidBrush(0x3f3936); // Background brush for the dark theme. + + switch (uMsg) { + HANDLE_MSG(hWnd, WM_ACTIVATE, msg::onActivate); + HANDLE_MSG(hWnd, WM_CLOSE, msg::onClose); + HANDLE_MSG(hWnd, WM_COMMAND, msg::onCommand); + HANDLE_MSG(hWnd, WM_CREATE, msg::onCreate); + HANDLE_MSG(hWnd, WM_INITMENUPOPUP, msg::onInitMenuPopup); + HANDLE_MSG(hWnd, WM_SIZE, msg::onSize); + +#ifndef UNDER_CE + HANDLE_MSG(hWnd, WM_DROPFILES, msg::onDropFiles); + HANDLE_MSG(hWnd, WM_GETMINMAXINFO, msg::onGetMinMaxInfo); + + case 0x2e0: // WM_DPICHANGED + msg::onDPIChanged(hWnd, HIWORD(wParam), (const RECT *)lParam); + return 0; +#endif + + case WM_CTLCOLOREDIT: + if (global::dark) { + SetTextColor((HDC)wParam, 0x00ff00); + SetBkColor((HDC)wParam, 0x3f3936); + return (LRESULT)BGDark; + } else { + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } + + case WM_CTLCOLORSTATIC: + if (global::dark) { + SetTextColor((HDC)wParam, 0x00ff00); + SetBkColor((HDC)wParam, 0x000000); + return (LRESULT)GetStockObject(BLACK_BRUSH); + } else { + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } + + case WM_DESTROY: + msg::onDestroy(); + DeleteObject(BGDark); + PostQuitMessage(0); + return 0; + + case WM_APP_THREADEND: + WaitForSingleObject(runner::hThread, INFINITE); + CloseHandle(runner::hThread); + runner::hThread = NULL; + runner::ctrlThread = runner::CTRLTHREAD_RUN; + return 0; + } + + return DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + +LRESULT CALLBACK editorProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam) { + static WNDPROC prevWndProc = (WNDPROC)myGetWindowLongW(hWnd, GWL_USERDATA); + + switch (uMsg) { + case WM_CHAR: { + if (!global::validHistory) break; + + int editorSize = GetWindowTextLengthW(global::hEditor) + 1; + wchar_t *wcEditor = (wchar_t *)malloc(sizeof(wchar_t) * editorSize); + if (!wcEditor) { + util::messageBox(hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", + MB_ICONWARNING); + while (!global::history.empty()) { + free(global::history.back()); + global::history.pop_back(); + } + global::historyIndex = 0; + global::savedIndex = -1; + global::validHistory = false; + break; + } + GetWindowTextW(global::hEditor, wcEditor, editorSize); + + if (!global::history.empty() && !wcscmp(global::history[global::historyIndex], wcEditor)) { + free(wcEditor); + break; + } + + int toRemove = (int)global::history.size() - global::historyIndex - 1, i = 0; + for (; i < toRemove; ++i) { + free(global::history.back()); + global::history.pop_back(); + } + + if (global::history.size() >= MAX_HISTORY) { + free(global::history.front()); + global::history.pop_front(); + --global::historyIndex; + if (global::savedIndex >= 0) --global::savedIndex; + } + + global::history.push_back(wcEditor); + ++global::historyIndex; + break; + } + + case WM_DESTROY: + mySetWindowLongW(hWnd, GWL_WNDPROC, prevWndProc); + return 0; + } + + return CallWindowProcW(prevWndProc, hWnd, uMsg, wParam, lParam); +} + +LRESULT CALLBACK memViewDlgEditor(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam) { + static WNDPROC prevWndProc = (WNDPROC)myGetWindowLongW(hWnd, GWL_USERDATA); + + switch (uMsg) { + case WM_CHAR: + switch ((wchar_t)wParam) { + case L'q': + SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"1"); + return 0; + + case L'w': + SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"2"); + return 0; + + case L'e': + SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"3"); + return 0; + + case L'r': + SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"4"); + return 0; + + case L't': + SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"5"); + return 0; + + case L'y': + SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"6"); + return 0; + + case L'u': + SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"7"); + return 0; + + case L'i': + SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"8"); + return 0; + + case L'o': + SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"9"); + return 0; + + case L'p': + SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"0"); + return 0; + } + break; + + case WM_DESTROY: + mySetWindowLongW(hWnd, GWL_WNDPROC, prevWndProc); + return 0; + } + + return CallWindowProcW(prevWndProc, hWnd, uMsg, wParam, lParam); +} + +INT_PTR CALLBACK memViewProc(HWND hDlg, unsigned uMsg, WPARAM wParam, LPARAM lParam) { + UNREFERENCED_PARAMETER(lParam); + + switch (uMsg) { + case WM_INITDIALOG: { + // Puts the dialog at the center of the parent window. + RECT wndSize, wndRect, dlgRect; + GetWindowRect(hDlg, &dlgRect); + GetWindowRect(global::hWnd, &wndRect); + GetClientRect(global::hWnd, &wndSize); + int newPosX = wndRect.left + (wndSize.right - (dlgRect.right - dlgRect.left)) / 2, + newPosY = wndRect.top + (wndSize.bottom - (dlgRect.bottom - dlgRect.top)) / 2; + if (newPosX < 0) newPosX = 0; + if (newPosY < 0) newPosY = 0; + SetWindowPos(hDlg, NULL, newPosX, newPosY, 0, 0, SWP_NOZORDER | SWP_NOSIZE); + + wchar_t editBuf[10]; + HWND hEdit = GetDlgItem(hDlg, 3); + wsprintfW(editBuf, L"%u", + global::memViewStart <= 999999999 ? global::memViewStart : 999999999); + SendDlgItemMessageW(hDlg, 3, EM_SETLIMITTEXT, 9, 0); + SetDlgItemTextW(hDlg, 3, editBuf); + mySetWindowLongW(hEdit, GWL_USERDATA, mySetWindowLongW(hEdit, GWL_WNDPROC, memViewDlgEditor)); + return TRUE; + } + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: { + wchar_t editBuf[10]; + long temp; + GetDlgItemTextW(hDlg, 3, (wchar_t *)editBuf, 10); + temp = wcstol(editBuf, NULL, 10); + if (temp < 0) { + util::messageBox(hDlg, global::hInst, L"Invalid input.", L"Error", MB_ICONWARNING); + } else { + global::memViewStart = temp; + EndDialog(hDlg, IDOK); + } + return TRUE; + } + + case IDCANCEL: + EndDialog(hDlg, IDCANCEL); + return TRUE; + } + return FALSE; + } + return FALSE; +} +} // namespace wproc diff --git a/wproc.hpp b/wproc.hpp new file mode 100644 index 0000000..ce1cb3d --- /dev/null +++ b/wproc.hpp @@ -0,0 +1,22 @@ +#ifndef HOOK_HPP_ +#define HOOK_HPP_ + +#include "main.hpp" + +namespace wproc { +// Window procedure for the main window. +LRESULT CALLBACK wndProc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam); + +// Hook window procedure for the program editor that manages the undo/redo history. +LRESULT CALLBACK editorProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam); + +// Hook window procedure for the edit control in the memory view options dialog. +// This procedure translates top row character keys to numbers according to the keyboard layout of +// SHARP Brain devices. +LRESULT CALLBACK memViewDlgEditor(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam); + +// Window procedure for the memory view options dialog. +INT_PTR CALLBACK memViewProc(HWND hDlg, unsigned uMsg, WPARAM wParam, LPARAM lParam); +} // namespace hook + +#endif From 9004bb988e5d4ff80b59566ff62cd26167f9ae89 Mon Sep 17 00:00:00 2001 From: watamario15 Date: Sat, 1 Apr 2023 01:29:42 +0900 Subject: [PATCH 8/9] Improve Undo/Redo --- bf.cpp | 14 ++- bf.hpp | 27 ++-- evc4proj/brainfuck_evc4.vcp | 16 +++ history.cpp | 117 ++++++++++++++++++ history.hpp | 60 +++++++++ main.cpp | 6 +- main.hpp | 9 +- msg.cpp | 42 ++++++- resource.rc | 1 + runner.cpp | 18 +-- ui.cpp | 85 ++++--------- ui.hpp | 6 +- vs2022proj-pc/brainfuck_vs2022.vcxproj | 2 + .../brainfuck_vs2022.vcxproj.filters | 6 + wproc.cpp | 55 +++----- wproc.hpp | 5 +- 16 files changed, 330 insertions(+), 139 deletions(-) create mode 100644 history.cpp create mode 100644 history.hpp diff --git a/bf.cpp b/bf.cpp index b4dda0c..56559e3 100644 --- a/bf.cpp +++ b/bf.cpp @@ -8,14 +8,23 @@ #include void Brainfuck::reset() { + if (Brainfuck::program) { + free(Brainfuck::program); + Brainfuck::program = NULL; + } progIndex = 0; + if (Brainfuck::input) { + free(Brainfuck::input); + Brainfuck::input = NULL; + } inIndex = 0; memset(memory, 0, sizeof(memory)); memIndex = 0; memLen = 1; } -void Brainfuck::reset(unsigned progLen, const wchar_t *program, unsigned inLen, const void *input) { +void Brainfuck::reset(unsigned progLen, wchar_t *program, unsigned inLen, void *input) { + if (Brainfuck::program) free(Brainfuck::program); if (progLen == 0 || !program) { Brainfuck::program = NULL; Brainfuck::progLen = 0; @@ -25,11 +34,12 @@ void Brainfuck::reset(unsigned progLen, const wchar_t *program, unsigned inLen, } progIndex = 0; + if (Brainfuck::input) free(Brainfuck::input); if (inLen == 0 || !input) { Brainfuck::input = NULL; Brainfuck::inLen = 0; } else { - Brainfuck::input = (const unsigned char *)input; + Brainfuck::input = (unsigned char *)input; Brainfuck::inLen = inLen; } inIndex = 0; diff --git a/bf.hpp b/bf.hpp index 45cabc3..2cb3362 100644 --- a/bf.hpp +++ b/bf.hpp @@ -4,7 +4,7 @@ #ifndef BF_HPP_ #define BF_HPP_ -#include +#include class Brainfuck { public: @@ -26,9 +26,11 @@ class Brainfuck { reset(); } - // Initializes the internal state and registers a program and an _input. + // Initializes the internal state and registers a program and an input. // You must call reset() whenever you want to modify the program and the input. - Brainfuck(unsigned progLen, const wchar_t *program, unsigned inLen, const void *input) + // Pass a program and an input as dedicated malloc()-ed data and don't free() them. This module + // takes care of it. + Brainfuck(unsigned progLen, wchar_t *program, unsigned inLen, void *input) : program(NULL), input(NULL), memLen(1), @@ -39,12 +41,19 @@ class Brainfuck { reset(progLen, program, inLen, input); } + ~Brainfuck() { + if (program) free(program); + if (input) free(input); + } + // Resets the internal state. void reset(); - // Resets the internal state and registers a program and an _input. + // Resets the internal state and registers a program and an input. // You must call reset() whenever you want to modify the program and the input. - void reset(unsigned progLen, const wchar_t *program, unsigned inLen, const void *input); + // Pass a program and an input as dedicated malloc()-ed data and don't free() them. This module + // takes care of it. + void reset(unsigned progLen, wchar_t *program, unsigned inLen, void *input); // Change implementation-defined behaviors. // If wrapInt is false, next() throws an exception when overflowed or underflowed. @@ -55,12 +64,12 @@ class Brainfuck { void setBehavior(enum noinput_t noInput = NOINPUT_ZERO, bool wrapInt = true, bool signedness = true, bool debug = false); - // Executes the next code, and writes its output on `_output` if any. + // Executes the next code, and writes its output on `output` if any. // Returns the result of an execution, which is running, breakpoint, finished, and // error. enum result_t next(unsigned char *output, bool *didOutput); - // Returns the memory and writes its size to `_size`. + // Returns the memory and writes its size to `size`. // The returned content becomes invalid on reset() and next(). const unsigned char *getMemory(unsigned *size) { *size = memLen; @@ -77,8 +86,8 @@ class Brainfuck { const wchar_t *getLastError() { return lastError; } private: - const wchar_t *program; // Program - const unsigned char *input; // Input stream + wchar_t *program; // Program + unsigned char *input; // Input stream unsigned char memory[65536]; // Memory unsigned memIndex, progIndex, inIndex, progLen, inLen, memLen; bool wrapInt, signedness, debug; diff --git a/evc4proj/brainfuck_evc4.vcp b/evc4proj/brainfuck_evc4.vcp index 3e6fa40..a71227f 100644 --- a/evc4proj/brainfuck_evc4.vcp +++ b/evc4proj/brainfuck_evc4.vcp @@ -108,9 +108,17 @@ DEP_CPP_BF_CP=\ # End Source File # Begin Source File +SOURCE=..\history.cpp +DEP_CPP_HISTO=\ + "..\history.hpp"\ + +# End Source File +# Begin Source File + SOURCE=..\main.cpp DEP_CPP_MAIN_=\ "..\bf.hpp"\ + "..\history.hpp"\ "..\main.hpp"\ "..\msg.hpp"\ "..\resource.h"\ @@ -124,6 +132,7 @@ DEP_CPP_MAIN_=\ SOURCE=..\msg.cpp DEP_CPP_MSG_C=\ "..\bf.hpp"\ + "..\history.hpp"\ "..\main.hpp"\ "..\msg.hpp"\ "..\resource.h"\ @@ -138,6 +147,7 @@ DEP_CPP_MSG_C=\ SOURCE=..\runner.cpp DEP_CPP_RUNNE=\ "..\bf.hpp"\ + "..\history.hpp"\ "..\main.hpp"\ "..\resource.h"\ "..\runner.hpp"\ @@ -158,6 +168,7 @@ DEP_CPP_TOKEN=\ SOURCE=..\ui.cpp DEP_CPP_UI_CP=\ "..\bf.hpp"\ + "..\history.hpp"\ "..\main.hpp"\ "..\msg.hpp"\ "..\resource.h"\ @@ -178,6 +189,7 @@ DEP_CPP_UTIL_=\ SOURCE=..\wproc.cpp DEP_CPP_WPROC=\ "..\bf.hpp"\ + "..\history.hpp"\ "..\main.hpp"\ "..\msg.hpp"\ "..\resource.h"\ @@ -196,6 +208,10 @@ SOURCE=..\bf.hpp # End Source File # Begin Source File +SOURCE=..\history.hpp +# End Source File +# Begin Source File + SOURCE=..\main.hpp # End Source File # Begin Source File diff --git a/history.cpp b/history.cpp new file mode 100644 index 0000000..61c0d75 --- /dev/null +++ b/history.cpp @@ -0,0 +1,117 @@ +#include "history.hpp" + +void History::reset(HWND hEditor, const wchar_t *str) { + SendMessageW(hEditor, EM_SETMODIFY, FALSE, 0); + + while (!history.empty()) { + free(history.back()); + history.pop_back(); + } + + wchar_t *newStr; + if (str) { + newStr = (wchar_t *)malloc((wcslen(str) + 1) * sizeof(wchar_t)); + if (!newStr) { + validHistory = false; + historyIndex = 0; + savedIndex = -1; + return; + } + wcscpy(newStr, str); + } else { + newStr = (wchar_t *)malloc(sizeof(wchar_t)); + if (!newStr) { + validHistory = false; + historyIndex = 0; + savedIndex = -1; + return; + } + newStr[0] = L'\0'; + } + + this->hEditor = hEditor; + history.push_back(newStr); + historyIndex = savedIndex = 0; + validHistory = true; +} + +void History::add() { + if (!validHistory) return; + + int editorSize = GetWindowTextLengthW(hEditor) + 1; + wchar_t *wcEditor = (wchar_t *)malloc(sizeof(wchar_t) * editorSize); + if (!wcEditor) { + while (!history.empty()) { + free(history.back()); + history.pop_back(); + } + historyIndex = 0; + savedIndex = -1; + validHistory = false; + return; + } + GetWindowTextW(hEditor, wcEditor, editorSize); + + if (!history.empty() && !wcscmp(history[historyIndex], wcEditor)) { + free(wcEditor); + return; + } + + while (historyIndex < (int)history.size() - 1) { + free(history.back()); + history.pop_back(); + } + + if (history.size() >= MAX_HISTORY) { + free(history.front()); + history.pop_front(); + --historyIndex; + if (savedIndex >= 0) --savedIndex; + } + + history.push_back(wcEditor); + ++historyIndex; +} + +void History::undo() { + if (canUndo()) { + DWORD selEnd; + --historyIndex; + SendMessageW(hEditor, EM_GETSEL, (WPARAM)NULL, (LPARAM)&selEnd); + SetWindowTextW(hEditor, history[historyIndex]); + SendMessageW(hEditor, EM_SETSEL, selEnd, selEnd); + } +} + +void History::redo() { + if (canRedo()) { + DWORD selEnd; + ++historyIndex; + SendMessageW(hEditor, EM_GETSEL, (WPARAM)NULL, (LPARAM)&selEnd); + SetWindowTextW(hEditor, history[historyIndex]); + SendMessageW(hEditor, EM_SETSEL, selEnd, selEnd); + } +} + +void History::setSaved() { + if (validHistory) savedIndex = historyIndex; + SendMessageW(hEditor, EM_SETMODIFY, FALSE, 0); +} + +bool History::isSaved() { + if (validHistory) { + if (historyIndex == savedIndex) return true; + if (savedIndex != -1) { + int editorSize = GetWindowTextLengthW(hEditor) + 1; + wchar_t *wcEditor = (wchar_t *)malloc(sizeof(wchar_t) * editorSize); + if (!wcEditor) return false; + GetWindowTextW(hEditor, wcEditor, editorSize); + bool result = !wcscmp(history[savedIndex], wcEditor); + free(wcEditor); + return result; + } + return false; + } else { + return SendMessageW(hEditor, EM_GETMODIFY, 0, 0) == FALSE; + } +} diff --git a/history.hpp b/history.hpp new file mode 100644 index 0000000..e660f7f --- /dev/null +++ b/history.hpp @@ -0,0 +1,60 @@ +#ifndef HISTORY_HPP_ +#define HISTORY_HPP_ + +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include + +#include + +#define MAX_HISTORY 128 + +class History { + public: + History() { reset(NULL); } + + ~History() { + while (!history.empty()) { + free(history.back()); + history.pop_back(); + } + } + + // Resets the undo/redo history and sets `str` as the first entry. + // `hEditor` is the handle to a edit control which this module tracks the history of. + // If `str` is NULL, the first entry will be an empty string. + void reset(HWND hEditor, const wchar_t *str = NULL); + + // Adds the current edit control text to the undo/redo history. + // Redo history will be cleared. + void add(); + + // Changes back the edit control text to the previous state. + void undo(); + + // Changes the edit control text to the next state. + void redo(); + + bool canUndo() { return validHistory && historyIndex > 0; } + + bool canRedo() { return validHistory && historyIndex < (int)history.size() - 1; } + + // Marks the current edit control text as saved. + void setSaved(); + + // Returns whether the current edit control text is saved. + bool isSaved(); + + // Changes the edit control which this module tracks the history of, without modifing the history. + void changeEditor(HWND hEditor) { this->hEditor = hEditor; } + + private: + int historyIndex; // Index to the current state in the undo/redo history. + int savedIndex; // Index to the saved state in the undo/redo history. + bool validHistory; // Whether the undo/redo history is valid. + HWND hEditor; // Handle to the edit control. + std::deque history; // Undo/Redo history. +}; + +#endif diff --git a/main.cpp b/main.cpp index cac28bf..65d6384 100644 --- a/main.cpp +++ b/main.cpp @@ -22,9 +22,6 @@ int speed = 10; // Brainfuck execution speed. int outCharSet = IDM_BF_OUTPUT_UTF8; // Brainfuck stdout charset. int inCharSet = IDM_BF_INPUT_UTF8; // Brainfuck stdin charset. int memViewStart = 0; // First address of the memory view (0-indexed byte). -int historyIndex = 0; // Index to the current state in the undo/redo history. -int savedIndex = 0; // Index to the saved state in the undo/redo history. -bool validHistory = true; // Whether the undo/redo history is valid. bool signedness = true; // Signedness of the Brainfuck memory. True for signed. bool wrapInt = true; // Whether to wrap around an integer in Brainfuck. bool breakpoint = false; // Whether to enable breakpoints in Brainfuck. @@ -34,8 +31,9 @@ bool horizontal = false; // Layout for this app. bool withBOM = false; // Whether the opened file contained an UTF-8 BOM. bool wordwrap = true; // Whether to enable the word wrap on editors. wchar_t *cmdLine; // Pointer to the command line. -std::deque history; // Undo/Redo history. std::wstring wstrFileName; // Current file name. +class History history; // Program undo/redo history. +class History inputHistory; // Input undo/redo history. enum Brainfuck::noinput_t noInput = Brainfuck::NOINPUT_ZERO; // Brainfuck behavior on no input. enum util::newline_t newLine = util::NEWLINE_CRLF; // Newline code for the current file. enum state_t state = STATE_INIT; // Current state of this app. diff --git a/main.hpp b/main.hpp index 54e2320..1769605 100644 --- a/main.hpp +++ b/main.hpp @@ -25,12 +25,12 @@ #include #endif -#include #include #include "bf.hpp" #include "resource.h" #include "util.hpp" +#include "history.hpp" // Workaround for wrong macro definitions in CeGCC. #ifdef UNDER_CE @@ -95,7 +95,6 @@ typedef HRESULT(CALLBACK *GetDpiForMonitor_t)(HMONITOR hmonitor, MONITOR_DPI_TYP WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX #endif #define WM_APP_THREADEND WM_APP -#define MAX_HISTORY 32 namespace global { enum state_t { STATE_INIT, STATE_RUN, STATE_PAUSE, STATE_FINISH }; @@ -115,9 +114,6 @@ extern int speed; extern int outCharSet; extern int inCharSet; extern int memViewStart; -extern int historyIndex; -extern int savedIndex; -extern bool validHistory; extern bool signedness; extern bool wrapInt; extern bool breakpoint; @@ -127,8 +123,9 @@ extern bool horizontal; extern bool withBOM; extern bool wordwrap; extern wchar_t *cmdLine; -extern std::deque history; extern std::wstring wstrFileName; +extern class History history; +extern class History inputHistory; extern Brainfuck::noinput_t noInput; extern enum util::newline_t newLine; extern enum state_t state; diff --git a/msg.cpp b/msg.cpp index 1a5f784..f4b75e1 100644 --- a/msg.cpp +++ b/msg.cpp @@ -98,6 +98,20 @@ void onCommand(HWND hWnd, int id, HWND hWndCtl, UINT codeNotify) { didInit = false; break; + case IDC_EDITOR: + if (codeNotify == EN_CHANGE) { + global::history.add(); + ui::updateTitle(); + } + break; + + case IDC_INPUT: + if (codeNotify == EN_CHANGE) { + global::inputHistory.add(); + ui::updateTitle(); + } + break; + case IDM_FILE_NEW: ui::openFile(true); break; @@ -119,11 +133,21 @@ void onCommand(HWND hWnd, int id, HWND hWndCtl, UINT codeNotify) { break; case IDM_EDIT_UNDO: - ui::undo(); + if (global::hFocused == global::hEditor) { + global::history.undo(); + ui::updateTitle(); + } else if (global::hFocused == global::hInput) { + global::inputHistory.undo(); + } break; case IDM_EDIT_REDO: - ui::redo(); + if (global::hFocused == global::hEditor) { + global::history.redo(); + ui::updateTitle(); + } else if (global::hFocused == global::hInput) { + global::inputHistory.redo(); + } break; case IDM_EDIT_CUT: @@ -308,6 +332,9 @@ BOOL onCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct) { (global::wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, 0, 0, 0, 0, hWnd, (HMENU)IDC_INPUT, global::hInst, NULL); SendMessageW(global::hInput, EM_SETLIMITTEXT, (WPARAM)-1, 0); + mySetWindowLongW(global::hInput, GWL_USERDATA, + mySetWindowLongW(global::hInput, GWL_WNDPROC, wproc::inputProc)); + global::inputHistory.reset(global::hInput); // Program output global::hOutput = CreateWindowExW( @@ -464,9 +491,14 @@ void onInitMenuPopup(HWND hWnd, HMENU hMenu, UINT item, BOOL fSystemMenu) { if (global::state == global::STATE_INIT) { ui::enableMenus(IDM_FILE_NEW, true); ui::enableMenus(IDM_FILE_OPEN, true); - ui::enableMenus(IDM_EDIT_UNDO, global::validHistory && global::historyIndex > 0); - ui::enableMenus(IDM_EDIT_REDO, - global::validHistory && global::historyIndex < (int)global::history.size() - 1); + ui::enableMenus(IDM_EDIT_UNDO, global::hFocused == global::hEditor ? global::history.canUndo() + : global::hFocused == global::hInput + ? global::inputHistory.canUndo() + : false); + ui::enableMenus(IDM_EDIT_REDO, global::hFocused == global::hEditor ? global::history.canRedo() + : global::hFocused == global::hInput + ? global::inputHistory.canRedo() + : false); ui::enableMenus(IDM_BF_MEMTYPE_UNSIGNED, true); ui::enableMenus(IDM_BF_OUTPUT_HEX, true); ui::enableMenus(IDM_BF_INPUT_HEX, true); diff --git a/resource.rc b/resource.rc index 3580175..1e457eb 100644 --- a/resource.rc +++ b/resource.rc @@ -126,6 +126,7 @@ menu MENU accel ACCELERATORS { "^A", IDM_EDIT_SELALL + "^Y", IDM_EDIT_REDO } diff --git a/runner.cpp b/runner.cpp index f41bc48..4fe5edf 100644 --- a/runner.cpp +++ b/runner.cpp @@ -14,8 +14,6 @@ static class SJISTokenizer sjisTokenizer; static bool prevCR = false; bool bfInit() { - static unsigned char *input = NULL; - static wchar_t *program = NULL; int inLen; int inputSize = GetWindowTextLengthW(global::hInput) + 1; @@ -28,20 +26,20 @@ bool bfInit() { return false; } - unsigned char *newInput; + unsigned char *input; if (global::inCharSet == IDM_BF_INPUT_HEX) { - if (!util::parseHex(global::hWnd, global::hInst, wcInput, &newInput, &inLen)) return false; + if (!util::parseHex(global::hWnd, global::hInst, wcInput, &input, &inLen)) return false; } else { int codePage = (global::inCharSet == IDM_BF_INPUT_SJIS) ? 932 : CP_UTF8; inLen = WideCharToMultiByte(codePage, 0, wcInput, -1, NULL, 0, NULL, NULL); - newInput = (unsigned char *)malloc(inLen); - if (!newInput) { + input = (unsigned char *)malloc(inLen); + if (!input) { util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); free(wcInput); return false; } - WideCharToMultiByte(codePage, 0, wcInput, -1, (char *)newInput, inLen, NULL, NULL); + WideCharToMultiByte(codePage, 0, wcInput, -1, (char *)input, inLen, NULL, NULL); inLen--; } @@ -60,7 +58,7 @@ bool bfInit() { } else { util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); - free(newInput); + free(input); return false; } @@ -69,10 +67,6 @@ bool bfInit() { SetWindowTextW(global::hOutput, NULL); SetWindowTextW(global::hMemView, NULL); prevCR = false; - if (input) free(input); - if (program) free(program); - input = newInput; - program = wcEditor; return true; } diff --git a/ui.cpp b/ui.cpp index 9f402cc..3d17a64 100644 --- a/ui.cpp +++ b/ui.cpp @@ -4,14 +4,6 @@ #include "wproc.hpp" namespace ui { -void undo() { - // TODO -} - -void redo() { - // TODO -} - void enableMenus(unsigned endID, bool enable) { unsigned i; for (i = (endID / 10) * 10; i <= endID; ++i) { @@ -164,6 +156,7 @@ void switchWordwrap() { SendMessageW(global::hInput, EM_SETLIMITTEXT, (WPARAM)-1, 0); mySetWindowLongW(global::hEditor, GWL_USERDATA, mySetWindowLongW(global::hEditor, GWL_WNDPROC, wproc::editorProc)); + global::history.changeEditor(global::hEditor); // Program input global::hInput = CreateWindowExW( @@ -172,6 +165,9 @@ void switchWordwrap() { (global::wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, 0, 0, 0, 0, global::hWnd, (HMENU)IDC_INPUT, global::hInst, NULL); SendMessageW(global::hInput, EM_SETLIMITTEXT, (WPARAM)-1, 0); + mySetWindowLongW(global::hInput, GWL_USERDATA, + mySetWindowLongW(global::hInput, GWL_WNDPROC, wproc::inputProc)); + global::inputHistory.changeEditor(global::hInput); // Program output global::hOutput = CreateWindowExW( @@ -211,6 +207,20 @@ void switchLayout() { msg::onSize(global::hWnd); } +void updateTitle() { + std::wstring title; + + title += L"["; + title += global::wstrFileName.empty() + ? L"New File" + : global::wstrFileName.substr(global::wstrFileName.rfind(L'\\') + 1); + title += global::history.isSaved() ? L"] - " : L" *] - "; + + title += APP_NAME; + + SetWindowTextW(global::hWnd, title.c_str()); +} + void chooseFont() { #ifndef UNDER_CE LONG origHeight = global::editFont.lfHeight; @@ -251,8 +261,7 @@ void chooseFont() { } bool promptSave() { - if (global::validHistory && global::historyIndex == global::savedIndex) return true; - if (!global::validHistory && SendMessageW(global::hEditor, EM_GETMODIFY, 0, 0) == 0) return true; + if (global::history.isSaved()) return true; int ret = util::messageBox(global::hWnd, global::hInst, L"Unsaved data will be lost. Save changes?", @@ -281,28 +290,11 @@ void openFile(bool newFile, const wchar_t *fileName) { if (newFile) { // new SetWindowTextW(global::hEditor, L""); - SetWindowTextW(global::hWnd, APP_NAME); - SendMessageW(global::hEditor, EM_SETMODIFY, FALSE, 0); + updateTitle(); global::wstrFileName = L""; global::withBOM = false; global::newLine = util::NEWLINE_CRLF; - - while (!global::history.empty()) { - free(global::history.back()); - global::history.pop_back(); - } - wchar_t *wcEditor = (wchar_t *)calloc(1, sizeof(wchar_t)); - if (!wcEditor) { - util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", - MB_ICONWARNING); - global::historyIndex = 0; - global::savedIndex = -1; - global::validHistory = false; - return; - } - global::history.push_back(wcEditor); - global::historyIndex = global::savedIndex = 0; - global::validHistory = true; + global::history.reset(global::hEditor); return; } @@ -369,35 +361,13 @@ void openFile(bool newFile, const wchar_t *fileName) { global::newLine = util::convertCRLF(converted, util::NEWLINE_CRLF); SetWindowTextW(global::hEditor, converted.c_str()); - SendMessageW(global::hEditor, EM_SETMODIFY, FALSE, 0); global::wstrFileName = fileName; global::withBOM = padding != 0; - - while (!global::history.empty()) { - free(global::history.back()); - global::history.pop_back(); - } - wchar_t *wcEditor = (wchar_t *)calloc(converted.size() + 1, sizeof(wchar_t)); - if (!wcEditor) { - util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", - MB_ICONWARNING); - global::historyIndex = 0; - global::savedIndex = -1; - global::validHistory = false; - return; - } - wcscpy(wcEditor, converted.c_str()); - global::history.push_back(wcEditor); - global::historyIndex = global::savedIndex = 0; - global::validHistory = true; + global::history.reset(global::hEditor, converted.c_str()); SetWindowTextW(global::hOutput, NULL); SetWindowTextW(global::hMemView, NULL); - - std::wstring title = L"["; - title.append(global::wstrFileName.substr(global::wstrFileName.rfind(L'\\') + 1) + - L"] - " APP_NAME); - SetWindowTextW(global::hWnd, title.c_str()); + updateTitle(); } bool saveFile(bool isOverwrite) { @@ -459,14 +429,9 @@ bool saveFile(bool isOverwrite) { CloseHandle(hFile); free(szEditor); - if (global::validHistory) global::savedIndex = global::historyIndex; - SendMessageW(global::hEditor, EM_SETMODIFY, FALSE, 0); + global::history.setSaved(); global::wstrFileName = wcFileName; - - std::wstring title = L"["; - title.append(global::wstrFileName.substr(global::wstrFileName.rfind(L'\\') + 1) + - L"] - " APP_NAME); - SetWindowTextW(global::hWnd, title.c_str()); + updateTitle(); return true; } diff --git a/ui.hpp b/ui.hpp index 0a84e03..06fa93c 100644 --- a/ui.hpp +++ b/ui.hpp @@ -4,9 +4,6 @@ #include "main.hpp" namespace ui { -void undo(); -void redo(); - // Enables/Disables menu items from the smaller nearest 10 multiple to `_endID`. void enableMenus(unsigned endID, bool enable); @@ -38,6 +35,9 @@ void switchTheme(); // Switches between horizontal/portrait layout. void switchLayout(); +// Sets the title of the main window. +void updateTitle(); + // Opens a font dialog and applies it on editors. void chooseFont(); diff --git a/vs2022proj-pc/brainfuck_vs2022.vcxproj b/vs2022proj-pc/brainfuck_vs2022.vcxproj index 1e0f714..9394044 100644 --- a/vs2022proj-pc/brainfuck_vs2022.vcxproj +++ b/vs2022proj-pc/brainfuck_vs2022.vcxproj @@ -354,6 +354,7 @@ + @@ -364,6 +365,7 @@ + diff --git a/vs2022proj-pc/brainfuck_vs2022.vcxproj.filters b/vs2022proj-pc/brainfuck_vs2022.vcxproj.filters index dc469c9..17109a7 100644 --- a/vs2022proj-pc/brainfuck_vs2022.vcxproj.filters +++ b/vs2022proj-pc/brainfuck_vs2022.vcxproj.filters @@ -39,6 +39,9 @@ ソース ファイル + + ソース ファイル + @@ -68,6 +71,9 @@ ヘッダー ファイル + + ヘッダー ファイル + diff --git a/wproc.cpp b/wproc.cpp index e61371a..1c83d82 100644 --- a/wproc.cpp +++ b/wproc.cpp @@ -4,6 +4,7 @@ #include "msg.hpp" #include "runner.hpp" +#include "ui.hpp" namespace wproc { LRESULT CALLBACK wndProc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam) { @@ -65,47 +66,27 @@ LRESULT CALLBACK editorProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lPar static WNDPROC prevWndProc = (WNDPROC)myGetWindowLongW(hWnd, GWL_USERDATA); switch (uMsg) { - case WM_CHAR: { - if (!global::validHistory) break; - - int editorSize = GetWindowTextLengthW(global::hEditor) + 1; - wchar_t *wcEditor = (wchar_t *)malloc(sizeof(wchar_t) * editorSize); - if (!wcEditor) { - util::messageBox(hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", - MB_ICONWARNING); - while (!global::history.empty()) { - free(global::history.back()); - global::history.pop_back(); - } - global::historyIndex = 0; - global::savedIndex = -1; - global::validHistory = false; - break; - } - GetWindowTextW(global::hEditor, wcEditor, editorSize); + case EM_UNDO: + global::history.undo(); + ui::updateTitle(); + return 0; - if (!global::history.empty() && !wcscmp(global::history[global::historyIndex], wcEditor)) { - free(wcEditor); - break; - } + case WM_DESTROY: + mySetWindowLongW(hWnd, GWL_WNDPROC, prevWndProc); + return 0; + } - int toRemove = (int)global::history.size() - global::historyIndex - 1, i = 0; - for (; i < toRemove; ++i) { - free(global::history.back()); - global::history.pop_back(); - } + return CallWindowProcW(prevWndProc, hWnd, uMsg, wParam, lParam); +} - if (global::history.size() >= MAX_HISTORY) { - free(global::history.front()); - global::history.pop_front(); - --global::historyIndex; - if (global::savedIndex >= 0) --global::savedIndex; - } +LRESULT CALLBACK inputProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam) { + static WNDPROC prevWndProc = (WNDPROC)myGetWindowLongW(hWnd, GWL_USERDATA); - global::history.push_back(wcEditor); - ++global::historyIndex; - break; - } + switch (uMsg) { + case EM_UNDO: + global::inputHistory.undo(); + ui::updateTitle(); + return 0; case WM_DESTROY: mySetWindowLongW(hWnd, GWL_WNDPROC, prevWndProc); diff --git a/wproc.hpp b/wproc.hpp index ce1cb3d..5300b06 100644 --- a/wproc.hpp +++ b/wproc.hpp @@ -7,9 +7,12 @@ namespace wproc { // Window procedure for the main window. LRESULT CALLBACK wndProc(HWND hWnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam); -// Hook window procedure for the program editor that manages the undo/redo history. +// Hook window procedure for the program editor to support the undo feature. LRESULT CALLBACK editorProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam); +// Hook window procedure for the input editor to support the undo feature. +LRESULT CALLBACK inputProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam); + // Hook window procedure for the edit control in the memory view options dialog. // This procedure translates top row character keys to numbers according to the keyboard layout of // SHARP Brain devices. From a8a541c503b7c252a3fbd2a219e296c4f73275ea Mon Sep 17 00:00:00 2001 From: watamario15 Date: Thu, 6 Jul 2023 00:21:21 +0900 Subject: [PATCH 9/9] Fix things --- .clang-format | 2 + bf.cpp | 18 ++-- bf.hpp | 33 ++---- brain.sh | 0 history.cpp | 2 +- main.hpp | 9 +- msg.cpp | 168 ++++++++++++----------------- tokenizer.cpp | 6 +- ui.cpp | 115 +++++++------------- util.cpp | 72 +++---------- util.hpp | 18 ++-- vs2005proj/brainfuck_vs2005.vcproj | 52 +++++++++ vs2008proj/brainfuck_vs2008.vcproj | 52 +++++++++ win.sh | 0 wproc.cpp | 3 +- wproc.hpp | 9 +- 16 files changed, 261 insertions(+), 298 deletions(-) create mode 100644 .clang-format mode change 100644 => 100755 brain.sh mode change 100644 => 100755 win.sh diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..bfd118e --- /dev/null +++ b/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: Google +ColumnLimit: 120 diff --git a/bf.cpp b/bf.cpp index 56559e3..36abf4b 100644 --- a/bf.cpp +++ b/bf.cpp @@ -70,8 +70,7 @@ enum Brainfuck::result_t Brainfuck::next(unsigned char *output, bool *didOutput) ++memIndex; if (memIndex == memLen) ++memLen; } else { - wsprintfW(lastError, L"%u: Instruction '>' used when the memory pointer is 65535.", - progIndex); + wsprintfW(lastError, L"%u: Instruction '>' used when the memory pointer is 65535.", progIndex); return RESULT_ERR; } break; @@ -93,16 +92,14 @@ enum Brainfuck::result_t Brainfuck::next(unsigned char *output, bool *didOutput) if (memory[memIndex] != 0x7F) { ++memory[memIndex]; } else { - wsprintfW(lastError, L"%u: Instruction '+' used when the pointed memory is 127.", - progIndex); + wsprintfW(lastError, L"%u: Instruction '+' used when the pointed memory is 127.", progIndex); return RESULT_ERR; } } else { if (memory[memIndex] != 0xFF) { ++memory[memIndex]; } else { - wsprintfW(lastError, L"%u: Instruction '+' used when the pointed memory is 255.", - progIndex); + wsprintfW(lastError, L"%u: Instruction '+' used when the pointed memory is 255.", progIndex); return RESULT_ERR; } } @@ -116,16 +113,14 @@ enum Brainfuck::result_t Brainfuck::next(unsigned char *output, bool *didOutput) if (memory[memIndex] != 0x80) { --memory[memIndex]; } else { - wsprintfW(lastError, L"%u: Instruction '-' used when the pointed memory is -128.", - progIndex); + wsprintfW(lastError, L"%u: Instruction '-' used when the pointed memory is -128.", progIndex); return RESULT_ERR; } } else { if (memory[memIndex] != 0x00) { --memory[memIndex]; } else { - wsprintfW(lastError, L"%u: Instruction '-' used when the pointed memory is 0.", - progIndex); + wsprintfW(lastError, L"%u: Instruction '-' used when the pointed memory is 0.", progIndex); return RESULT_ERR; } } @@ -143,8 +138,7 @@ enum Brainfuck::result_t Brainfuck::next(unsigned char *output, bool *didOutput) } else if (noInput == NOINPUT_FF) { memory[memIndex] = 255; } else { - wsprintfW(lastError, L"%u: Instruction ',' used when the input stream is empty.", - progIndex); + wsprintfW(lastError, L"%u: Instruction ',' used when the input stream is empty.", progIndex); return RESULT_ERR; } } else { diff --git a/bf.hpp b/bf.hpp index 2cb3362..6905b51 100644 --- a/bf.hpp +++ b/bf.hpp @@ -16,28 +16,15 @@ class Brainfuck { // Initializes the internal state. Brainfuck() - : program(NULL), - input(NULL), - memLen(1), - wrapInt(true), - signedness(true), - debug(false), - noInput(NOINPUT_ZERO) { + : program(NULL), input(NULL), memLen(1), wrapInt(true), signedness(true), debug(false), noInput(NOINPUT_ZERO) { reset(); } // Initializes the internal state and registers a program and an input. // You must call reset() whenever you want to modify the program and the input. - // Pass a program and an input as dedicated malloc()-ed data and don't free() them. This module - // takes care of it. + // Pass a program and an input as dedicated malloc()-ed data and don't free() them. This module takes care of it. Brainfuck(unsigned progLen, wchar_t *program, unsigned inLen, void *input) - : program(NULL), - input(NULL), - memLen(1), - wrapInt(true), - signedness(true), - debug(false), - noInput(NOINPUT_ZERO) { + : program(NULL), input(NULL), memLen(1), wrapInt(true), signedness(true), debug(false), noInput(NOINPUT_ZERO) { reset(progLen, program, inLen, input); } @@ -51,22 +38,20 @@ class Brainfuck { // Resets the internal state and registers a program and an input. // You must call reset() whenever you want to modify the program and the input. - // Pass a program and an input as dedicated malloc()-ed data and don't free() them. This module - // takes care of it. + // Pass a program and an input as dedicated malloc()-ed data and don't free() them. This module takes care of it. void reset(unsigned progLen, wchar_t *program, unsigned inLen, void *input); // Change implementation-defined behaviors. // If wrapInt is false, next() throws an exception when overflowed or underflowed. // If wrapInt is true, signedness doen't have any effect. // If debug is true, breakpoint instruction ("@") is enabled. - // The default behavior is [zero for no input, wrap around integer, signed integer (no effects in - // this case), no debug]. - void setBehavior(enum noinput_t noInput = NOINPUT_ZERO, bool wrapInt = true, - bool signedness = true, bool debug = false); + // The default behavior is [zero for no input, wrap around integer, signed integer (no effects in this case), no + // debug]. + void setBehavior(enum noinput_t noInput = NOINPUT_ZERO, bool wrapInt = true, bool signedness = true, + bool debug = false); // Executes the next code, and writes its output on `output` if any. - // Returns the result of an execution, which is running, breakpoint, finished, and - // error. + // Returns the result of an execution, which is running, breakpoint, finished, and error. enum result_t next(unsigned char *output, bool *didOutput); // Returns the memory and writes its size to `size`. diff --git a/brain.sh b/brain.sh old mode 100644 new mode 100755 diff --git a/history.cpp b/history.cpp index 61c0d75..6bd9050 100644 --- a/history.cpp +++ b/history.cpp @@ -74,7 +74,7 @@ void History::add() { } void History::undo() { - if (canUndo()) { + if (canUndo()) { DWORD selEnd; --historyIndex; SendMessageW(hEditor, EM_GETSEL, (WPARAM)NULL, (LPARAM)&selEnd); diff --git a/main.hpp b/main.hpp index 1769605..881f0ca 100644 --- a/main.hpp +++ b/main.hpp @@ -28,9 +28,9 @@ #include #include "bf.hpp" +#include "history.hpp" #include "resource.h" #include "util.hpp" -#include "history.hpp" // Workaround for wrong macro definitions in CeGCC. #ifdef UNDER_CE @@ -71,8 +71,8 @@ typedef enum MONITOR_DPI_TYPE { } MONITOR_DPI_TYPE; // The function pointer type for GetDpiForMonitor API. -typedef HRESULT(CALLBACK *GetDpiForMonitor_t)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, - unsigned *dpiX, unsigned *dpiY); +typedef HRESULT(CALLBACK *GetDpiForMonitor_t)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, unsigned *dpiX, + unsigned *dpiY); #endif // Making SetWindowLongW and GetWindowLongW compatible for both 32-bit and 64-bit system. @@ -91,8 +91,7 @@ typedef HRESULT(CALLBACK *GetDpiForMonitor_t)(HMONITOR hmonitor, MONITOR_DPI_TYP #endif #ifndef WS_OVERLAPPEDWINDOW -#define WS_OVERLAPPEDWINDOW \ - WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX +#define WS_OVERLAPPEDWINDOW WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX #endif #define WM_APP_THREADEND WM_APP diff --git a/msg.cpp b/msg.cpp index f4b75e1..423a380 100644 --- a/msg.cpp +++ b/msg.cpp @@ -54,8 +54,7 @@ void onCommand(HWND hWnd, int id, HWND hWndCtl, UINT codeNotify) { if (runner::hThread) { SetThreadPriority(runner::hThread, THREAD_PRIORITY_BELOW_NORMAL); } else { - util::messageBox(hWnd, global::hInst, L"Failed to create a runner thread.", - L"Internal Error", MB_ICONERROR); + util::messageBox(hWnd, global::hInst, L"Failed to create a runner thread.", L"Internal Error", MB_ICONERROR); break; } @@ -70,12 +69,10 @@ void onCommand(HWND hWnd, int id, HWND hWndCtl, UINT codeNotify) { } Brainfuck::result_t result = runner::bfNext(); - ui::setState(result == Brainfuck::RESULT_FIN || result == Brainfuck::RESULT_ERR - ? global::STATE_FINISH - : global::STATE_PAUSE); + ui::setState(result == Brainfuck::RESULT_FIN || result == Brainfuck::RESULT_ERR ? global::STATE_FINISH + : global::STATE_PAUSE); if (result == Brainfuck::RESULT_ERR) { - util::messageBox(hWnd, global::hInst, runner::bf.getLastError(), L"Brainfuck Error", - MB_ICONWARNING); + util::messageBox(hWnd, global::hInst, runner::bf.getLastError(), L"Brainfuck Error", MB_ICONWARNING); } unsigned size; @@ -220,8 +217,7 @@ void onCommand(HWND hWnd, int id, HWND hWndCtl, UINT codeNotify) { case IDM_BF_BREAKPOINT: global::breakpoint = !global::breakpoint; - runner::bf.setBehavior(global::noInput, global::wrapInt, global::signedness, - global::breakpoint); + runner::bf.setBehavior(global::noInput, global::wrapInt, global::signedness, global::breakpoint); break; case IDM_OPT_SPEED_FASTEST: @@ -289,8 +285,7 @@ void onCommand(HWND hWnd, int id, HWND hWndCtl, UINT codeNotify) { break; default: - if (id >= IDC_SCRKBD_FIRST && id < IDC_SCRKBD_FIRST + SCRKBD_LEN && - global::hFocused == global::hEditor) { + if (id >= IDC_SCRKBD_FIRST && id < IDC_SCRKBD_FIRST + SCRKBD_LEN && global::hFocused == global::hEditor) { SendMessageW(global::hEditor, EM_REPLACESEL, 0, (WPARAM)wcScrKB[id - IDC_SCRKBD_FIRST]); } } @@ -316,54 +311,49 @@ BOOL onCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct) { #endif // Program editor - global::hEditor = CreateWindowExW( - 0, L"EDIT", L"", - WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL | - (global::wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, - 0, 0, 0, 0, hWnd, (HMENU)IDC_EDITOR, global::hInst, NULL); + global::hEditor = + CreateWindowExW(0, L"EDIT", L"", + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL | + (global::wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, + 0, 0, 0, 0, hWnd, (HMENU)IDC_EDITOR, global::hInst, NULL); SendMessageW(global::hEditor, EM_SETLIMITTEXT, (WPARAM)-1, 0); - mySetWindowLongW(global::hEditor, GWL_USERDATA, - mySetWindowLongW(global::hEditor, GWL_WNDPROC, wproc::editorProc)); + mySetWindowLongW(global::hEditor, GWL_USERDATA, mySetWindowLongW(global::hEditor, GWL_WNDPROC, wproc::editorProc)); // Program input - global::hInput = CreateWindowExW( - 0, L"EDIT", L"", - WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL | - (global::wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, - 0, 0, 0, 0, hWnd, (HMENU)IDC_INPUT, global::hInst, NULL); + global::hInput = CreateWindowExW(0, L"EDIT", L"", + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | + WS_VSCROLL | (global::wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, + 0, 0, 0, 0, hWnd, (HMENU)IDC_INPUT, global::hInst, NULL); SendMessageW(global::hInput, EM_SETLIMITTEXT, (WPARAM)-1, 0); - mySetWindowLongW(global::hInput, GWL_USERDATA, - mySetWindowLongW(global::hInput, GWL_WNDPROC, wproc::inputProc)); + mySetWindowLongW(global::hInput, GWL_USERDATA, mySetWindowLongW(global::hInput, GWL_WNDPROC, wproc::inputProc)); global::inputHistory.reset(global::hInput); // Program output - global::hOutput = CreateWindowExW( - 0, L"EDIT", L"", - WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL | - WS_VSCROLL | (global::wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, - 0, 0, 0, 0, hWnd, (HMENU)IDC_OUTPUT, global::hInst, NULL); + global::hOutput = + CreateWindowExW(0, L"EDIT", L"", + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL | + WS_VSCROLL | (global::wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, + 0, 0, 0, 0, hWnd, (HMENU)IDC_OUTPUT, global::hInst, NULL); SendMessageW(global::hOutput, EM_SETLIMITTEXT, (WPARAM)-1, 0); // Memory view global::hMemView = CreateWindowExW(0, L"EDIT", L"", - WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | - ES_READONLY | ES_AUTOVSCROLL | WS_VSCROLL | ES_NOHIDESEL, + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_READONLY | + ES_AUTOVSCROLL | WS_VSCROLL | ES_NOHIDESEL, 0, 0, 0, 0, hWnd, (HMENU)IDC_MEMVIEW, global::hInst, NULL); SendMessageW(global::hMemView, EM_SETLIMITTEXT, (WPARAM)-1, 0); // Command button for (i = 0; i < CMDBTN_LEN; ++i) { - global::hCmdBtn[i] = CreateWindowExW( - 0, L"BUTTON", wcCmdBtn[i], - WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | (i == 2 || i == 3 ? WS_DISABLED : 0), 0, 0, 0, 0, - hWnd, (HMENU)(IDC_CMDBTN_FIRST + i), global::hInst, NULL); + global::hCmdBtn[i] = CreateWindowExW(0, L"BUTTON", wcCmdBtn[i], + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | (i == 2 || i == 3 ? WS_DISABLED : 0), + 0, 0, 0, 0, hWnd, (HMENU)(IDC_CMDBTN_FIRST + i), global::hInst, NULL); } // Screen keyboard for (i = 0; i < SCRKBD_LEN; ++i) { - global::hScrKB[i] = - CreateWindowExW(0, L"BUTTON", wcScrKB[i], WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 0, 0, 0, 0, - hWnd, (HMENU)(IDC_SCRKBD_FIRST + i), global::hInst, NULL); + global::hScrKB[i] = CreateWindowExW(0, L"BUTTON", wcScrKB[i], WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 0, 0, 0, 0, + hWnd, (HMENU)(IDC_SCRKBD_FIRST + i), global::hInst, NULL); } global::hFocused = global::hEditor; @@ -382,51 +372,42 @@ BOOL onCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct) { // It maps to a default shell font associated with the current culture/locale. lstrcpyW(global::editFont.lfFaceName, L"MS Shell Dlg"); - // Obtains the "system DPI" value. We use this as the fallback value on older Windows versions - // and to calculate the appropriate font height value for ChooseFontW. + // Obtains the "system DPI" value. We use this as the fallback value on older Windows versions and to calculate the + // appropriate font height value for ChooseFontW. HDC hDC = GetDC(hWnd); global::sysDPI = GetDeviceCaps(hDC, LOGPIXELSX); ReleaseDC(hWnd, hDC); - // Tries to load the GetDpiForMonitor API. Use of the full path improves security. We avoid a - // direct call to keep this program compatible with Windows 7 and earlier. + // Tries to load the GetDpiForMonitor API. Use of the full path improves security. We avoid a direct call to keep this + // program compatible with Windows 7 and earlier. // - // Microsoft recommends the use of GetDpiForWindow API instead of this API according to their - // documentation. However, it requires Windows 10 1607 or later, which makes this compatibility - // keeping code more complicated, and GetDpiForMonitor API still works for programs that only use - // the process-wide DPI awareness. Here, as we only use the process-wide DPI awareness, we are - // going to use GetDpiForMonitor API. + // Microsoft recommends the use of GetDpiForWindow API instead of this API according to their documentation. However, + // it requires Windows 10 1607 or later, which makes this compatibility keeping code more complicated, and + // GetDpiForMonitor API still works for programs that only use the process-wide DPI awareness. Here, as we only use + // the process-wide DPI awareness, we are going to use GetDpiForMonitor API. // // References: // https://learn.microsoft.com/en-us/windows/win32/api/shellscalingapi/nf-shellscalingapi-getdpiformonitor // https://mariusbancila.ro/blog/2021/05/19/how-to-build-high-dpi-aware-native-desktop-applications/ GetDpiForMonitor_t getDpiForMonitor = NULL; HMODULE dll = LoadLibraryW(L"C:\\Windows\\System32\\Shcore.dll"); - if (dll) { - getDpiForMonitor = (GetDpiForMonitor_t)(void *)GetProcAddress(dll, "GetDpiForMonitor"); - } + if (dll) getDpiForMonitor = (GetDpiForMonitor_t)(void *)GetProcAddress(dll, "GetDpiForMonitor"); // Tests whether it successfully got the GetDpiForMonitor API. if (getDpiForMonitor) { // It got (the system is presumably Windows 8.1 or later). unsigned tmpX, tmpY; - getDpiForMonitor(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST), MDT_EFFECTIVE_DPI, &tmpX, - &tmpY); + getDpiForMonitor(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST), MDT_EFFECTIVE_DPI, &tmpX, &tmpY); dpi = tmpX; } else { // It failed (the system is presumably older than Windows 8.1). dpi = global::sysDPI; } if (dll) FreeLibrary(dll); - // Adjusts the windows size according to the DPI value. - SetWindowPos(hWnd, NULL, 0, 0, adjust(480), adjust(320), - SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE); + // Adjusts the window size according to the DPI value. + SetWindowPos(hWnd, NULL, 0, 0, adjust(480), adjust(320), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE); #endif - if (global::cmdLine[0]) { - ui::openFile(false, global::cmdLine); - } else { - ui::openFile(true); - } + ui::openFile(!global::cmdLine[0], global::cmdLine); return TRUE; } @@ -443,62 +424,48 @@ void onInitMenuPopup(HWND hWnd, HMENU hMenu, UINT item, BOOL fSystemMenu) { // Brainfuck -> Memory Type CheckMenuRadioItem(hMenu, IDM_BF_MEMTYPE_SIGNED, IDM_BF_MEMTYPE_UNSIGNED, - global::signedness ? IDM_BF_MEMTYPE_SIGNED : IDM_BF_MEMTYPE_UNSIGNED, - MF_BYCOMMAND); + global::signedness ? IDM_BF_MEMTYPE_SIGNED : IDM_BF_MEMTYPE_UNSIGNED, MF_BYCOMMAND); // Brainfuck -> Output Charset - CheckMenuRadioItem(hMenu, IDM_BF_OUTPUT_UTF8, IDM_BF_OUTPUT_HEX, global::outCharSet, - MF_BYCOMMAND); + CheckMenuRadioItem(hMenu, IDM_BF_OUTPUT_UTF8, IDM_BF_OUTPUT_HEX, global::outCharSet, MF_BYCOMMAND); // Brainfuck -> Input Charset CheckMenuRadioItem(hMenu, IDM_BF_INPUT_UTF8, IDM_BF_INPUT_HEX, global::inCharSet, MF_BYCOMMAND); // Brainfuck -> Input Instruction - CheckMenuRadioItem(hMenu, IDM_BF_NOINPUT_ERROR, IDM_BF_NOINPUT_FF, - IDM_BF_NOINPUT_ERROR + global::noInput, MF_BYCOMMAND); + CheckMenuRadioItem(hMenu, IDM_BF_NOINPUT_ERROR, IDM_BF_NOINPUT_FF, IDM_BF_NOINPUT_ERROR + global::noInput, + MF_BYCOMMAND); // Brainfuck -> Integer Overflow CheckMenuRadioItem(hMenu, IDM_BF_INTOVF_ERROR, IDM_BF_INTOVF_WRAPAROUND, - global::wrapInt ? IDM_BF_INTOVF_WRAPAROUND : IDM_BF_INTOVF_ERROR, - MF_BYCOMMAND); + global::wrapInt ? IDM_BF_INTOVF_WRAPAROUND : IDM_BF_INTOVF_ERROR, MF_BYCOMMAND); // Brainfuck -> Breakpoint - CheckMenuItem(hMenu, IDM_BF_BREAKPOINT, - MF_BYCOMMAND | global::breakpoint ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu, IDM_BF_BREAKPOINT, MF_BYCOMMAND | global::breakpoint ? MF_CHECKED : MF_UNCHECKED); // Options -> Speed - if (global::speed == 0) { - CheckMenuRadioItem(hMenu, IDM_OPT_SPEED_FASTEST, IDM_OPT_SPEED_100MS, IDM_OPT_SPEED_FASTEST, - MF_BYCOMMAND); - } else if (global::speed == 1) { - CheckMenuRadioItem(hMenu, IDM_OPT_SPEED_FASTEST, IDM_OPT_SPEED_100MS, IDM_OPT_SPEED_1MS, - MF_BYCOMMAND); - } else if (global::speed == 10) { - CheckMenuRadioItem(hMenu, IDM_OPT_SPEED_FASTEST, IDM_OPT_SPEED_100MS, IDM_OPT_SPEED_10MS, - MF_BYCOMMAND); - } else if (global::speed == 100) { - CheckMenuRadioItem(hMenu, IDM_OPT_SPEED_FASTEST, IDM_OPT_SPEED_100MS, IDM_OPT_SPEED_100MS, - MF_BYCOMMAND); - } + CheckMenuRadioItem(hMenu, IDM_OPT_SPEED_FASTEST, IDM_OPT_SPEED_100MS, + global::speed == 0 ? IDM_OPT_SPEED_FASTEST + : global::speed == 1 ? IDM_OPT_SPEED_1MS + : global::speed == 10 ? IDM_OPT_SPEED_10MS + : IDM_OPT_SPEED_100MS, + MF_BYCOMMAND); // Options -> Debug CheckMenuItem(hMenu, IDM_OPT_TRACK, MF_BYCOMMAND | global::debug ? MF_CHECKED : MF_UNCHECKED); // Options -> Word Wrap - CheckMenuItem(hMenu, IDM_OPT_WORDWRAP, - MF_BYCOMMAND | global::wordwrap ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu, IDM_OPT_WORDWRAP, MF_BYCOMMAND | global::wordwrap ? MF_CHECKED : MF_UNCHECKED); if (global::state == global::STATE_INIT) { ui::enableMenus(IDM_FILE_NEW, true); ui::enableMenus(IDM_FILE_OPEN, true); - ui::enableMenus(IDM_EDIT_UNDO, global::hFocused == global::hEditor ? global::history.canUndo() - : global::hFocused == global::hInput - ? global::inputHistory.canUndo() - : false); - ui::enableMenus(IDM_EDIT_REDO, global::hFocused == global::hEditor ? global::history.canRedo() - : global::hFocused == global::hInput - ? global::inputHistory.canRedo() - : false); + ui::enableMenus(IDM_EDIT_UNDO, global::hFocused == global::hEditor ? global::history.canUndo() + : global::hFocused == global::hInput ? global::inputHistory.canUndo() + : false); + ui::enableMenus(IDM_EDIT_REDO, global::hFocused == global::hEditor ? global::history.canRedo() + : global::hFocused == global::hInput ? global::inputHistory.canRedo() + : false); ui::enableMenus(IDM_BF_MEMTYPE_UNSIGNED, true); ui::enableMenus(IDM_BF_OUTPUT_HEX, true); ui::enableMenus(IDM_BF_INPUT_HEX, true); @@ -561,8 +528,8 @@ void onSize(HWND hWnd) { // Seemingly, Windows CE doesn't re-send WM_SIZE on SetWindowPos calls inside a WM_SIZE handler. GetWindowRect(hWnd, &rect); if (rect.right - rect.left < 480 || rect.bottom - rect.top < 320) { - SetWindowPos(hWnd, NULL, 0, 0, mymax(480, rect.right - rect.left), - mymax(320, rect.bottom - rect.top), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE); + SetWindowPos(hWnd, NULL, 0, 0, mymax(480, rect.right - rect.left), mymax(320, rect.bottom - rect.top), + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE); } #endif @@ -608,10 +575,8 @@ void onSize(HWND hWnd) { if (global::horizontal) { MoveWindow(global::hEditor, 0, topEditor, scrX / 3, scrY - topEditor, TRUE); MoveWindow(global::hInput, scrX / 3, topEditor, scrX / 3, halfY, TRUE); - MoveWindow(global::hOutput, scrX / 3, halfY + topEditor, scrX / 3, scrY - halfY - topEditor, - TRUE); - MoveWindow(global::hMemView, scrX * 2 / 3, topEditor, scrX - scrX * 2 / 3, scrY - topEditor, - TRUE); + MoveWindow(global::hOutput, scrX / 3, halfY + topEditor, scrX / 3, scrY - halfY - topEditor, TRUE); + MoveWindow(global::hMemView, scrX * 2 / 3, topEditor, scrX - scrX * 2 / 3, scrY - topEditor, TRUE); } else { MoveWindow(global::hEditor, 0, topEditor, scrX, halfY, TRUE); MoveWindow(global::hInput, 0, centerY, scrX / 2, halfY / 2, TRUE); @@ -649,8 +614,7 @@ void onGetMinMaxInfo(HWND hWnd, LPMINMAXINFO lpMinMaxInfo) { void onDPIChanged(HWND hWnd, int dpi, const RECT *rect) { msg::dpi = dpi; - MoveWindow(hWnd, rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top, - FALSE); + MoveWindow(hWnd, rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top, FALSE); } #endif } // namespace msg diff --git a/tokenizer.cpp b/tokenizer.cpp index 28aaa99..9023693 100644 --- a/tokenizer.cpp +++ b/tokenizer.cpp @@ -63,8 +63,7 @@ const wchar_t *UTF8Tokenizer::add(unsigned char chr) { // Completed one character. if (state == 0) { memset(result, 0, sizeof(result)); - MultiByteToWideChar(CP_UTF8, 0, (char *)token, -1, result, - sizeof(result) / sizeof(result[0])); + MultiByteToWideChar(CP_UTF8, 0, (char *)token, -1, result, sizeof(result) / sizeof(result[0])); index = 0; return result; } @@ -121,8 +120,7 @@ const wchar_t *SJISTokenizer::add(unsigned char chr) { // Completed one character. const unsigned char character[2] = {token, chr}; memset(result, 0, sizeof(result)); - MultiByteToWideChar(932, 0, (const char *)character, 2, result, - sizeof(result) / sizeof(result[0])); + MultiByteToWideChar(932, 0, (const char *)character, 2, result, sizeof(result) / sizeof(result[0])); token = 0; return result; } diff --git a/ui.cpp b/ui.cpp index 3d17a64..964bc5a 100644 --- a/ui.cpp +++ b/ui.cpp @@ -23,9 +23,7 @@ void setState(enum global::state_t state, bool force) { SendMessageW(global::hEditor, EM_SETREADONLY, (WPARAM)FALSE, (LPARAM)NULL); SendMessageW(global::hInput, EM_SETREADONLY, (WPARAM)FALSE, (LPARAM)NULL); SendMessageW(global::hOutput, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); - for (i = 0; i < SCRKBD_LEN; ++i) { - EnableWindow(global::hScrKB[i], TRUE); - } + for (i = 0; i < SCRKBD_LEN; ++i) EnableWindow(global::hScrKB[i], TRUE); #ifndef UNDER_CE DragAcceptFiles(global::hWnd, TRUE); #endif @@ -37,9 +35,7 @@ void setState(enum global::state_t state, bool force) { SendMessageW(global::hEditor, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); SendMessageW(global::hInput, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); SendMessageW(global::hOutput, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); - for (i = 0; i < SCRKBD_LEN; ++i) { - EnableWindow(global::hScrKB[i], FALSE); - } + for (i = 0; i < SCRKBD_LEN; ++i) EnableWindow(global::hScrKB[i], FALSE); #ifndef UNDER_CE DragAcceptFiles(global::hWnd, FALSE); #endif @@ -51,9 +47,7 @@ void setState(enum global::state_t state, bool force) { SendMessageW(global::hEditor, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); SendMessageW(global::hInput, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); SendMessageW(global::hOutput, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); - for (i = 0; i < SCRKBD_LEN; ++i) { - EnableWindow(global::hScrKB[i], FALSE); - } + for (i = 0; i < SCRKBD_LEN; ++i) EnableWindow(global::hScrKB[i], FALSE); #ifndef UNDER_CE DragAcceptFiles(global::hWnd, FALSE); #endif @@ -65,9 +59,7 @@ void setState(enum global::state_t state, bool force) { SendMessageW(global::hEditor, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); SendMessageW(global::hInput, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); SendMessageW(global::hOutput, EM_SETREADONLY, (WPARAM)TRUE, (LPARAM)NULL); - for (i = 0; i < SCRKBD_LEN; ++i) { - EnableWindow(global::hScrKB[i], FALSE); - } + for (i = 0; i < SCRKBD_LEN; ++i) EnableWindow(global::hScrKB[i], FALSE); #ifndef UNDER_CE DragAcceptFiles(global::hWnd, FALSE); #endif @@ -114,8 +106,7 @@ void switchWordwrap() { int editorSize = GetWindowTextLengthW(global::hEditor) + 1; wchar_t *wcEditor = (wchar_t *)malloc(sizeof(wchar_t) * editorSize); if (!wcEditor) { - util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", - MB_ICONWARNING); + util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); return; } GetWindowTextW(global::hEditor, wcEditor, editorSize); @@ -126,8 +117,7 @@ void switchWordwrap() { wchar_t *wcInput = (wchar_t *)malloc(sizeof(wchar_t) * inputSize); if (!wcInput) { free(wcEditor); - util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", - MB_ICONWARNING); + util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); return; } GetWindowTextW(global::hInput, wcInput, inputSize); @@ -138,8 +128,7 @@ void switchWordwrap() { if (!wcOutput) { free(wcEditor); free(wcInput); - util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", - MB_ICONWARNING); + util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); return; } GetWindowTextW(global::hOutput, wcOutput, outputSize); @@ -148,33 +137,30 @@ void switchWordwrap() { global::wordwrap = !global::wordwrap; // Program editor - global::hEditor = CreateWindowExW( - 0, L"EDIT", L"", - WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL | - (global::wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, - 0, 0, 0, 0, global::hWnd, (HMENU)IDC_EDITOR, global::hInst, NULL); + global::hEditor = + CreateWindowExW(0, L"EDIT", L"", + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL | + (global::wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, + 0, 0, 0, 0, global::hWnd, (HMENU)IDC_EDITOR, global::hInst, NULL); SendMessageW(global::hInput, EM_SETLIMITTEXT, (WPARAM)-1, 0); - mySetWindowLongW(global::hEditor, GWL_USERDATA, - mySetWindowLongW(global::hEditor, GWL_WNDPROC, wproc::editorProc)); + mySetWindowLongW(global::hEditor, GWL_USERDATA, mySetWindowLongW(global::hEditor, GWL_WNDPROC, wproc::editorProc)); global::history.changeEditor(global::hEditor); // Program input - global::hInput = CreateWindowExW( - 0, L"EDIT", L"", - WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL | - (global::wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, - 0, 0, 0, 0, global::hWnd, (HMENU)IDC_INPUT, global::hInst, NULL); + global::hInput = CreateWindowExW(0, L"EDIT", L"", + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | + WS_VSCROLL | (global::wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, + 0, 0, 0, 0, global::hWnd, (HMENU)IDC_INPUT, global::hInst, NULL); SendMessageW(global::hInput, EM_SETLIMITTEXT, (WPARAM)-1, 0); - mySetWindowLongW(global::hInput, GWL_USERDATA, - mySetWindowLongW(global::hInput, GWL_WNDPROC, wproc::inputProc)); + mySetWindowLongW(global::hInput, GWL_USERDATA, mySetWindowLongW(global::hInput, GWL_WNDPROC, wproc::inputProc)); global::inputHistory.changeEditor(global::hInput); // Program output - global::hOutput = CreateWindowExW( - 0, L"EDIT", L"", - WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL | - WS_VSCROLL | (global::wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, - 0, 0, 0, 0, global::hWnd, (HMENU)IDC_OUTPUT, global::hInst, NULL); + global::hOutput = + CreateWindowExW(0, L"EDIT", L"", + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL | + WS_VSCROLL | (global::wordwrap ? 0 : ES_AUTOHSCROLL | WS_HSCROLL) | ES_NOHIDESEL, + 0, 0, 0, 0, global::hWnd, (HMENU)IDC_OUTPUT, global::hInst, NULL); SendMessageW(global::hOutput, EM_SETLIMITTEXT, (WPARAM)-1, 0); global::hFocused = global::hEditor; @@ -211,9 +197,8 @@ void updateTitle() { std::wstring title; title += L"["; - title += global::wstrFileName.empty() - ? L"New File" - : global::wstrFileName.substr(global::wstrFileName.rfind(L'\\') + 1); + title += + global::wstrFileName.empty() ? L"New File" : global::wstrFileName.substr(global::wstrFileName.rfind(L'\\') + 1); title += global::history.isSaved() ? L"] - " : L" *] - "; title += APP_NAME; @@ -248,8 +233,7 @@ void chooseFont() { #else if (ret) { // Re-converts to the 96 DPI value as we properly adjust it on the fly. - global::editFont.lfHeight = - (global::editFont.lfHeight * 96 - (global::sysDPI - 1)) / global::sysDPI; + global::editFont.lfHeight = (global::editFont.lfHeight * 96 - (global::sysDPI - 1)) / global::sysDPI; global::editFont.lfQuality = ANTIALIASED_QUALITY; msg::onSize(global::hWnd); } else { @@ -263,24 +247,10 @@ void chooseFont() { bool promptSave() { if (global::history.isSaved()) return true; - int ret = - util::messageBox(global::hWnd, global::hInst, L"Unsaved data will be lost. Save changes?", - L"Confirm", MB_ICONWARNING | MB_YESNOCANCEL); + int ret = util::messageBox(global::hWnd, global::hInst, L"Unsaved data will be lost. Save changes?", L"Confirm", + MB_ICONWARNING | MB_YESNOCANCEL); - if (ret == IDCANCEL) { - return false; - } else if (ret == IDYES) { - if (saveFile(true)) { - return true; - } else { - return false; - } - } else if (ret == IDNO) { - return true; - } - - // Shouldn't be reached. - return true; + return ret == IDCANCEL ? false : ret == IDYES ? saveFile(true) : true; } void openFile(bool newFile, const wchar_t *fileName) { @@ -298,7 +268,7 @@ void openFile(bool newFile, const wchar_t *fileName) { return; } - if (!fileName) { + if (!fileName || !fileName[0]) { wchar_t initDir[MAX_PATH] = {0}; if (!global::wstrFileName.empty()) { global::wstrFileName.substr(0, global::wstrFileName.rfind(L'\\')).copy(initDir, MAX_PATH - 1); @@ -317,8 +287,7 @@ void openFile(bool newFile, const wchar_t *fileName) { fileName = wcFileName; } - HANDLE hFile = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE hFile = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { util::messageBox(global::hWnd, global::hInst, L"Open failed.", L"Error", MB_ICONWARNING); return; @@ -326,15 +295,13 @@ void openFile(bool newFile, const wchar_t *fileName) { DWORD fileSize = GetFileSize(hFile, NULL), readLen; if (fileSize >= 65536 * 2) { - util::messageBox(global::hWnd, global::hInst, L"This file is too large.", L"Error", - MB_ICONWARNING); + util::messageBox(global::hWnd, global::hInst, L"This file is too large.", L"Error", MB_ICONWARNING); return; } char *fileBuf = (char *)malloc(sizeof(char) * fileSize); if (!fileBuf) { - util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", - MB_ICONWARNING); + util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); return; } @@ -348,8 +315,7 @@ void openFile(bool newFile, const wchar_t *fileName) { wchar_t *wcFileBuf = (wchar_t *)calloc(length + 1, sizeof(wchar_t)); if (!wcFileBuf) { free(fileBuf); - util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", - MB_ICONWARNING); + util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); return; } @@ -376,8 +342,7 @@ bool saveFile(bool isOverwrite) { if (!isOverwrite || global::wstrFileName.empty()) { wchar_t initDir[MAX_PATH] = {0}; if (!global::wstrFileName.empty()) { - global::wstrFileName.substr(global::wstrFileName.rfind(L'\\') + 1) - .copy(wcFileName, MAX_PATH - 1); + global::wstrFileName.substr(global::wstrFileName.rfind(L'\\') + 1).copy(wcFileName, MAX_PATH - 1); global::wstrFileName.substr(0, global::wstrFileName.rfind(L'\\')).copy(initDir, MAX_PATH - 1); } OPENFILENAMEW ofn; @@ -398,8 +363,7 @@ bool saveFile(bool isOverwrite) { int editorSize = GetWindowTextLengthW(global::hEditor) + 1; wchar_t *wcEditor = (wchar_t *)malloc(sizeof(wchar_t) * editorSize); if (!wcEditor) { - util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", - MB_ICONWARNING); + util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); return false; } GetWindowTextW(global::hEditor, wcEditor, editorSize); @@ -410,14 +374,13 @@ bool saveFile(bool isOverwrite) { int length = WideCharToMultiByte(CP_UTF8, 0, converted.c_str(), -1, NULL, 0, NULL, NULL); char *szEditor = (char *)malloc(sizeof(char) * length); if (!szEditor) { - util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", - MB_ICONWARNING); + util::messageBox(global::hWnd, global::hInst, L"Memory allocation failed.", L"Internal Error", MB_ICONWARNING); return false; } WideCharToMultiByte(CP_UTF8, 0, converted.c_str(), -1, szEditor, length, NULL, NULL); - HANDLE hFile = CreateFileW(wcFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE hFile = + CreateFileW(wcFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { util::messageBox(global::hWnd, global::hInst, L"Open failed.", L"Error", MB_ICONWARNING); return false; diff --git a/util.cpp b/util.cpp index 4b03280..63e9f2e 100644 --- a/util.cpp +++ b/util.cpp @@ -2,21 +2,17 @@ namespace util { #ifdef UNDER_CE -int messageBox(HWND hWnd, HINSTANCE hInst, const wchar_t *lpText, const wchar_t *lpCaption, - unsigned uType) { +int messageBox(HWND hWnd, HINSTANCE hInst, const wchar_t *lpText, const wchar_t *lpCaption, unsigned uType) { UNREFERENCED_PARAMETER(hInst); return MessageBoxW(hWnd, lpText, lpCaption, uType); } #else // The function pointer type for TaskDialog API. -typedef HRESULT(__stdcall *TaskDialog_t)(HWND hwndOwner, HINSTANCE hInstance, - const wchar_t *pszWindowTitle, - const wchar_t *pszMainInstruction, - const wchar_t *pszContent, int dwCommonButtons, - const wchar_t *pszIcon, int *pnButton); - -int messageBox(HWND hWnd, HINSTANCE hInst, const wchar_t *lpText, const wchar_t *lpCaption, - unsigned uType) { +typedef HRESULT(__stdcall *TaskDialog_t)(HWND hwndOwner, HINSTANCE hInstance, const wchar_t *pszWindowTitle, + const wchar_t *pszMainInstruction, const wchar_t *pszContent, + int dwCommonButtons, const wchar_t *pszIcon, int *pnButton); + +int messageBox(HWND hWnd, HINSTANCE hInst, const wchar_t *lpText, const wchar_t *lpCaption, unsigned uType) { // Tests whether uType uses some features that TaskDialog doesn't support. if (uType & ~(MB_ICONMASK | MB_TYPEMASK)) goto mbfallback; @@ -80,8 +76,7 @@ int messageBox(HWND hWnd, HINSTANCE hInst, const wchar_t *lpText, const wchar_t } #endif -bool parseHex(HWND hWnd, HINSTANCE hInst, const wchar_t *hexInput, unsigned char **dest, - int *length) { +bool parseHex(HWND hWnd, HINSTANCE hInst, const wchar_t *hexInput, unsigned char **dest, int *length) { wchar_t hex[2]; std::string tmp; int hexLen = 0, i; @@ -93,30 +88,15 @@ bool parseHex(HWND hWnd, HINSTANCE hInst, const wchar_t *hexInput, unsigned char *dest = NULL; return false; } - if (hexInput[i] > L'Z') { // Aligns to the upper case. - hex[hexLen++] = hexInput[i] - (L'a' - L'A'); - } else { - hex[hexLen++] = hexInput[i]; - } + // Aligns to the upper case. + hex[hexLen++] = hexInput[i] > L'Z' ? hexInput[i] - (L'a' - L'A') : hexInput[i]; } else if (iswspace(hexInput[i]) || !hexInput[i]) { if (hexLen == 1) { - if (hex[0] < L'A') { - tmp += (char)(hex[0] - L'0'); - } else { - tmp += (char)(hex[0] - L'A' + 10); - } + tmp += hex[0] < L'A' ? (char)(hex[0] - L'0') : (char)(hex[0] - L'A' + 10); hexLen = 0; } else if (hexLen == 2) { - if (hex[0] < L'A') { - tmp += (char)((hex[0] - L'0') << 4); - } else { - tmp += (char)((hex[0] - L'A' + 10) << 4); - } - if (hex[1] < L'A') { - tmp[tmp.size() - 1] += hex[1] - L'0'; - } else { - tmp[tmp.size() - 1] += hex[1] - L'A' + 10; - } + tmp += hex[0] < L'A' ? (char)((hex[0] - L'0') << 4) : (char)((hex[0] - L'A' + 10) << 4); + tmp[tmp.size() - 1] += hex[1] < L'A' ? hex[1] - L'0' : hex[1] - L'A' + 10; hexLen = 0; } } else { @@ -149,18 +129,8 @@ bool parseHex(HWND hWnd, HINSTANCE hInst, const wchar_t *hexInput, unsigned char void toHex(unsigned char num, wchar_t *dest) { unsigned char high = num >> 4, low = num & 0xF; - if (high < 10) { - dest[0] = L'0' + high; - } else { - dest[0] = L'A' + (high - 10); - } - - if (low < 10) { - dest[1] = L'0' + low; - } else { - dest[1] = L'A' + (low - 10); - } - + dest[0] = high < 10 ? L'0' + high : L'A' + (high - 10); + dest[1] = low < 10 ? L'0' + low : L'A' + (low - 10); dest[2] = L' '; dest[3] = 0; } @@ -170,15 +140,7 @@ enum newline_t convertCRLF(std::wstring &target, enum newline_t newLine) { std::wstring::iterator iterEnd = target.end(); std::wstring temp; size_t CRs = 0, LFs = 0, CRLFs = 0; - - const wchar_t *nl; - if (newLine == NEWLINE_LF) { - nl = L"\n"; - } else if (newLine == NEWLINE_CR) { - nl = L"\r"; - } else { - nl = L"\r\n"; - } + const wchar_t *nl = newLine == NEWLINE_LF ? L"\n" : newLine == NEWLINE_CR ? L"\r" : L"\r\n"; if (0 < target.size()) { wchar_t bNextChar = *iter++; @@ -215,8 +177,6 @@ enum newline_t convertCRLF(std::wstring &target, enum newline_t newLine) { target = temp; - return LFs > CRLFs && LFs >= CRs ? NEWLINE_LF - : CRs > LFs && CRs > CRLFs ? NEWLINE_CR - : NEWLINE_CRLF; + return LFs > CRLFs && LFs >= CRs ? NEWLINE_LF : CRs > LFs && CRs > CRLFs ? NEWLINE_CR : NEWLINE_CRLF; } } // namespace util diff --git a/util.hpp b/util.hpp index af822cc..4ae71a1 100644 --- a/util.hpp +++ b/util.hpp @@ -12,25 +12,21 @@ namespace util { enum newline_t { NEWLINE_CRLF, NEWLINE_LF, NEWLINE_CR }; static inline bool isHex(wchar_t chr) { - return (chr >= L'0' && chr <= L'9') || (chr >= L'A' && chr <= L'F') || - (chr >= L'a' && chr <= L'f'); + return (chr >= L'0' && chr <= L'9') || (chr >= L'A' && chr <= L'F') || (chr >= L'a' && chr <= L'f'); } // Shows a message box with TaskDialog or MessageBoxW. // -// When the system this program is running on supports TaskDialog, and no features that are -// MessageBoxW specific (MB_ABORTRETRYIGNORE, MB_CANCELTRYCONTINUE, MB_HELP, default selection, etc) -// is used, this function uses TaskDialog. In other cases, uses MessageBoxW. -// This function substitutes MB_ICONQUESTION with MB_ICONINFORMATION on TaskDialog, as it doesn't -// support it. -int messageBox(HWND hWnd, HINSTANCE hInst, const wchar_t *lpText, const wchar_t *lpCaption, - unsigned uType); +// When the system this program is running on supports TaskDialog, and no features that are MessageBoxW specific +// (MB_ABORTRETRYIGNORE, MB_CANCELTRYCONTINUE, MB_HELP, default selection, etc) is used, this function uses TaskDialog. +// In other cases, uses MessageBoxW. This function substitutes MB_ICONQUESTION with MB_ICONINFORMATION on TaskDialog, as +// it doesn't support it. +int messageBox(HWND hWnd, HINSTANCE hInst, const wchar_t *lpText, const wchar_t *lpCaption, unsigned uType); // Converts a hex string to a byte sequence. This function allocates `dest` unless it is NULL. // It's your responsibility to `free()` it. // Return value of true indicates success, otherwise indicates failure. -bool parseHex(HWND hWnd, HINSTANCE hInst, const wchar_t *hexInput, unsigned char **dest, - int *length); +bool parseHex(HWND hWnd, HINSTANCE hInst, const wchar_t *hexInput, unsigned char **dest, int *length); // Converts a byte to a hex string. `dest` must have at least 4 elements. void toHex(unsigned char num, wchar_t *dest); diff --git a/vs2005proj/brainfuck_vs2005.vcproj b/vs2005proj/brainfuck_vs2005.vcproj index b1bc431..ca36623 100644 --- a/vs2005proj/brainfuck_vs2005.vcproj +++ b/vs2005proj/brainfuck_vs2005.vcproj @@ -194,14 +194,38 @@ RelativePath="..\bf.cpp" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +