// LUADBG.cpp : Defines the entry point for the application. // #include "stdafx.h" #include "LUADBG.h" #include "_TinyRegistry.h" #include "_TinyFileEnum.h" #include "_TinyBrowseFolder.h" #include "AboutWnd.h" #include #include #ifndef WIN64 #include "Shlwapi.h" #pragma comment (lib, "Shlwapi.lib") #endif _TINY_DECLARE_APP(); CLUADbg::CLUADbg() { m_pIScriptSystem = NULL; m_pIPak = NULL; m_pIVariable = NULL; m_hRoot = NULL; m_bDisabled = false; m_pTreeToAdd = NULL; } CLUADbg::~CLUADbg() { _TinyRegistry cTReg; _TinyVerify(cTReg.WriteNumber("Software\\Tiny\\LuaDebugger\\", "MainHorzSplitter", m_wndMainHorzSplitter.GetSplitterPos())); _TinyVerify(cTReg.WriteNumber("Software\\Tiny\\LuaDebugger\\", "SrcEditSplitter", m_wndSrcEditSplitter.GetSplitterPos())); _TinyVerify(cTReg.WriteNumber("Software\\Tiny\\LuaDebugger\\", "WatchSplitter", m_wndWatchSplitter.GetSplitterPos())); _TinyVerify(cTReg.WriteNumber("Software\\Tiny\\LuaDebugger\\", "WatchCallstackSplitter", m_wndWatchCallstackSplitter.GetSplitterPos())); _TinyVerify(cTReg.WriteNumber("Software\\Tiny\\LuaDebugger\\", "Fullscreen", ::IsZoomed(m_hWnd) ? 1 : 0)); } TBBUTTON tbButtonsAdd [] = { { 0, ID_DEBUG_RUN, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0L, 0 }, { 1, ID_DEBUG_BREAK, 0, BTNS_BUTTON, { 0 }, 0L, 0 }, { 2, ID_DEBUG_STOP, 0, BTNS_BUTTON, { 0 }, 0L, 0 }, { 0, 0, TBSTATE_ENABLED, BTNS_SEP, { 0 }, 0L, 0 }, { 3, ID_DEBUG_STEPINTO, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0L, 0 }, { 4, ID_DEBUG_STEPOVER, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0L, 0 }, { 5, ID_DEBUG_TOGGLEBREAKPOINT, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0L, 0 }, { 6, ID_DEBUG_DISABLE, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0L, 0 }, { 0, 0, TBSTATE_ENABLED, BTNS_SEP, { 0 }, 0L, 0 }, { 7, IDM_ABOUT, TBSTATE_ENABLED, BTNS_BUTTON, { 0 }, 0L, 0 }, }; //! add a backslash if needed inline void MyPathAddBackslash( char* szPath ) { if(szPath[0]==0) return; size_t nLen = strlen(szPath); if (szPath[nLen-1] == '\\') return; if (szPath[nLen-1] == '/') { szPath[nLen-1] = '\\'; return; } szPath[nLen] = '\\'; szPath[nLen+1] = '\0'; } LRESULT CLUADbg::OnCreate(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { _TinyRect erect; _TinyRect lrect; _TinyRect wrect; _TinyRegistry cTReg; DWORD dwXOrigin=0,dwYOrigin=0,dwXSize=800,dwYSize=600; if(cTReg.WriteNumber("Software\\Tiny\\LuaDebugger\\", "XOrigin",dwXOrigin)) { cTReg.WriteNumber("Software\\Tiny\\LuaDebugger\\", "YOrigin",dwYOrigin); cTReg.WriteNumber("Software\\Tiny\\LuaDebugger\\", "XSize",dwXSize); cTReg.WriteNumber("Software\\Tiny\\LuaDebugger\\", "YSize",dwYSize); } SetWindowPos(dwXOrigin, dwYOrigin, dwXSize, dwYSize, SWP_NOZORDER | SWP_NOMOVE); // TODO: Make sure fullscreen flag is loaded and used // WS_MAXIMIZE // DWORD dwFullscreen = 0; // cTReg.ReadNumber("Software\\Tiny\\LuaDebugger\\", "Fullscreen", dwFullscreen); // if (dwFullscreen == 1) // ShowWindow(SW_MAXIMIZE); CenterOnScreen(); GetClientRect(&wrect); erect=wrect; erect.top=0; erect.bottom=32; m_wToolbar.Create((ULONG_PTR) GetMenu(m_hWnd), WS_CHILD | WS_VISIBLE | TBSTYLE_TOOLTIPS, 0, &erect, this); //AMD Port m_wToolbar.AddButtons(IDC_LUADBG, tbButtonsAdd, 10); m_wndStatus.Create(0, NULL, this); // Client area window _TinyVerify(m_wndClient.Create(NULL, _T(""), WS_CHILD | WS_VISIBLE, 0, &wrect, this)); m_wndClient.NotifyReflection(TRUE); // Splitter dividing source/file and watch windows m_wndMainHorzSplitter.Create(&m_wndClient, NULL, NULL, true); // Divides source and file view m_wndSrcEditSplitter.Create(&m_wndMainHorzSplitter); // Divides two watch windows m_wndWatchSplitter.Create(&m_wndMainHorzSplitter); // Divides the watch window and the cllstack m_wndWatchCallstackSplitter.Create(&m_wndWatchSplitter); // Add all scripts to the file tree m_wFilesTree.Create(erect, &m_wndSrcEditSplitter, IDC_FILES); char szRootPath[_MAX_PATH]; _getcwd(szRootPath, _MAX_PATH); MyPathAddBackslash(szRootPath); strcat(szRootPath, "SCRIPTS\\"); m_wFilesTree.ScanFiles(szRootPath); _TinyVerify(m_wScriptWindow.Create(WS_VISIBLE | WS_CHILD | ES_WANTRETURN | WS_HSCROLL | WS_VSCROLL | ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE, 0, &erect, &m_wndSrcEditSplitter)); m_wScriptWindow.SendMessage(EM_SETEVENTMASK,0,ENM_SCROLL); m_wndFileViewCaption.Create("Scripts", &m_wFilesTree, &m_wndSrcEditSplitter); m_wndSourceCaption.Create("Source", &m_wScriptWindow, &m_wndSrcEditSplitter); m_wndSrcEditSplitter.SetFirstPan(&m_wndFileViewCaption); m_wndSrcEditSplitter.SetSecondPan(&m_wndSourceCaption); m_wLocals.Create(IDC_LOCALS,WS_CHILD|TVS_HASLINES|TVS_HASBUTTONS|TVS_LINESATROOT|WS_VISIBLE,WS_EX_CLIENTEDGE,&erect, &m_wndWatchSplitter); m_wWatch.Create(IDC_WATCH,WS_CHILD|WS_VISIBLE|TVS_HASLINES|TVS_HASBUTTONS|TVS_LINESATROOT,WS_EX_CLIENTEDGE,&erect, &m_wndWatchSplitter); m_wCallstack.Create(0,WS_CHILD|WS_VISIBLE|LVS_REPORT,WS_EX_CLIENTEDGE,&erect, &m_wndWatchSplitter); m_wndLocalsCaption.Create("Locals", &m_wLocals, &m_wndWatchSplitter); m_wndWatchCaption.Create("Watch", &m_wWatch, &m_wndWatchCallstackSplitter); m_wndCallstackCaption.Create("Callstack", &m_wCallstack, &m_wndWatchCallstackSplitter); m_wndWatchSplitter.SetFirstPan(&m_wndWatchCallstackSplitter); m_wndWatchCallstackSplitter.SetFirstPan(&m_wndWatchCaption); m_wndWatchCallstackSplitter.SetSecondPan(&m_wndCallstackCaption); m_wndWatchSplitter.SetSecondPan(&m_wndLocalsCaption); m_wndMainHorzSplitter.SetFirstPan(&m_wndSrcEditSplitter); m_wndMainHorzSplitter.SetSecondPan(&m_wndWatchSplitter); Reshape(wrect.right-wrect.left,wrect.bottom-wrect.top); // Read splitter window locations from registry DWORD dwVal; cTReg.ReadNumber("Software\\Tiny\\LuaDebugger\\", "MainHorzSplitter", dwVal, 150); m_wndMainHorzSplitter.SetSplitterPos(dwVal); m_wndMainHorzSplitter.Reshape(); cTReg.ReadNumber("Software\\Tiny\\LuaDebugger\\", "SrcEditSplitter", dwVal, 190); m_wndSrcEditSplitter.SetSplitterPos(dwVal); m_wndSrcEditSplitter.Reshape(); cTReg.ReadNumber("Software\\Tiny\\LuaDebugger\\", "WatchSplitter", dwVal, wrect.right / 3 * 2); m_wndWatchSplitter.SetSplitterPos(dwVal); m_wndWatchSplitter.Reshape(); cTReg.ReadNumber("Software\\Tiny\\LuaDebugger\\", "WatchCallstackSplitter", dwVal, wrect.right / 3); m_wndWatchCallstackSplitter.SetSplitterPos(dwVal); m_wndWatchCallstackSplitter.Reshape(); m_wCallstack.InsertColumn(0, "Function", 108, 0); m_wCallstack.InsertColumn(1, "Line", 40, 1); m_wCallstack.InsertColumn(2, "File", 108, 2); //_TinyVerify(LoadFile("C:\\MASTERCD\\SCRIPTS\\Default\\Entities\\PLAYER\\BasicPlayer.lua")); //_TinyVerify(LoadFile("C:\\MASTERCD\\SCRIPTS\\Default\\Entities\\PLAYER\\player.lua")); // _TINY_CHECK_LAST_ERROR return 0; } bool CLUADbg::LoadFile(const char *pszFile, bool bForceReload) { FILE *hFile = NULL; char *pszScript = NULL, *pszFormattedScript = NULL; UINT iLength, iCmpBufPos, iSrcBufPos, iDestBufPos, iNumChars, iStrStartPos, iCurKWrd, i; char szCmpBuf[2048]; bool bIsKeyWord; string strLuaFormatFileName = "@"; char szMasterCD[_MAX_PATH]; // Create lua specific filename and check if we already have that file loaded _getcwd(szMasterCD, _MAX_PATH); if (strncmp(szMasterCD, pszFile, strlen(szMasterCD) - 1) == 0) { strLuaFormatFileName += &pszFile[strlen(szMasterCD) + 1]; for (i=0; iFOpen(pszFile, "rb"); if (!hFile) return false; // set filename in window title { char str[_MAX_PATH]; sprintf(str,"Lua Debugger [%s]",pszFile); SetWindowText(str); } // Get file size // fseek(hFile, 0, SEEK_END); m_pIPak->FSeek(hFile, 0, SEEK_END); // iLength = ftell(hFile); iLength = m_pIPak->FTell(hFile); // fseek(hFile, 0, SEEK_SET); m_pIPak->FSeek(hFile, 0, SEEK_SET); pszScript = new char [iLength + 1]; pszFormattedScript = new char [512 + iLength * 3]; // _TinyVerify(fread(pszScript, iLength, 1, hFile) == 1); _TinyVerify(m_pIPak->FRead(pszScript, iLength, 1, hFile) == 1); pszScript[iLength] = '\0'; _TinyAssert(strlen(pszScript) == iLength); // RTF text, font Courier, green comments and blue keyword strcpy(pszFormattedScript, "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033" \ "{\\fonttbl{\\f0\\fmodern\\fprq1\\fcharset0 Courier(PS);}{\\f1\\fswiss\\fcharset0 Arial;}}" \ "{\\colortbl ;\\red0\\green0\\blue255;\\red0\\green128\\blue0;\\red160\\green160\\blue160;}\\f0\\fs20"); const char szKeywords[][32] = { "function", "do", "for", "end", "and", "or", "not", "while", "return", "if", "then", "else", "elseif", "self", "local", "in", "nil", "repeat", "until", "break", }; // Format text with syntax coloring iDestBufPos = strlen(pszFormattedScript); iSrcBufPos = 0; while (pszScript[iSrcBufPos] != '\0') { // Scan next token iNumChars = 1; iStrStartPos = iSrcBufPos; while (pszScript[iSrcBufPos] != ' ' && pszScript[iSrcBufPos] != '\n' && pszScript[iSrcBufPos] != '\r' && pszScript[iSrcBufPos] != '\t' && pszScript[iSrcBufPos] != '\0' && pszScript[iSrcBufPos] != '(' && pszScript[iSrcBufPos] != ')' && pszScript[iSrcBufPos] != '[' && pszScript[iSrcBufPos] != ']' && pszScript[iSrcBufPos] != '{' && pszScript[iSrcBufPos] != '}' && pszScript[iSrcBufPos] != ',' && pszScript[iSrcBufPos] != '.' && pszScript[iSrcBufPos] != ';' && pszScript[iSrcBufPos] != ':' && pszScript[iSrcBufPos] != '=' && pszScript[iSrcBufPos] != '==' && pszScript[iSrcBufPos] != '*' && pszScript[iSrcBufPos] != '+' && pszScript[iSrcBufPos] != '/' && pszScript[iSrcBufPos] != '~' && pszScript[iSrcBufPos] != '"') { iSrcBufPos++; iNumChars++; // Special treatment of '-' to allow parsing of '--' if (pszScript[iSrcBufPos - 1] == '-' && pszScript[iSrcBufPos] != '-') break; } if (iNumChars == 1) iSrcBufPos++; else iNumChars--; // Copy token and add escapes iCmpBufPos = 0; for (i=iStrStartPos; iFClose(hFile); if (pszScript) { delete [] pszScript; pszScript = NULL; } /* hFile = fopen("C:\\Debug.txt", "w"); fwrite(pszFormattedScript, 1, strlen(pszFormattedScript), hFile); fclose(hFile); */ m_wScriptWindow.SetText(pszFormattedScript); if (pszFormattedScript) { delete [] pszFormattedScript; pszFormattedScript = NULL; } return true; } LRESULT CLUADbg::OnEraseBkGnd(HWND hWnd,UINT message, WPARAM wParam, LPARAM lParam) { return 1; } extern bool g_bDone; // From LuaDbgInterface.cpp LRESULT CLUADbg::OnClose(HWND hWnd,UINT message, WPARAM wParam, LPARAM lParam) { // Quit(); WINDOWINFO wi; if(::GetWindowInfo(m_hWnd,&wi)) { _TinyRegistry cTReg; _TinyVerify(cTReg.WriteNumber("Software\\Tiny\\LuaDebugger\\", "XOrigin", wi.rcWindow.left)); _TinyVerify(cTReg.WriteNumber("Software\\Tiny\\LuaDebugger\\", "YOrigin", wi.rcWindow.top)); _TinyVerify(cTReg.WriteNumber("Software\\Tiny\\LuaDebugger\\", "XSize", wi.rcWindow.right-wi.rcWindow.left)); _TinyVerify(cTReg.WriteNumber("Software\\Tiny\\LuaDebugger\\", "YSize", wi.rcWindow.bottom-wi.rcWindow.top)); } g_bDone=true; return 0; } LRESULT CLUADbg::OnSize(HWND hWnd,UINT message, WPARAM wParam, LPARAM lParam) { int w=LOWORD(lParam); int h=HIWORD(lParam); Reshape(w,h); return 0; } LRESULT CLUADbg::OnAbout(HWND hWnd,UINT message, WPARAM wParam, LPARAM lParam) { CAboutWnd wndAbout; wndAbout.DoModal(this); return 0; } LRESULT CLUADbg::OnToggleBreakpoint(HWND hWnd,UINT message, WPARAM wParam, LPARAM lParam) { m_wScriptWindow.AddBreakpoint(); return 0; } #define TOOLBAR_HEIGHT 28 #define STATUS_BAR_HEIGHT 20 bool CLUADbg::Reshape(int w,int h) { /* int nWatchHeight=(((float)h)*WATCH_HEIGHT_MULTIPLIER); int nFilesWidth=(((float)h)*FILES_WIDTH_MULTIPLIER); m_wScriptWindow.SetWindowPos(nFilesWidth,TOOLBAR_HEIGHT,w-nFilesWidth,h-TOOLBAR_HEIGHT-nWatchHeight,SWP_DRAWFRAME); m_wLocals.SetWindowPos(0,h-nWatchHeight,w/2,nWatchHeight,SWP_DRAWFRAME); m_wFilesTree.SetWindowPos(nFilesWidth,TOOLBAR_HEIGHT,nFilesWidth,h-TOOLBAR_HEIGHT-nWatchHeight,SWP_DRAWFRAME); m_wWatch.SetWindowPos(w/2,h-nWatchHeight,w/2,nWatchHeight,SWP_DRAWFRAME); */ m_wndClient.Reshape(w, h - TOOLBAR_HEIGHT - STATUS_BAR_HEIGHT); m_wndClient.SetWindowPos(0, TOOLBAR_HEIGHT, 0, 0, SWP_NOZORDER | SWP_NOSIZE); m_wndMainHorzSplitter.Reshape(w, h - TOOLBAR_HEIGHT - STATUS_BAR_HEIGHT); m_wToolbar.SetWindowPos(0,0,w,TOOLBAR_HEIGHT,0); m_wndStatus.SetWindowPos(0, h - STATUS_BAR_HEIGHT, w, STATUS_BAR_HEIGHT, NULL); return true; } void CLUADbg::PlaceLineMarker(UINT iLine) { m_wScriptWindow.SetLineMarker(iLine); m_wScriptWindow.ScrollToLine(iLine); } void CLUADbg::SetStatusBarText(const char *pszText) { // TODO: Find out why setting the panel text using the // dedicated message doesn't work. For some reason the // text gets drawn once but never again. // m_wndStatus.SetPanelText(pszText); m_wndStatus.SetWindowText(pszText); } void CLUADbg::GetStackAndLocals() { /////////// // Stack // /////////// { IScriptObject *pICallstack = m_pIScriptSystem->GetCallsStack(); _SmartScriptObject pIEntry(m_pIScriptSystem, true); const char *pszText = NULL; int iItem=0; m_wCallstack.Clear(); int i; for (i=pICallstack->Count() - 1; i>=1; i--) { pICallstack->GetAt(i, pIEntry); pIEntry->GetValue("description", pszText); iItem = m_wCallstack.InsertItem(0, pszText, 0); pIEntry->GetValue("line", pszText); m_wCallstack.SetItemText(iItem, 1, pszText); if (pIEntry->GetValue("sourcefile", pszText)) { if (_stricmp(pszText, "=C") == 0) m_wCallstack.SetItemText(iItem, 2, "Native C Function"); else m_wCallstack.SetItemText(iItem, 2, &pszText[1]); } else m_wCallstack.SetItemText(iItem, 2, "No Source"); } if (pICallstack->Count() == 0) iItem = m_wCallstack.InsertItem(0, "No Callstack Available", 0); pICallstack->Release(); pICallstack = NULL; } //////////// // Locals // //////////// m_wLocals.Clear(); m_pIVariable = m_pIScriptSystem->GetLocalVariables(1); m_pTreeToAdd = &m_wLocals; if (m_pIVariable) { m_hRoot = NULL; m_iRecursionLevel = 0; m_pIVariable->Dump((IScriptObjectDumpSink *) this); m_pIVariable->Release(); m_pIVariable = NULL; } else m_wLocals.AddItemToTree("No Locals Available"); m_pTreeToAdd = NULL; /////////// // Watch // /////////// const char *pszText = NULL; m_wWatch.Clear(); IScriptObject *pIScriptObj = m_pIScriptSystem->GetLocalVariables(1); string strWatchVar = "self"; bool bVarFound = false; pIScriptObj->BeginIteration(); while (pIScriptObj->MoveNext()) { if (pIScriptObj->GetCurrentKey(pszText)) { if (strWatchVar == pszText) { _SmartScriptObject pIEntry(m_pIScriptSystem, true); pIScriptObj->GetCurrent(pIEntry); m_pIVariable = pIEntry; m_pTreeToAdd = &m_wWatch; if (m_pIVariable) { bVarFound = true; m_hRoot = NULL; m_iRecursionLevel = 0; // Dump only works for tables, in case of values call the sink directly if (m_pIVariable->GetCurrentType() == svtObject) m_pIVariable->Dump((IScriptObjectDumpSink *) this); else { m_pIVariable = pIScriptObj; // Value needs to be retrieved from parent table OnElementFound(pszText, m_pIVariable->GetCurrentType()); } // No AddRef() ! // m_pIVariable->Release(); m_pIVariable = NULL; } m_pTreeToAdd = NULL; } } } pIScriptObj->EndIteration(); if (!bVarFound) m_wWatch.AddItemToTree(const_cast((strWatchVar + " = ?").c_str())); // TODO: Cast... } HTREEITEM CLUADbg::AddVariableToTree(const char *sName, ScriptVarType type, HTREEITEM hParent) { char szBuf[2048]; char szType[32]; const char *pszContent = NULL; bool bRetrieved = false; int iIdx; if (m_pTreeToAdd == NULL) { _TinyAssert(m_pTreeToAdd != NULL); return 0; } switch (type) { case svtNull: strcpy(szType, "[nil]"); break; case svtString: strcpy(szType, "[string]"); break; case svtNumber: strcpy(szType, "[numeric]"); break; case svtFunction: strcpy(szType, "[function]"); break; case svtObject: strcpy(szType, "[table]"); break; case svtUserData: strcpy(szType, "[user data]"); break; default: strcpy(szType, "[unknown]"); break; } if (type == svtString || type == svtNumber) { if (sName[0] == '[') { strcpy(szBuf, &sName[1]); szBuf[strlen(szBuf) - 1] = '\0'; iIdx = atoi(szBuf); bRetrieved = m_pIVariable->GetAt(iIdx, pszContent); } else bRetrieved = m_pIVariable->GetValue(sName, pszContent); if (bRetrieved) sprintf(szBuf, "%s %s = %s", szType, sName, pszContent); else sprintf(szBuf, "%s %s = (Unknown)", szType, sName); } else sprintf(szBuf, "%s %s", szType, sName); HTREEITEM hNewItem = m_pTreeToAdd->AddItemToTree(szBuf, NULL, hParent); TreeView_SortChildren(m_pTreeToAdd->m_hWnd, hNewItem, FALSE); return hNewItem; } void CLUADbg::OnElementFound(const char *sName, ScriptVarType type) { HTREEITEM hRoot = NULL; UINT iRecursionLevel = 0; _SmartScriptObject pTable(m_pIScriptSystem, true); IScriptObject *pIOldTbl = NULL; HTREEITEM hOldRoot = NULL; if(!sName)sName="[table idx]"; hRoot = AddVariableToTree(sName, type, m_hRoot); if (type == svtObject && m_iRecursionLevel < 5) { if (m_pIVariable->GetValue(sName, pTable)) { pIOldTbl = m_pIVariable; hOldRoot = m_hRoot; m_pIVariable = pTable; m_hRoot = hRoot; m_iRecursionLevel++; pTable->Dump((IScriptObjectDumpSink *) this); m_iRecursionLevel--; m_pIVariable = pIOldTbl; m_hRoot = hOldRoot; } } } void CLUADbg::OnElementFound(int nIdx,ScriptVarType type) { char szBuf[32]; sprintf(szBuf, "[%i]", nIdx); OnElementFound(szBuf, type); }