diff --git a/src/drivers/win/luaconsole.cpp b/src/drivers/win/luaconsole.cpp new file mode 100644 index 00000000..062f14db --- /dev/null +++ b/src/drivers/win/luaconsole.cpp @@ -0,0 +1,207 @@ + +#include +#include +#include "main.h" +#include "resource.h" +#include "../../fceulua.h" + +extern HWND hAppWnd; + +HWND LuaConsoleHWnd = NULL; + +void PrintToWindowConsole(int hDlgAsInt, const char* str) +{ + HWND hDlg = (HWND)hDlgAsInt; + HWND hConsole = GetDlgItem(hDlg, IDC_LUACONSOLE); + + int length = GetWindowTextLength(hConsole); + if(length >= 250000) + { + // discard first half of text if it's getting too long + SendMessage(hConsole, EM_SETSEL, 0, length/2); + SendMessage(hConsole, EM_REPLACESEL, false, (LPARAM)""); + length = GetWindowTextLength(hConsole); + } + SendMessage(hConsole, EM_SETSEL, length, length); + + //LuaPerWindowInfo& info = LuaWindowInfo[hDlg]; + + { + SendMessage(hConsole, EM_REPLACESEL, false, (LPARAM)str); + } +} + +void WinLuaOnStart(int hDlgAsInt) +{ + HWND hDlg = (HWND)hDlgAsInt; + //LuaPerWindowInfo& info = LuaWindowInfo[hDlg]; + //info.started = true; + EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_LUABROWSE), false); // disable browse while running because it misbehaves if clicked in a frameadvance loop + EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_LUASTOP), true); + SetWindowText(GetDlgItem(hDlg, IDC_BUTTON_LUARUN), "Restart"); + SetWindowText(GetDlgItem(hDlg, IDC_LUACONSOLE), ""); // clear the console +// Show_Genesis_Screen(HWnd); // otherwise we might never show the first thing the script draws +} + +void WinLuaOnStop(int hDlgAsInt) +{ + HWND hDlg = (HWND)hDlgAsInt; + //LuaPerWindowInfo& info = LuaWindowInfo[hDlg]; + + HWND prevWindow = GetActiveWindow(); + SetActiveWindow(hDlg); // bring to front among other script/secondary windows, since a stopped script will have some message for the user that would be easier to miss otherwise + if(prevWindow == hAppWnd) SetActiveWindow(prevWindow); + + //info.started = false; + EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_LUABROWSE), true); + EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_LUASTOP), false); + SetWindowText(GetDlgItem(hDlg, IDC_BUTTON_LUARUN), "Run"); +// if(statusOK) +// Show_Genesis_Screen(MainWindow->getHWnd()); // otherwise we might never show the last thing the script draws + //if(info.closeOnStop) + // PostMessage(hDlg, WM_CLOSE, 0, 0); +} + +INT_PTR CALLBACK DlgLuaScriptDialog(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + RECT r; + RECT r2; + int dx1, dy1, dx2, dy2; + + switch (msg) { + + case WM_INITDIALOG: + { + // disable resizing + LONG wndStyle = GetWindowLong(hDlg, GWL_STYLE); + wndStyle &= ~WS_THICKFRAME; + SetWindowLong(hDlg, GWL_STYLE, wndStyle); + + // remove the 30000 character limit from the console control + SendMessage(GetDlgItem(hDlg, IDC_LUACONSOLE),EM_LIMITTEXT,0,0); + + GetWindowRect(hAppWnd, &r); + dx1 = (r.right - r.left) / 2; + dy1 = (r.bottom - r.top) / 2; + + GetWindowRect(hDlg, &r2); + dx2 = (r2.right - r2.left) / 2; + dy2 = (r2.bottom - r2.top) / 2; + + int windowIndex = 0;//std::find(LuaScriptHWnds.begin(), LuaScriptHWnds.end(), hDlg) - LuaScriptHWnds.begin(); + int staggerOffset = windowIndex * 24; + r.left += staggerOffset; + r.right += staggerOffset; + r.top += staggerOffset; + r.bottom += staggerOffset; + + // push it away from the main window if we can + const int width = (r.right-r.left); + const int width2 = (r2.right-r2.left); + if(r.left+width2 + width < GetSystemMetrics(SM_CXSCREEN)) + { + r.right += width; + r.left += width; + } + else if((int)r.left - (int)width2 > 0) + { + r.right -= width2; + r.left -= width2; + } + + SetWindowPos(hDlg, NULL, r.left, r.top, NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW); + + DragAcceptFiles(hDlg, true); + SetDlgItemText(hDlg, IDC_EDIT_LUAPATH, FCEU_GetLuaScriptName()); + return true; + } break; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + case IDCANCEL: { + EndDialog(hDlg, true); // goto case WM_CLOSE; + } break; + + case IDC_BUTTON_LUARUN: + { + char filename[MAX_PATH]; + GetDlgItemText(hDlg, IDC_EDIT_LUAPATH, filename, MAX_PATH); + FCEU_LoadLuaCode(filename); + } break; + + case IDC_BUTTON_LUASTOP: + { + FCEU_LuaStop(); + } break; + + case IDC_BUTTON_LUAEDIT: + { + char Str_Tmp [1024]; // shadow added because the global one is unreliable + SendDlgItemMessage(hDlg,IDC_EDIT_LUAPATH,WM_GETTEXT,(WPARAM)512,(LPARAM)Str_Tmp); + // tell the OS to open the file with its associated editor, + // without blocking on it or leaving a command window open. + ShellExecute(NULL, "edit", Str_Tmp, NULL, NULL, SW_SHOWNORMAL); + } break; + + case IDC_BUTTON_LUABROWSE: + { + OPENFILENAME ofn; + char szFileName[MAX_PATH]; + szFileName[0] = '\0'; + ZeroMemory( (LPVOID)&ofn, sizeof(OPENFILENAME) ); + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = hAppWnd; + ofn.lpstrFilter = "Lua scripts (*.lua)\0*.lua\0All files (*.*)\0*.*\0\0"; + ofn.lpstrFile = szFileName; + ofn.lpstrDefExt = "lua"; + ofn.nMaxFile = MAX_PATH; + ofn.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST; // hide previously-ignored read-only checkbox (the real read-only box is in the open-movie dialog itself) + std::string initdir = FCEU_GetPath(FCEUMKF_LUA); + ofn.lpstrInitialDir=initdir.c_str(); + if(GetOpenFileName( &ofn )) + { + SetWindowText(GetDlgItem(hDlg, IDC_EDIT_LUAPATH), szFileName); + } + return true; + } break; + + case IDC_EDIT_LUAPATH: + { + char filename[MAX_PATH]; + GetDlgItemText(hDlg, IDC_EDIT_LUAPATH, filename, MAX_PATH); + FILE* file = fopen(filename, "rb"); + EnableWindow(GetDlgItem(hDlg, IDOK), file != NULL); + if(file) + fclose(file); + } break; + } + break; + + case WM_CLOSE: { + FCEU_LuaStop(); + DragAcceptFiles(hDlg, FALSE); + LuaConsoleHWnd = NULL; + } break; + + case WM_DROPFILES: { + HDROP hDrop; + //UINT fileNo; + UINT fileCount; + char filename[MAX_PATH]; + + hDrop = (HDROP)wParam; + fileCount = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); + if (fileCount > 0) { + DragQueryFile(hDrop, 0, filename, sizeof(filename)); + SetWindowText(GetDlgItem(hDlg, IDC_EDIT_LUAPATH), filename); + } + DragFinish(hDrop); + return true; + } break; + + } + + return false; + +} diff --git a/src/drivers/win/res.rc b/src/drivers/win/res.rc index 5e9b82ea..05913466 100644 --- a/src/drivers/win/res.rc +++ b/src/drivers/win/res.rc @@ -7,7 +7,8 @@ // // Generated from the TEXTINCLUDE 2 resource. // -#include "afxres.h" +#include "afxres.h" + ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS @@ -74,9 +75,8 @@ BEGIN POPUP "&Lua" BEGIN MENUITEM "&Recent", MENU_LUA_RECENT - MENUITEM "Run &Lua Script...", ID_FILE_RUNLUASCRIPT - MENUITEM "&Stop Lua Script", ID_FILE_STOPLUASCRIPT - MENUITEM "&Reload Lua Script", ID_FILE_LUA_RELOADLUASCRIPT + MENUITEM "&New Lua Script Window...", ID_FILE_OPENLUAWINDOW + MENUITEM "&Close All Script Windows", ID_FILE_CLOSELUAWINDOWS END MENUITEM "&Screenshot", ID_FILE_SCREENSHOT MENUITEM SEPARATOR @@ -1446,15 +1446,19 @@ BEGIN LTEXT "(Hex)",IDC_STATIC,226,34,19,8 END -IDD_LUA_ADD DIALOGEX 0, 0, 186, 66 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Load Lua Script..." +IDD_LUA DIALOGEX 0, 0, 270, 150 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Lua Script" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - DEFPUSHBUTTON "Load && Run",1,70,44,50,14 - PUSHBUTTON "Cancel",2,129,44,50,14 - EDITTEXT 1096,7,7,116,14,ES_AUTOHSCROLL | ES_READONLY,WS_EX_NOPARENTNOTIFY - PUSHBUTTON "Browse...",1359,129,7,50,14 + PUSHBUTTON "Browse...",IDC_BUTTON_LUABROWSE,7,31,48,16 + PUSHBUTTON "Run",IDC_BUTTON_LUARUN,213,31,50,16 + PUSHBUTTON "Stop",IDC_BUTTON_LUASTOP,160,31,50,16 + EDITTEXT IDC_EDIT_LUAPATH,7,16,256,14,ES_AUTOHSCROLL + EDITTEXT IDC_LUACONSOLE,7,61,256,81,ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY + LTEXT "Output Console",IDC_STATIC,7,51,51,8 + LTEXT "Script File",IDC_STATIC,7,7,32,8 + PUSHBUTTON "Edit",IDC_BUTTON_LUAEDIT,58,31,46,16 END VIDEOCONFIG DIALOGEX 65520, 76, 384, 296 @@ -1787,14 +1791,6 @@ BEGIN BOTTOMMARGIN, 171 END - "IDD_LUA_ADD", DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 179 - TOPMARGIN, 7 - BOTTOMMARGIN, 59 - END - "VIDEOCONFIG", DIALOG BEGIN LEFTMARGIN, 10 @@ -1926,7 +1922,8 @@ IDB_TE_ARROW BITMAP "res/te_arrow.bmp" // // Generated from the TEXTINCLUDE 3 resource. // - + + ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED diff --git a/src/drivers/win/resource.h b/src/drivers/win/resource.h index 5e37291f..ab1ecaea 100644 --- a/src/drivers/win/resource.h +++ b/src/drivers/win/resource.h @@ -153,6 +153,7 @@ #define IDD_EDITWATCH 156 #define IDD_PROMPT 157 #define IDR_RWACCELERATOR 158 +#define IDD_LUA 159 #define MENU_RESET 200 #define BUTTON_ROMS 200 #define TXT_PAD1 200 @@ -456,6 +457,12 @@ #define IDC_PROMPT_TEXT 1245 #define IDC_PROMPT_TEXT2 1246 #define IDC_PROMPT_EDIT 1247 +#define IDC_BUTTON_LUABROWSE 1248 +#define IDC_BUTTON_LUARUN 1249 +#define IDC_BUTTON_LUASTOP 1250 +#define IDC_EDIT_LUAPATH 1251 +#define IDC_LUACONSOLE 1252 +#define IDC_BUTTON_LUAEDIT 1253 #define MENU_NETWORK 40040 #define MENU_PALETTE 40041 #define MENU_SOUND 40042 @@ -605,8 +612,8 @@ #define ID_MEMWVIEW_FILE_CLOSE 40217 #define ID_FILE_CLOSE40218 40218 #define MENU_BASIC_BOT2 40220 -#define ID_FILE_RUNLUASCRIPT 40229 -#define ID_FILE_STOPLUASCRIPT 40230 +#define ID_FILE_OPENLUAWINDOW 40229 +#define ID_FILE_CLOSELUAWINDOWS 40230 #define ID_CONFIG_DISPLAY 40231 #define ID_DISPLAY_INPUTDISPLAY 40232 #define ID_DISPLAY_LAGCOUNTER 40233 @@ -764,9 +771,9 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 159 +#define _APS_NEXT_RESOURCE_VALUE 160 #define _APS_NEXT_COMMAND_VALUE 40401 -#define _APS_NEXT_CONTROL_VALUE 1248 +#define _APS_NEXT_CONTROL_VALUE 1254 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/src/drivers/win/window.cpp b/src/drivers/win/window.cpp index c7205865..76caf6b5 100644 --- a/src/drivers/win/window.cpp +++ b/src/drivers/win/window.cpp @@ -75,9 +75,9 @@ using namespace std; //----Context Menu - Some dynamically added menu items -#define FCEUX_CONTEXT_UNHIDEMENU 60000 -#define FCEUX_CONTEXT_LOADLASTLUA 60001 -#define FCEUX_CONTEXT_STOPLUA 60002 +#define FCEUX_CONTEXT_UNHIDEMENU 60000 +#define FCEUX_CONTEXT_LOADLASTLUA 60001 +#define FCEUX_CONTEXT_CLOSELUAWINDOWS 60002 //******************************************************************************** //Globals @@ -99,7 +99,6 @@ extern FCEUGI *GameInfo; extern int EnableAutosave; extern bool frameAdvanceLagSkip; extern bool turbo; -extern int luaRunning; extern bool movie_readonly; extern bool AutoSS; //flag for whether an auto-save has been made extern int newppu; @@ -143,6 +142,10 @@ char *recent_files[] = { 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }; const unsigned int MENU_FIRST_RECENT_FILE = 600; const unsigned int MAX_NUMBER_OF_RECENT_FILES = sizeof(recent_files)/sizeof(*recent_files); +//Lua Console -------------------------------------------- +extern HWND LuaConsoleHWnd; +extern INT_PTR CALLBACK DlgLuaScriptDialog(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + //Recent Lua Menu ---------------------------------------- char *recent_lua[] = {0,0,0,0,0}; const unsigned int LUA_FIRST_RECENT_FILE = 50000; @@ -371,6 +374,7 @@ void UpdateCheckedMenuItems() } //File Maneu CheckMenuItem(fceumenu, ID_FILE_MOVIE_TOGGLEREAD, movie_readonly ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(fceumenu, ID_FILE_OPENLUAWINDOW, LuaConsoleHWnd ? MF_CHECKED : MF_UNCHECKED); //NES Menu CheckMenuItem(fceumenu, ID_NES_PAUSE, EmulationPaused ? MF_CHECKED : MF_UNCHECKED); @@ -507,7 +511,7 @@ void UpdateContextMenuItems(HMENU context, int whichContext) EnableMenuItem(context,FCEUX_CONTEXT_RECENTROM1,MF_BYCOMMAND | MF_GRAYED); //Add Lua separator if either lua condition is true (yeah, a little ugly but it works) - if (recent_lua[0] || luaRunning) + if (recent_lua[0] || FCEU_LuaRunning()) InsertMenu(context, 0xFFFF, MF_SEPARATOR, 0, ""); //If a recent lua file exists, add Load Last Lua @@ -515,8 +519,8 @@ void UpdateContextMenuItems(HMENU context, int whichContext) InsertMenu(context, 0xFFFF, MF_BYCOMMAND, FCEUX_CONTEXT_LOADLASTLUA, "Load last Lua"); //If lua is loaded, add a stop lua item - if (luaRunning) - InsertMenu(context, 0xFFFF, MF_BYCOMMAND, FCEUX_CONTEXT_STOPLUA, "Stop Lua script"); + if (FCEU_LuaRunning()) + InsertMenu(context, 0xFFFF, MF_BYCOMMAND, FCEUX_CONTEXT_CLOSELUAWINDOWS, "Close All Script Windows"); //If menu is hidden, add an Unhide menu option if (tog) @@ -1500,15 +1504,31 @@ LRESULT FAR PASCAL AppWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam) break; //Lua submenu - case ID_FILE_RUNLUASCRIPT: - FCEUD_LuaRunFrom(); + case ID_FILE_OPENLUAWINDOW: + if(!LuaConsoleHWnd) + LuaConsoleHWnd = CreateDialog(fceu_hInstance, MAKEINTRESOURCE(IDD_LUA), hWnd, (DLGPROC) DlgLuaScriptDialog); + else + SetForegroundWindow(LuaConsoleHWnd); break; - case FCEUX_CONTEXT_STOPLUA: - case ID_FILE_STOPLUASCRIPT: - FCEU_LuaStop(); + case FCEUX_CONTEXT_CLOSELUAWINDOWS: + case ID_FILE_CLOSELUAWINDOWS: + if(LuaConsoleHWnd) + PostMessage(LuaConsoleHWnd, WM_CLOSE, 0, 0); break; - case ID_FILE_LUA_RELOADLUASCRIPT: - FCEU_ReloadLuaCode(); + //Recent Lua 1 + case FCEUX_CONTEXT_LOADLASTLUA: + if(recent_lua[0]) + { + if (!FCEU_LoadLuaCode(recent_lua[0])) + { + int result = MessageBox(hWnd,"Remove from list?", "Could Not Open Recent File", MB_YESNO); + if (result == IDYES) + { + RemoveRecentItem(0, recent_lua, MAX_NUMBER_OF_LUA_RECENT_FILES); + UpdateLuaRMenu(recentluamenu, recent_lua, MENU_LUA_RECENT, LUA_FIRST_RECENT_FILE); + } + } + } break; case MENU_EXIT: @@ -1864,22 +1884,6 @@ LRESULT FAR PASCAL AppWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam) } break; - //Recent Lua 1 - case FCEUX_CONTEXT_LOADLASTLUA: - if(recent_lua[0]) - { - if (!FCEU_LoadLuaCode(recent_lua[0])) - { - int result = MessageBox(hWnd,"Remove from list?", "Could Not Open Recent File", MB_YESNO); - if (result == IDYES) - { - RemoveRecentItem(0, recent_lua, MAX_NUMBER_OF_LUA_RECENT_FILES); - UpdateLuaRMenu(recentluamenu, recent_lua, MENU_LUA_RECENT, LUA_FIRST_RECENT_FILE); - } - } - } - break; - //Recent Movie 1 case FCEUX_CONTEXT_LOADLASTMOVIE: if(recent_movie[0]) @@ -2052,7 +2056,7 @@ adelikat: Outsourced this to a remappable hotkey EnableMenuItem(fceumenu,MENU_VIEWSAVESLOTS,MF_BYCOMMAND | (FCEU_IsValidUI(FCEUI_VIEWSLOTS)?MF_ENABLED:MF_GRAYED)); EnableMenuItem(fceumenu,MENU_STOP_AVI,MF_BYCOMMAND | (FCEUI_AviIsRecording()?MF_ENABLED:MF_GRAYED)); EnableMenuItem(fceumenu,MENU_STOP_WAV,MF_BYCOMMAND | (loggingSound?MF_ENABLED:MF_GRAYED)); - EnableMenuItem(fceumenu,ID_FILE_STOPLUASCRIPT,MF_BYCOMMAND | (luaRunning?MF_ENABLED:MF_GRAYED)); + EnableMenuItem(fceumenu,ID_FILE_CLOSELUAWINDOWS,MF_BYCOMMAND | (LuaConsoleHWnd?MF_ENABLED:MF_GRAYED)); CheckMenuItem(fceumenu, ID_NEWPPU, newppu ? MF_CHECKED : MF_UNCHECKED); CheckMenuItem(fceumenu, ID_OLDPPU, !newppu ? MF_CHECKED : MF_UNCHECKED); @@ -2376,102 +2380,6 @@ bool FCEUD_PauseAfterPlayback() return pauseAfterPlayback!=0; } -INT_PTR CALLBACK DlgLuaScriptDialog(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { - - static int *success; - - switch (msg) { - case WM_INITDIALOG: - { - - // Nothing very useful to do - success = (int*)lParam; - return TRUE; - } - break; - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDOK: - { - char filename[MAX_PATH]; - GetDlgItemText(hDlg, 1096, filename, MAX_PATH); - if (FCEU_LoadLuaCode(filename)) { - *success = 1; - // For user's convenience, don't close dialog unless we're done. - // Users who make syntax errors and fix/reload will thank us. - EndDialog(hDlg, 1); - } else { - //MessageBox(hDlg, "Couldn't load script.", "Oops", MB_OK); // XXX better if errors are displayed by the Lua code. - *success = 0; - } - return TRUE; - } - case IDCANCEL: - { - EndDialog(hDlg, 0); - return TRUE; - } - case 1359: - { - OPENFILENAME ofn; - char szFileName[MAX_PATH]; - szFileName[0] = '\0'; - ZeroMemory( (LPVOID)&ofn, sizeof(OPENFILENAME) ); - ofn.lStructSize = sizeof(OPENFILENAME); - ofn.hwndOwner = hDlg; - ofn.lpstrFilter = "Lua scripts\0*.lua\0All files\0*.*\0\0"; - ofn.lpstrFile = szFileName; - ofn.lpstrDefExt = "lua"; - ofn.nMaxFile = MAX_PATH; - ofn.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST; // hide previously-ignored read-only checkbox (the real read-only box is in the open-movie dialog itself) - if(GetOpenFileName( &ofn )) - { - SetWindowText(GetDlgItem(hDlg, 1096), szFileName); - } - //SetCurrentDirectory(movieDirectory); - return TRUE; - } - } - - - } - //char message[1024]; -// sprintf(message, "Unkonwn command %d,%d",msg,wParam); - //MessageBox(hDlg, message, TEXT("Range Error"), MB_OK); - -// printf("Unknown entry %d,%d,%d\n",msg,wParam,lParam); - // All else, fall off - return FALSE; - -} - -void FCEUD_LuaRunFrom(void) -{ - int success = 0; - - //mbg 8/2/08 - i decided i didnt like this dialog box. so for now we are just going to run the script directly - //DialogBoxParam(fceu_hInstance, "IDD_LUA_ADD", hAppWnd, DlgLuaScriptDialog,(LPARAM) &success); - - OPENFILENAME ofn; - char szFileName[MAX_PATH]; - szFileName[0] = '\0'; - ZeroMemory( (LPVOID)&ofn, sizeof(OPENFILENAME) ); - ofn.lStructSize = sizeof(OPENFILENAME); - ofn.hwndOwner = hAppWnd; - ofn.lpstrFilter = "Lua scripts (*.lua)\0*.lua\0All files (*.*)\0*.*\0\0"; - ofn.lpstrFile = szFileName; - ofn.lpstrDefExt = "lua"; - ofn.nMaxFile = MAX_PATH; - ofn.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST; // hide previously-ignored read-only checkbox (the real read-only box is in the open-movie dialog itself) - std::string initdir = FCEU_GetPath(FCEUMKF_LUA); - ofn.lpstrInitialDir=initdir.c_str(); - if(GetOpenFileName( &ofn )) - { - AddRecentLuaFile(szFileName); - FCEU_LoadLuaCode(szFileName); - } -} - void ChangeContextMenuItemText(int menuitem, string text, HMENU menu) { MENUITEMINFO moo; @@ -2584,11 +2492,6 @@ void UpdateMenuHotkeys() combo = GetKeyComboName(FCEUD_CommandMapping[EMUCMD_AVI_STOP]); combined = "&Stop AVI\t" + combo; ChangeMenuItemText(MENU_STOP_AVI, combined); - - //Reload Lua Script - combo = GetKeyComboName(FCEUD_CommandMapping[EMUCMD_SCRIPT_RELOAD]); - combined = "&Reload Lua Script\t" + combo; - ChangeMenuItemText(ID_FILE_LUA_RELOADLUASCRIPT, combined); //-------------------------------NES---------------------------------------- //Reset diff --git a/src/fceulua.h b/src/fceulua.h index 6f8ee0fd..4d53aad4 100644 --- a/src/fceulua.h +++ b/src/fceulua.h @@ -40,6 +40,8 @@ int FCEU_LuaRerecordCountSkip(); void FCEU_LuaGui(uint8 *XBuf); void FCEU_LuaUpdatePalette(); +char* FCEU_GetLuaScriptName(); + // And some interesting REVERSE declarations! char *FCEU_GetFreezeFilename(int slot); diff --git a/src/lua-engine.cpp b/src/lua-engine.cpp index c252ac3f..6cf3b776 100644 --- a/src/lua-engine.cpp +++ b/src/lua-engine.cpp @@ -93,6 +93,18 @@ struct LuaSaveState { } }; +static void(*info_print)(int uid, const char* str); +static void(*info_onstart)(int uid); +static void(*info_onstop)(int uid); +static int info_uid; +#ifdef WIN32 +extern HWND LuaConsoleHWnd; +extern INT_PTR CALLBACK DlgLuaScriptDialog(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); +extern void PrintToWindowConsole(int hDlgAsInt, const char* str); +extern void WinLuaOnStart(int hDlgAsInt); +extern void WinLuaOnStop(int hDlgAsInt); +#endif + static lua_State *L; // Are we running any code right now? @@ -148,6 +160,14 @@ static const char *button_mappings[] = { "A", "B", "select", "start", "up", "down", "left", "right" }; +#ifdef _MSC_VER + #define snprintf _snprintf + #define vscprintf _vscprintf +#else + #define stricmp strcasecmp + #define strnicmp strncasecmp +#endif + static const char* luaCallIDStrings [] = { "CALL_BEFOREEMULATION", @@ -172,6 +192,9 @@ static const char* luaMemHookTypeStrings [] = //make sure we have the right number of strings CTASSERT(sizeof(luaMemHookTypeStrings)/sizeof(*luaMemHookTypeStrings) == LUAMEMHOOK_COUNT) +static char* rawToCString(lua_State* L, int idx=0); +static const char* toCString(lua_State* L, int idx=0); + /** * Resets emulator speed / pause states after script exit. */ @@ -435,6 +458,320 @@ static int memory_readbyterange(lua_State *L) { return 1; } +static inline bool isalphaorunderscore(char c) +{ + return isalpha(c) || c == '_'; +} + +static std::vector s_tableAddressStack; // prevents infinite recursion of a table within a table (when cycle is found, print something like table:parent) +static std::vector s_metacallStack; // prevents infinite recursion if something's __tostring returns another table that contains that something (when cycle is found, print the inner result without using __tostring) + +#define APPENDPRINT { int _n = snprintf(ptr, remaining, +#define END ); if(_n >= 0) { ptr += _n; remaining -= _n; } else { remaining = 0; } } +static void toCStringConverter(lua_State* L, int i, char*& ptr, int& remaining) +{ + if(remaining <= 0) + return; + + const char* str = ptr; // for debugging + + // if there is a __tostring metamethod then call it + int usedMeta = luaL_callmeta(L, i, "__tostring"); + if(usedMeta) + { + std::vector::const_iterator foundCycleIter = std::find(s_metacallStack.begin(), s_metacallStack.end(), lua_topointer(L,i)); + if(foundCycleIter != s_metacallStack.end()) + { + lua_pop(L, 1); + usedMeta = false; + } + else + { + s_metacallStack.push_back(lua_topointer(L,i)); + i = lua_gettop(L); + } + } + + switch(lua_type(L, i)) + { + case LUA_TNONE: break; + case LUA_TNIL: APPENDPRINT "nil" END break; + case LUA_TBOOLEAN: APPENDPRINT lua_toboolean(L,i) ? "true" : "false" END break; + case LUA_TSTRING: APPENDPRINT "%s",lua_tostring(L,i) END break; + case LUA_TNUMBER: APPENDPRINT "%.12Lg",lua_tonumber(L,i) END break; + case LUA_TFUNCTION: + /*if((L->base + i-1)->value.gc->cl.c.isC) + { + //lua_CFunction func = lua_tocfunction(L, i); + //std::map::iterator iter = s_cFuncInfoMap.find(func); + //if(iter == s_cFuncInfoMap.end()) + goto defcase; + //APPENDPRINT "function(%s)", iter->second END + } + else + { + APPENDPRINT "function(" END + Proto* p = (L->base + i-1)->value.gc->cl.l.p; + int numParams = p->numparams + (p->is_vararg?1:0); + for (int n=0; nnumparams; n++) + { + APPENDPRINT "%s", getstr(p->locvars[n].varname) END + if(n != numParams-1) + APPENDPRINT "," END + } + if(p->is_vararg) + APPENDPRINT "..." END + APPENDPRINT ")" END + }*/ + goto defcase; + break; +defcase:default: APPENDPRINT "%s:%p",luaL_typename(L,i),lua_topointer(L,i) END break; + case LUA_TTABLE: + { + // first make sure there's enough stack space + if(!lua_checkstack(L, 4)) + { + // note that even if lua_checkstack never returns false, + // that doesn't mean we didn't need to call it, + // because calling it retrieves stack space past LUA_MINSTACK + goto defcase; + } + + std::vector::const_iterator foundCycleIter = std::find(s_tableAddressStack.begin(), s_tableAddressStack.end(), lua_topointer(L,i)); + if(foundCycleIter != s_tableAddressStack.end()) + { + int parentNum = s_tableAddressStack.end() - foundCycleIter; + if(parentNum > 1) + APPENDPRINT "%s:parent^%d",luaL_typename(L,i),parentNum END + else + APPENDPRINT "%s:parent",luaL_typename(L,i) END + } + else + { + s_tableAddressStack.push_back(lua_topointer(L,i)); + struct Scope { ~Scope(){ s_tableAddressStack.pop_back(); } } scope; + + APPENDPRINT "{" END + + lua_pushnil(L); // first key + int keyIndex = lua_gettop(L); + int valueIndex = keyIndex + 1; + bool first = true; + bool skipKey = true; // true if we're still in the "array part" of the table + lua_Number arrayIndex = (lua_Number)0; + while(lua_next(L, i)) + { + if(first) + first = false; + else + APPENDPRINT ", " END + if(skipKey) + { + arrayIndex += (lua_Number)1; + bool keyIsNumber = (lua_type(L, keyIndex) == LUA_TNUMBER); + skipKey = keyIsNumber && (lua_tonumber(L, keyIndex) == arrayIndex); + } + if(!skipKey) + { + bool keyIsString = (lua_type(L, keyIndex) == LUA_TSTRING); + bool invalidLuaIdentifier = (!keyIsString || !isalphaorunderscore(*lua_tostring(L, keyIndex))); + if(invalidLuaIdentifier) + if(keyIsString) + APPENDPRINT "['" END + else + APPENDPRINT "[" END + + toCStringConverter(L, keyIndex, ptr, remaining); // key + + if(invalidLuaIdentifier) + if(keyIsString) + APPENDPRINT "']=" END + else + APPENDPRINT "]=" END + else + APPENDPRINT "=" END + } + + bool valueIsString = (lua_type(L, valueIndex) == LUA_TSTRING); + if(valueIsString) + APPENDPRINT "'" END + + toCStringConverter(L, valueIndex, ptr, remaining); // value + + if(valueIsString) + APPENDPRINT "'" END + + lua_pop(L, 1); + + if(remaining <= 0) + { + lua_settop(L, keyIndex-1); // stack might not be clean yet if we're breaking early + break; + } + } + APPENDPRINT "}" END + } + } break; + } + + if(usedMeta) + { + s_metacallStack.pop_back(); + lua_pop(L, 1); + } +} + +static const int s_tempStrMaxLen = 64 * 1024; +static char s_tempStr [s_tempStrMaxLen]; + +static char* rawToCString(lua_State* L, int idx) +{ + int a = idx>0 ? idx : 1; + int n = idx>0 ? idx : lua_gettop(L); + + char* ptr = s_tempStr; + *ptr = 0; + + int remaining = s_tempStrMaxLen; + for(int i = a; i <= n; i++) + { + toCStringConverter(L, i, ptr, remaining); + if(i != n) + APPENDPRINT " " END + } + + if(remaining < 3) + { + while(remaining < 6) + remaining++, ptr--; + APPENDPRINT "..." END + } + APPENDPRINT "\r\n" END + // the trailing newline is so print() can avoid having to do wasteful things to print its newline + // (string copying would be wasteful and calling info.print() twice can be extremely slow) + // at the cost of functions that don't want the newline needing to trim off the last two characters + // (which is a very fast operation and thus acceptable in this case) + + return s_tempStr; +} +#undef APPENDPRINT +#undef END + + +// replacement for luaB_tostring() that is able to show the contents of tables (and formats numbers better, and show function prototypes) +// can be called directly from lua via tostring(), assuming tostring hasn't been reassigned +static int tostring(lua_State *L) +{ + char* str = rawToCString(L); + str[strlen(str)-2] = 0; // hack: trim off the \r\n (which is there to simplify the print function's task) + lua_pushstring(L, str); + return 1; +} + +// like rawToCString, but will check if the global Lua function tostring() +// has been replaced with a custom function, and call that instead if so +static const char* toCString(lua_State* L, int idx) +{ + int a = idx>0 ? idx : 1; + int n = idx>0 ? idx : lua_gettop(L); + lua_getglobal(L, "tostring"); + lua_CFunction cf = lua_tocfunction(L,-1); + if(cf == tostring) // optimization: if using our own C tostring function, we can bypass the call through Lua and all the string object allocation that would entail + { + lua_pop(L,1); + return rawToCString(L, idx); + } + else // if the user overrided the tostring function, we have to actually call it and store the temporarily allocated string it returns + { + lua_pushstring(L, ""); + for (int i=a; i<=n; i++) { + lua_pushvalue(L, -2); // function to be called + lua_pushvalue(L, i); // value to print + lua_call(L, 1, 1); + if(lua_tostring(L, -1) == NULL) + luaL_error(L, LUA_QL("tostring") " must return a string to " LUA_QL("print")); + lua_pushstring(L, (il_G->mainthread]; + //LuaContextInfo& info = GetCurrentInfo(); + + if(info_print) + info_print(uid, str); + else + puts(str); + + //worry(L, 100); + return 0; +} + +// provides an easy way to copy a table from Lua +// (simple assignment only makes an alias, but sometimes an independent table is desired) +// currently this function only performs a shallow copy, +// but I think it should be changed to do a deep copy (possibly of configurable depth?) +// that maintains the internal table reference structure +static int copytable(lua_State *L) +{ + int origIndex = 1; // we only care about the first argument + int origType = lua_type(L, origIndex); + if(origType == LUA_TNIL) + { + lua_pushnil(L); + return 1; + } + if(origType != LUA_TTABLE) + { + luaL_typerror(L, 1, lua_typename(L, LUA_TTABLE)); + lua_pushnil(L); + return 1; + } + + lua_createtable(L, lua_objlen(L,1), 0); + int copyIndex = lua_gettop(L); + + lua_pushnil(L); // first key + int keyIndex = lua_gettop(L); + int valueIndex = keyIndex + 1; + + while(lua_next(L, origIndex)) + { + lua_pushvalue(L, keyIndex); + lua_pushvalue(L, valueIndex); + lua_rawset(L, copyIndex); // copytable[key] = value + lua_pop(L, 1); + } + + // copy the reference to the metatable as well, if any + if(lua_getmetatable(L, origIndex)) + lua_setmetatable(L, copyIndex); + + return 1; // return the new table +} + +// because print traditionally shows the address of tables, +// and the print function I provide instead shows the contents of tables, +// I also provide this function +// (otherwise there would be no way to see a table's address, AFAICT) +static int addressof(lua_State *L) +{ + const void* ptr = lua_topointer(L,-1); + lua_pushinteger(L, (lua_Integer)ptr); + return 1; +} + struct registerPointerMap { const char* registerName; @@ -875,7 +1212,7 @@ static int memory_registerexec(lua_State *L) //adelikat: table pulled from GENS. credz nitsuja! -#ifdef _WIN32 +#ifdef WIN32 const char* s_keyToName[256] = { NULL, @@ -989,7 +1326,7 @@ const char* s_keyToName[256] = static int input_get(lua_State *L) { lua_newtable(L); -#ifdef _WIN32 +#ifdef WIN32 // keyboard and mouse button status { extern int EnableBackgroundInput; @@ -2967,7 +3304,7 @@ bool luabitop_validate(lua_State *L) // originally named as luaopen_bit if (b != (UBits)1437217655L || BAD_SAR) { /* Perform a simple self-test. */ const char *msg = "compiled with incompatible luaconf.h"; #ifdef LUA_NUMBER_DOUBLE -#ifdef _WIN32 +#ifdef WIN32 if (b == (UBits)1610612736L) msg = "use D3DCREATE_FPU_PRESERVE with DirectX"; #endif @@ -3144,6 +3481,7 @@ static const struct luaL_reg emulib [] = { {"registerexit", emu_registerexit}, {"readonly", movie_getreadonly}, {"setreadonly", movie_setreadonly}, + {"print", print}, // sure, why not {NULL,NULL} }; @@ -3324,7 +3662,7 @@ void FCEU_LuaFrameBoundary() { #endif } else { - FCEU_LuaStop(); + FCEU_LuaOnStop(); FCEU_DispMessage("Script died of natural causes.\n"); } @@ -3383,6 +3721,12 @@ int FCEU_LoadLuaCode(const char *filename) { luaL_register(L, "bit", bit_funcs); // LuaBitOp library lua_settop(L, 0); // clean the stack, because each call to luaL_register leaves a table on top + // register a few utility functions outside of libraries (in the global namespace) + lua_register(L, "print", print); + lua_register(L, "tostring", tostring); + lua_register(L, "addressof", addressof); + lua_register(L, "copytable", copytable); + // old bit operation functions lua_register(L, "AND", bit_band); lua_register(L, "OR", bit_bor); @@ -3446,6 +3790,21 @@ int FCEU_LoadLuaCode(const char *filename) { // Set up our protection hook to be executed once every 10,000 bytecode instructions. //lua_sethook(thread, FCEU_LuaHookFunction, LUA_MASKCOUNT, 10000); +#ifdef WIN32 + info_print = PrintToWindowConsole; + info_onstart = WinLuaOnStart; + info_onstop = WinLuaOnStop; + if(!LuaConsoleHWnd) + LuaConsoleHWnd = CreateDialog(fceu_hInstance, MAKEINTRESOURCE(IDD_LUA), hAppWnd, (DLGPROC) DlgLuaScriptDialog); + info_uid = (int)LuaConsoleHWnd; +#else + info_print = NULL; + info_onstart = NULL; + info_onstop = NULL; +#endif + if (info_onstart) + info_onstart(info_uid); + // We're done. return 1; } @@ -3486,6 +3845,9 @@ void FCEU_LuaStop() { CoInitialize(0); #endif + if (info_onstop) + info_onstop(info_uid); + //lua_gc(L,LUA_GCCOLLECT,0); @@ -3499,7 +3861,8 @@ void FCEU_LuaStop() { * */ int FCEU_LuaRunning() { - return L && luaRunning; + // FIXME: return false when no callback functions are registered. + return (int) (L != NULL); // should return true if callback functions are active. } @@ -3542,6 +3905,7 @@ uint8 FCEU_LuaReadJoypad(int which, uint8 joyl) { * This function will not return true if a script is not running. */ int FCEU_LuaRerecordCountSkip() { + // FIXME: return true if (there are any active callback functions && skipRerecords) return L && luaRunning && skipRerecords; } @@ -3554,7 +3918,7 @@ int FCEU_LuaRerecordCountSkip() { */ void FCEU_LuaGui(uint8 *XBuf) { - if (!L || !luaRunning) + if (!L/* || !luaRunning*/) return; // First, check if we're being called by anybody @@ -3623,3 +3987,10 @@ void FCEU_LuaGui(uint8 *XBuf) { return; } + +lua_State* FCEU_GetLuaState() { + return L; +} +char* FCEU_GetLuaScriptName() { + return luaScriptName; +} diff --git a/vc/vc8_fceux.vcproj b/vc/vc8_fceux.vcproj index 41f1a7c0..202d992c 100644 --- a/vc/vc8_fceux.vcproj +++ b/vc/vc8_fceux.vcproj @@ -1076,6 +1076,10 @@ RelativePath="..\src\drivers\win\log.h" > + + diff --git a/vc/vc9_fceux.vcproj b/vc/vc9_fceux.vcproj index ecd4d9e0..75b4aac4 100644 --- a/vc/vc9_fceux.vcproj +++ b/vc/vc9_fceux.vcproj @@ -1075,6 +1075,10 @@ RelativePath="..\src\drivers\win\log.h" > + +