/**************************************************************************** * * * Project64 - A Nintendo 64 emulator. * * http://www.pj64-emu.com/ * * Copyright (C) 2012 Project64. All rights reserved. * * * * License: * * GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html * * * ****************************************************************************/ #include "stdafx.h" #include "DebuggerUI.h" #include "CPULog.h" #include "Debugger-CPULogView.h" #include CDebugCPULogView* CDebugCPULogView::_this = NULL; HHOOK CDebugCPULogView::hWinMessageHook = NULL; CDebugCPULogView::CDebugCPULogView(CDebuggerUI* debugger) : CDebugDialog(debugger), m_CPULogCopy(NULL), m_LogStartIndex(0), m_RowHeight(13) { } CDebugCPULogView::~CDebugCPULogView() { if (m_CPULogCopy != NULL) { delete m_CPULogCopy; } } LRESULT CDebugCPULogView::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { DlgResize_Init(false, true); DlgToolTip_Init(); DlgSavePos_Init(DebuggerUI_CPULogPos); m_CPUListView.Attach(GetDlgItem(IDC_CPU_LIST)); m_StateInfoEdit.Attach(GetDlgItem(IDC_STATEINFO_EDIT)); m_EnabledChk.Attach(GetDlgItem(IDC_CHK_ENABLE)); m_BuffSizeEdit.Attach(GetDlgItem(IDC_BUFFSIZE_EDIT)); m_Scrollbar.Attach(GetDlgItem(IDC_SCRL_BAR)); m_ExportBtn.Attach(GetDlgItem(IDC_BTN_EXPORT)); m_CPUListView.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER); m_CPUListView.ModifyStyle(LVS_OWNERDRAWFIXED, 0, 0); m_CPUListView.AddColumn(L"PC", 0); m_CPUListView.AddColumn(L"Command", 1); m_CPUListView.AddColumn(L"Parameters", 2); m_CPUListView.SetColumnWidth(0, 65); m_CPUListView.SetColumnWidth(1, 60); m_CPUListView.SetColumnWidth(2, 120); bool bLoggingEnabled = g_Settings->LoadBool(Debugger_CPULoggingEnabled); uint32_t bufferSize = g_Settings->LoadDword(Debugger_CPULogBufferSize); m_EnabledChk.SetCheck(bLoggingEnabled); m_BuffSizeEdit.SetDisplayType(CEditNumber32::DisplayDec); m_BuffSizeEdit.SetValue(bufferSize); m_BuffSizeEdit.EnableWindow(!bLoggingEnabled); RefreshList(true); m_ExportBtn.EnableWindow(false); if (!bLoggingEnabled && m_CPULogCopy != NULL) { m_ExportBtn.EnableWindow(true); } _this = this; DWORD dwThreadID = ::GetCurrentThreadId(); hWinMessageHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)HookProc, NULL, dwThreadID); LoadWindowPos(); WindowCreated(); return TRUE; } LRESULT CDebugCPULogView::OnDestroy(void) { UnhookWindowsHookEx(hWinMessageHook); m_CPUListView.Detach(); m_StateInfoEdit.Detach(); m_EnabledChk.Detach(); m_BuffSizeEdit.Detach(); m_Scrollbar.Detach(); m_ExportBtn.Detach(); if (m_CPULogCopy != NULL) { delete m_CPULogCopy; m_CPULogCopy = NULL; } return 0; } LRESULT CALLBACK CDebugCPULogView::HookProc(int nCode, WPARAM wParam, LPARAM lParam) { MSG *pMsg = (MSG*)lParam; switch (pMsg->message) { case WM_MOUSEWHEEL: _this->InterceptMouseWheel(pMsg->wParam, pMsg->lParam); break; } if (nCode < 0) { return CallNextHookEx(hWinMessageHook, nCode, wParam, lParam); } return 0; } LRESULT CDebugCPULogView::OnClicked(WORD /*wNotifyCode*/, WORD wID, HWND, BOOL& /*bHandled*/) { switch (wID) { case IDOK: EndDialog(0); break; case IDCANCEL: EndDialog(0); break; case IDC_CHK_ENABLE: ToggleLoggingEnabled(); break; case IDC_BTN_EXPORT: Export(); break; } return FALSE; } LRESULT CDebugCPULogView::OnActivate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { return FALSE; } LRESULT CDebugCPULogView::OnListItemChanged(NMHDR* pNMHDR) { NMITEMACTIVATE* pIA = reinterpret_cast(pNMHDR); int nItem = pIA->iItem; ShowRegStates(m_LogStartIndex + nItem); return FALSE; } LRESULT CDebugCPULogView::OnListDblClicked(NMHDR* pNMHDR) { NMITEMACTIVATE* pIA = reinterpret_cast(pNMHDR); int nItem = pIA->iItem; CPUState* state = m_CPULogCopy->GetEntry(m_LogStartIndex + nItem); if (state == NULL) { return FALSE; } m_Debugger->Debug_ShowCommandsLocation(state->pc, true); return FALSE; } LRESULT CDebugCPULogView::OnMeasureItem(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { if (wParam == IDC_CPU_LIST) { CClientDC dc(m_hWnd); dc.SelectFont(GetFont()); TEXTMETRIC tm; dc.GetTextMetrics(&tm); m_RowHeight = tm.tmHeight + tm.tmExternalLeading; MEASUREITEMSTRUCT* lpMeasureItem = (MEASUREITEMSTRUCT*)lParam; lpMeasureItem->itemHeight = m_RowHeight; } return FALSE; } LRESULT CDebugCPULogView::OnScroll(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { WORD type = LOWORD(wParam); HWND hScrollbar = (HWND)lParam; WORD scrlId = (WORD)::GetDlgCtrlID(hScrollbar); SCROLLINFO scrollInfo; scrollInfo.cbSize = sizeof(SCROLLINFO); scrollInfo.fMask = SIF_ALL; ::GetScrollInfo(hScrollbar, SB_CTL, &scrollInfo); int newPos; switch (type) { case SB_LINEUP: newPos = max(scrollInfo.nMin, scrollInfo.nPos - 1); break; case SB_LINEDOWN: newPos = min(scrollInfo.nMax, scrollInfo.nPos + 1); break; case SB_THUMBTRACK: newPos = scrollInfo.nTrackPos; break; default: return 0; } m_LogStartIndex = newPos; ::SetScrollPos(hScrollbar, SB_CTL, newPos, TRUE); if (scrlId == IDC_SCRL_BAR) { RefreshList(false); } return 0; } void CDebugCPULogView::InterceptMouseWheel(WPARAM wParam, LPARAM /*lParam*/) { int nScroll = -((short)HIWORD(wParam) / WHEEL_DELTA); if (MouseHovering(IDC_CPU_LIST) || MouseHovering(IDC_SCRL_BAR)) { int scrlMin, scrlMax; m_Scrollbar.GetScrollRange(&scrlMin, &scrlMax); int scrollPos = m_Scrollbar.GetScrollPos(); int newPos = scrollPos + nScroll; if (newPos < scrlMin) { newPos = scrlMin; } else if (newPos > scrlMax) { newPos = scrlMax; } m_LogStartIndex = newPos; m_Scrollbar.SetScrollPos(newPos, true); RefreshList(false); } } void CDebugCPULogView::OnExitSizeMove(void) { RefreshList(false); SaveWindowPos(true); } void CDebugCPULogView::ToggleLoggingEnabled(void) { bool bEnableLogging = (m_EnabledChk.GetCheck() == BST_CHECKED); m_BuffSizeEdit.EnableWindow(!bEnableLogging); g_Settings->SaveBool(Debugger_CPULoggingEnabled, bEnableLogging); if (bEnableLogging) { uint32_t newSize = m_BuffSizeEdit.GetValue(); g_Settings->SaveDword(Debugger_CPULogBufferSize, newSize); m_Debugger->CPULog()->Reset(); m_ExportBtn.EnableWindow(false); } else { RefreshList(true); m_ExportBtn.EnableWindow(m_CPULogCopy != NULL); } } void CDebugCPULogView::RefreshList(bool bUpdateBuffer) { if (bUpdateBuffer) { if (m_CPULogCopy != NULL) { delete m_CPULogCopy; } m_CPULogCopy = m_Debugger->CPULog()->Clone(); } if (m_CPULogCopy == NULL) { return; } size_t count = m_CPULogCopy->GetCount(); size_t numVisibleRows = GetNumVisibleRows(m_CPUListView); bool bCanDisplayAll = (numVisibleRows >= count); int scrollRangeMax = bCanDisplayAll ? 0 : count - numVisibleRows; m_Scrollbar.SetScrollRange(0, scrollRangeMax, false); m_Scrollbar.EnableWindow(!bCanDisplayAll); if (bUpdateBuffer) { m_LogStartIndex = scrollRangeMax; m_Scrollbar.SetScrollPos(m_LogStartIndex, true); ShowRegStates(count - 1); } size_t start = m_Scrollbar.GetScrollPos(); size_t end = start + numVisibleRows; if (end > count) { end = count; } m_CPUListView.SetRedraw(FALSE); m_CPUListView.DeleteAllItems(); int nItem = 0; for (size_t i = start; i < end; i++) { CPUState* state = m_CPULogCopy->GetEntry(i); if (state == NULL) { break; } char szPC[9]; sprintf(szPC, "%08X", state->pc); char *tokctx; char* szCommand = (char*)R4300iOpcodeName(state->opcode.Hex, state->pc); char* szCmdName = strtok_s((char*)szCommand, "\t", &tokctx); char* szCmdArgs = strtok_s(NULL, "\t", &tokctx); m_CPUListView.AddItem(nItem, 0, stdstr(szPC).ToUTF16().c_str()); m_CPUListView.AddItem(nItem, 1, stdstr(szCmdName).ToUTF16().c_str()); m_CPUListView.AddItem(nItem, 2, stdstr(szCmdArgs).ToUTF16().c_str()); nItem++; } m_CPUListView.SetRedraw(TRUE); } void CDebugCPULogView::ShowRegStates(size_t stateIndex) { CPUState* state = m_CPULogCopy->GetEntry(stateIndex); if (state == NULL) { return; } char szRegStates[2048]; char* out = szRegStates; out += sprintf(out, "PC: %08X\r\n\r\n", state->pc); for (int i = 0; i < 16; i++) { int regl = i, regr = i + 16; out += sprintf(out, "%s: %08X %08X %s: %08X %08X\r\n", CRegName::GPR[regl], state->gpr[regl].UW[1], state->gpr[regl].UW[0], CRegName::GPR[regr], state->gpr[regr].UW[1], state->gpr[regr].UW[0]); } out += sprintf(out, "HI: %08X %08X LO: %08X %08X\r\n\r\n", state->gprHi.UW[1], state->gprHi.UW[0], state->gprLo.UW[1], state->gprLo.UW[0]); for (int i = 0; i < 16; i++) { int regl = i, regr = i + 16; out += sprintf(out, "%-3s: %08X %-3s: %08X\r\n", CRegName::FPR[regl], *(uint32_t*)&state->fpr[regl], CRegName::FPR[regr], *(uint32_t*)&state->fpr[regr]); } out += sprintf(out, "FPCR: %08X\r\n", state->fpcr); m_StateInfoEdit.SetWindowText(stdstr(szRegStates).ToUTF16().c_str()); } void CDebugCPULogView::Export(void) { if (m_CPULogCopy == NULL) { return; } OPENFILENAMEA openfilename; char filePath[255]; memset(&filePath, 0, sizeof(filePath)); memset(&openfilename, 0, sizeof(openfilename)); sprintf(filePath, "CPULOG.txt"); openfilename.lStructSize = sizeof(openfilename); openfilename.hwndOwner = (HWND)m_hWnd; openfilename.lpstrFilter = "CPU Log (*.*)\0*.*;\0"; openfilename.lpstrFile = filePath; openfilename.lpstrInitialDir = "Logs"; openfilename.nMaxFile = MAX_PATH; openfilename.Flags = OFN_HIDEREADONLY; if (GetSaveFileNameA(&openfilename)) { m_CPULogCopy->DumpToFile(filePath); } } // util int CDebugCPULogView::GetNumVisibleRows(CListViewCtrl& list) { CHeaderCtrl header = list.GetHeader(); CRect listRect, headRect; list.GetWindowRect(&listRect); header.GetWindowRect(&headRect); int innerHeight = listRect.Height() - headRect.Height(); return (innerHeight / m_RowHeight); } bool CDebugCPULogView::MouseHovering(WORD ctrlId, int xMargin, int yMargin) { CRect rect; POINT pointerPos; ::GetWindowRect(GetDlgItem(ctrlId), &rect); ::GetCursorPos(&pointerPos); return ( pointerPos.x >= rect.left - xMargin && pointerPos.x <= rect.right + xMargin && pointerPos.y >= rect.top - yMargin && pointerPos.y <= rect.bottom + yMargin); }