From 11fd32d453e01a6419f3afea19a6208857b29854 Mon Sep 17 00:00:00 2001 From: aldelaro5 Date: Wed, 23 Aug 2017 17:41:03 -0400 Subject: [PATCH 1/9] Add a common RAM cache and make the scanner uses it This has the side-effect to basically have the RAM allocated once and not really freed until the program stops. This is necessary for the memory viewer because it needs to constantly have updated RAM, but without having the scanner running necessarily. --- Source/DolphinProcess/DolphinAccessor.cpp | 72 +++++++++++++++++++++-- Source/DolphinProcess/DolphinAccessor.h | 9 +++ Source/MemoryScanner/MemoryScanner.cpp | 28 +-------- Source/MemoryScanner/MemoryScanner.h | 1 - 4 files changed, 80 insertions(+), 30 deletions(-) mode change 100644 => 100755 Source/DolphinProcess/DolphinAccessor.cpp diff --git a/Source/DolphinProcess/DolphinAccessor.cpp b/Source/DolphinProcess/DolphinAccessor.cpp old mode 100644 new mode 100755 index c7650d53..fa09e30c --- a/Source/DolphinProcess/DolphinAccessor.cpp +++ b/Source/DolphinProcess/DolphinAccessor.cpp @@ -5,15 +5,19 @@ #include "Windows/WindowsDolphinProcess.h" #endif +#include + +#include "../Common/CommonUtils.h" #include "../Common/MemoryCommon.h" namespace DolphinComm { IDolphinProcess* DolphinAccessor::m_instance = nullptr; DolphinAccessor::DolphinStatus DolphinAccessor::m_status = DolphinStatus::unHooked; +char* DolphinAccessor::m_updatedRAMCache = nullptr; bool DolphinAccessor::m_mem2Enabled = false; -void DolphinAccessor::hook() +void DolphinAccessor::init() { if (m_instance == nullptr) { @@ -23,7 +27,11 @@ void DolphinAccessor::hook() m_instance = new WindowsDolphinProcess(); #endif } +} +void DolphinAccessor::hook() +{ + init(); if (!m_instance->findPID()) { m_status = DolphinStatus::notRunning; @@ -35,6 +43,7 @@ void DolphinAccessor::hook() else { m_status = DolphinStatus::hooked; + updateRAMCache(); } } @@ -75,7 +84,10 @@ u64 DolphinAccessor::getEmuRAMAddressStart() void DolphinAccessor::enableMem2(const bool doEnable) { + bool old = m_mem2Enabled; m_mem2Enabled = doEnable; + if (old != m_mem2Enabled) + updateRAMCache(); } bool DolphinAccessor::isMem2Enabled() @@ -94,19 +106,19 @@ void DolphinAccessor::autoDetectMem2() { case 'G': // Gamecube disc case 'P': // Promotional games, guaranteed to be Gamecube - m_mem2Enabled = false; + enableMem2(false); break; case 'R': // Wii disc, can be prototypes, but unlikely case 'S': // Later Wii disc - m_mem2Enabled = true; + enableMem2(true); break; // If there's no ID, likely to be a Wiiware, but this isn't guaranteed, could also be D, but // this one is present on both, so let's just leave MEM2 enabled for these by default, the // user can be the judge here, these are extremely unlikely cases anyway default: - m_mem2Enabled = true; + enableMem2(true); break; } } @@ -119,6 +131,8 @@ void DolphinAccessor::autoDetectMem2() bool DolphinAccessor::isValidConsoleAddress(const u32 address) { + if (getStatus() != DolphinStatus::hooked) + return false; bool isMem1Address = address >= Common::MEM1_START && address < Common::MEM1_END; if (m_mem2Enabled) { @@ -126,4 +140,54 @@ bool DolphinAccessor::isValidConsoleAddress(const u32 address) } return isMem1Address; } + +Common::MemOperationReturnCode DolphinAccessor::updateRAMCache() +{ + delete[] m_updatedRAMCache; + m_updatedRAMCache = nullptr; + + // MEM2, if enabled, is read right after MEM1 in the cache so both regions are contigous + if (m_mem2Enabled) + { + m_updatedRAMCache = new char[Common::MEM1_SIZE + Common::MEM2_SIZE - 1]; + // Read Wii extra RAM + if (!DolphinComm::DolphinAccessor::readFromRAM(Common::dolphinAddrToOffset(Common::MEM2_START), + m_updatedRAMCache + Common::MEM1_SIZE, + Common::MEM2_SIZE - 1, false)) + return Common::MemOperationReturnCode::operationFailed; + } + else + { + m_updatedRAMCache = new char[Common::MEM1_SIZE - 1]; + } + // Read GameCube and Wii basic RAM + if (!DolphinComm::DolphinAccessor::readFromRAM(0, m_updatedRAMCache, Common::MEM1_SIZE - 1, + false)) + return Common::MemOperationReturnCode::operationFailed; + return Common::MemOperationReturnCode::OK; +} + +std::string DolphinAccessor::getFormattedValueFromCache(const u32 ramIndex, Common::MemType memType, + size_t memSize, Common::MemBase memBase, + bool memIsUnsigned) +{ + return Common::formatMemoryToString(&m_updatedRAMCache[ramIndex], memType, memSize, memBase, + memIsUnsigned, Common::shouldBeBSwappedForType(memType)); +} + +void DolphinAccessor::copyRawMemoryFromCache(char* dest, const u32 consoleAddress, + const size_t byteCount) +{ + if (isValidConsoleAddress(consoleAddress) && + isValidConsoleAddress((consoleAddress + static_cast(byteCount)) - 1)) + { + u32 offset = Common::dolphinAddrToOffset(consoleAddress); + u32 ramIndex = 0; + if (offset >= Common::MEM1_SIZE) + ramIndex = offset - (Common::MEM2_START - Common::MEM1_END); + else + ramIndex = offset; + std::memcpy(dest, m_updatedRAMCache + ramIndex, byteCount); + } +} } diff --git a/Source/DolphinProcess/DolphinAccessor.h b/Source/DolphinProcess/DolphinAccessor.h index c83fe551..8257f0be 100644 --- a/Source/DolphinProcess/DolphinAccessor.h +++ b/Source/DolphinProcess/DolphinAccessor.h @@ -1,6 +1,8 @@ // Wrapper around IDolphinProcess #pragma once +#include "../Common/CommonTypes.h" +#include "../Common/MemoryCommon.h" #include "IDolphinProcess.h" namespace DolphinComm @@ -16,6 +18,7 @@ class DolphinAccessor unHooked }; + static void init(); static void hook(); static void unHook(); static bool readFromRAM(const u32 offset, char* buffer, const size_t size, const bool withBSwap); @@ -27,11 +30,17 @@ class DolphinAccessor static void enableMem2(const bool doEnable); static bool isMem2Enabled(); static void autoDetectMem2(); + static Common::MemOperationReturnCode updateRAMCache(); + static std::string getFormattedValueFromCache(const u32 ramIndex, Common::MemType memType, + size_t memSize, Common::MemBase memBase, + bool memIsUnsigned); + static void copyRawMemoryFromCache(char* dest, const u32 consoleAddress, const size_t byteCount); static bool isValidConsoleAddress(const u32 address); private: static IDolphinProcess* m_instance; static DolphinStatus m_status; static bool m_mem2Enabled; + static char* m_updatedRAMCache; }; } \ No newline at end of file diff --git a/Source/MemoryScanner/MemoryScanner.cpp b/Source/MemoryScanner/MemoryScanner.cpp index 0ea3f073..8b8d5e92 100644 --- a/Source/MemoryScanner/MemoryScanner.cpp +++ b/Source/MemoryScanner/MemoryScanner.cpp @@ -16,7 +16,6 @@ Common::MemOperationReturnCode MemScanner::firstScan(const MemScanner::ScanFiter const std::string& searchTerm2) { m_scanRAMCache = nullptr; - m_updatedRAMCache = nullptr; u32 ramSize = 0; if (DolphinComm::DolphinAccessor::isMem2Enabled()) { @@ -49,7 +48,6 @@ Common::MemOperationReturnCode MemScanner::firstScan(const MemScanner::ScanFiter m_wasUnknownInitialValue = true; m_memSize = 1; m_scanStarted = true; - m_updatedRAMCache = new char[ramSize - 1]; return Common::MemOperationReturnCode::OK; } @@ -82,8 +80,6 @@ Common::MemOperationReturnCode MemScanner::firstScan(const MemScanner::ScanFiter return scanReturn; } - m_updatedRAMCache = new char[ramSize - 1]; - bool withBSwap = Common::shouldBeBSwappedForType(m_memType); m_memSize = Common::getSizeForType(m_memType, termActualLength); @@ -272,8 +268,6 @@ void MemScanner::reset() m_wasUnknownInitialValue = false; if (m_scanRAMCache != nullptr) delete[] m_scanRAMCache; - if (m_updatedRAMCache != nullptr) - delete[] m_updatedRAMCache; m_resultCount = 0; m_scanStarted = false; } @@ -468,29 +462,13 @@ std::string MemScanner::getFormattedScannedValueAt(const int index) const Common::MemOperationReturnCode MemScanner::updateCurrentRAMCache() { - if (DolphinComm::DolphinAccessor::isMem2Enabled()) - { - if (!DolphinComm::DolphinAccessor::readFromRAM(Common::dolphinAddrToOffset(Common::MEM2_START), - m_updatedRAMCache + Common::MEM1_SIZE, - Common::MEM2_SIZE - 1, false)) - return Common::MemOperationReturnCode::operationFailed; - } - if (!DolphinComm::DolphinAccessor::readFromRAM(0, m_updatedRAMCache, Common::MEM1_SIZE - 1, - false)) - return Common::MemOperationReturnCode::operationFailed; - return Common::MemOperationReturnCode::OK; + return DolphinComm::DolphinAccessor::updateRAMCache(); } std::string MemScanner::getFormattedCurrentValueAt(const int index) const { - u32 offset = Common::dolphinAddrToOffset(m_resultsConsoleAddr.at(index)); - u32 ramIndex = 0; - if (offset >= Common::MEM1_SIZE) - ramIndex = offset - (Common::MEM2_START - Common::MEM1_END); - else - ramIndex = offset; - return Common::formatMemoryToString(&m_updatedRAMCache[ramIndex], m_memType, m_memSize, m_memBase, - !m_memIsSigned, Common::shouldBeBSwappedForType(m_memType)); + return DolphinComm::DolphinAccessor::getFormattedCurrentValue( + m_resultsConsoleAddr.at(index), m_memType, m_memSize, m_memBase, !m_memIsSigned); } size_t MemScanner::getResultCount() const diff --git a/Source/MemoryScanner/MemoryScanner.h b/Source/MemoryScanner/MemoryScanner.h index 8073cdd4..44233a69 100644 --- a/Source/MemoryScanner/MemoryScanner.h +++ b/Source/MemoryScanner/MemoryScanner.h @@ -153,6 +153,5 @@ class MemScanner bool m_wasUnknownInitialValue = false; size_t m_resultCount = 0; char* m_scanRAMCache = nullptr; - char* m_updatedRAMCache = nullptr; bool m_scanStarted = false; }; \ No newline at end of file From 542251251e4769bd66934a50cfcaa85925202c16 Mon Sep 17 00:00:00 2001 From: aldelaro5 Date: Sun, 10 Sep 2017 03:58:53 -0400 Subject: [PATCH 2/9] Remove an extra space at the end of the byte array Not only it's unnecessary, but it needs to be done so the viewer doesn't display spaces everywhere. --- Source/Common/MemoryCommon.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/Common/MemoryCommon.cpp b/Source/Common/MemoryCommon.cpp index 73db42bb..0e6d0c1d 100644 --- a/Source/Common/MemoryCommon.cpp +++ b/Source/Common/MemoryCommon.cpp @@ -463,8 +463,10 @@ std::string formatMemoryToString(const char* memory, const MemType type, const s std::memcpy(&aByte, memory + i, sizeof(u8)); ss << std::setfill('0') << std::setw(2) << static_cast(aByte) << " "; } - std::string test = ss.str(); - return ss.str(); + std::string str = ss.str(); + // Remove the space at the end + str.pop_back(); + return str; } default: return ""; From 252a7814145e2a4343662664c82655924bd1cdbe Mon Sep 17 00:00:00 2001 From: aldelaro5 Date: Sun, 10 Sep 2017 04:01:53 -0400 Subject: [PATCH 3/9] Make the scanner use the new RAM cache --- Source/MemoryScanner/MemoryScanner.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Source/MemoryScanner/MemoryScanner.cpp b/Source/MemoryScanner/MemoryScanner.cpp index 8b8d5e92..d22afa27 100644 --- a/Source/MemoryScanner/MemoryScanner.cpp +++ b/Source/MemoryScanner/MemoryScanner.cpp @@ -467,8 +467,18 @@ Common::MemOperationReturnCode MemScanner::updateCurrentRAMCache() std::string MemScanner::getFormattedCurrentValueAt(const int index) const { - return DolphinComm::DolphinAccessor::getFormattedCurrentValue( - m_resultsConsoleAddr.at(index), m_memType, m_memSize, m_memBase, !m_memIsSigned); + if (DolphinComm::DolphinAccessor::isValidConsoleAddress(m_resultsConsoleAddr.at(index))) + { + u32 offset = Common::dolphinAddrToOffset(m_resultsConsoleAddr.at(index)); + u32 ramIndex = 0; + if (offset >= Common::MEM1_SIZE) + ramIndex = offset - (Common::MEM2_START - Common::MEM1_END); + else + ramIndex = offset; + return DolphinComm::DolphinAccessor::getFormattedValueFromCache(ramIndex, m_memType, m_memSize, + m_memBase, !m_memIsSigned); + } + return ""; } size_t MemScanner::getResultCount() const From 2e39e08e960fce8cfb1d2a185d214caab4afaead Mon Sep 17 00:00:00 2001 From: aldelaro5 Date: Sun, 10 Sep 2017 04:08:30 -0400 Subject: [PATCH 4/9] Add a memory viewer This CE inspired viewer supports the following features: - Continuously updated memory in hex bytes and ASCII printed in typical hex editor format - Real time editing of the memory by selection - Keyboard navigation, arrow keys, page up/down and home/end are supported - Navigation by a scrollbar - The ability to jump to any entered address - Support for both MEM1 and MEM2, however, each is viewed separately, you can use the buttons on the top right to switch between the 2, obviously, if MEM2 isn't enabled, you can't go there - Handles unexpected unhooking gracefully, it just displays ?? all over to indicate it, a rehook will bring the data back --- Source/CMakeLists.txt | 2 + Source/GUI/MemViewer/MemViewer.cpp | 475 +++++++++++++++++++++++ Source/GUI/MemViewer/MemViewer.h | 70 ++++ Source/GUI/MemViewer/MemViewerWidget.cpp | 78 ++++ Source/GUI/MemViewer/MemViewerWidget.h | 32 ++ 5 files changed, 657 insertions(+) create mode 100755 Source/GUI/MemViewer/MemViewer.cpp create mode 100644 Source/GUI/MemViewer/MemViewer.h create mode 100644 Source/GUI/MemViewer/MemViewerWidget.cpp create mode 100644 Source/GUI/MemViewer/MemViewerWidget.h diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 84f3e730..1e52c796 100755 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -23,6 +23,8 @@ set(SRCS ${DolphinProcessSrc} GUI/MemWatcher/MemWatchWidget.cpp GUI/MemScanner/ResultsListModel.cpp GUI/MemScanner/MemScanWidget.cpp + GUI/MemViewer/MemViewer.cpp + GUI/MemViewer/MemViewerWidget.cpp GUI/MainWindow.cpp main.cpp) diff --git a/Source/GUI/MemViewer/MemViewer.cpp b/Source/GUI/MemViewer/MemViewer.cpp new file mode 100755 index 00000000..bacfa53e --- /dev/null +++ b/Source/GUI/MemViewer/MemViewer.cpp @@ -0,0 +1,475 @@ +#include "MemViewer.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../../Common/CommonUtils.h" +#include "../../DolphinProcess/DolphinAccessor.h" + +MemViewer::MemViewer(QWidget* parent) : QAbstractScrollArea(parent) +{ +#ifdef __linux__ + setFont(QFont("Monospace", 15)); +#elif _WIN32 + setFont(QFont("Courier New", 15)); +#endif + m_hexKeyList = QList({Qt::Key_0, Qt::Key_1, Qt::Key_2, Qt::Key_3, Qt::Key_4, Qt::Key_5, + Qt::Key_6, Qt::Key_7, Qt::Key_8, Qt::Key_9, Qt::Key_A, Qt::Key_B, + Qt::Key_C, Qt::Key_D, Qt::Key_E, Qt::Key_F}); + m_charWidthEm = fontMetrics().width(QLatin1Char('M')); + m_charHeight = fontMetrics().height(); + m_hexAreaWidth = 16 * (m_charWidthEm * 2 + m_charWidthEm / 2); + m_hexAreaHeight = 16 * m_charHeight; + m_rowHeaderWidth = m_charWidthEm * (sizeof(u32) * 2 + 1) + m_charWidthEm / 2; + m_hexAsciiSeparatorPosX = m_rowHeaderWidth + m_hexAreaWidth; + m_columnHeaderHeight = m_charHeight + m_charWidthEm / 2; + m_curosrRect = new QRect(); + m_updatedRawMemoryData = new char[16 * 16]; + m_lastRawMemoryData = new char[16 * 16]; + m_memoryMsElapsedLastChange = new int[16 * 16]; + m_memViewStart = Common::MEM1_START; + m_memViewEnd = Common::MEM1_END; + m_currentFirstAddress = m_memViewStart; + std::fill(m_memoryMsElapsedLastChange, m_memoryMsElapsedLastChange + 16 * 16, 0); + updateMemoryData(); + std::memcpy(m_lastRawMemoryData, m_updatedRawMemoryData, 16 * 16); + + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + verticalScrollBar()->setRange(0, ((m_memViewEnd - m_memViewStart) / 16) - 1); + verticalScrollBar()->setPageStep(16); + + m_elapsedTimer.start(); + + // The viewport is implicitly updated at the constructor's end +} + +MemViewer::~MemViewer() +{ + delete[] m_updatedRawMemoryData; +} + +QSize MemViewer::sizeHint() const +{ + return QSize(m_rowHeaderWidth + m_hexAreaWidth + 17 * m_charWidthEm + + verticalScrollBar()->width(), + m_columnHeaderHeight + m_hexAreaHeight + m_charHeight / 2); +} + +void MemViewer::memoryValidityChanged(const bool valid) +{ + m_validMemory = valid; + viewport()->update(); +} + +void MemViewer::updateMemoryData() +{ + std::swap(m_updatedRawMemoryData, m_lastRawMemoryData); + m_validMemory = + (DolphinComm::DolphinAccessor::updateRAMCache() == Common::MemOperationReturnCode::OK); + if (DolphinComm::DolphinAccessor::isValidConsoleAddress(m_currentFirstAddress)) + DolphinComm::DolphinAccessor::copyRawMemoryFromCache(m_updatedRawMemoryData, + m_currentFirstAddress, 16 * 16); + if (!m_validMemory) + emit memErrorOccured(); +} + +void MemViewer::updateViewer() +{ + updateMemoryData(); + viewport()->update(); + if (!m_validMemory) + emit memErrorOccured(); +} + +void MemViewer::jumpToAddress(const u32 address) +{ + if (DolphinComm::DolphinAccessor::isValidConsoleAddress(address)) + { + m_currentFirstAddress = address; + if (address < Common::MEM1_END && m_memViewStart != Common::MEM1_START) + changeMemoryRegion(false); + else if (address >= Common::MEM2_START && m_memViewStart != Common::MEM2_START) + changeMemoryRegion(true); + + if (m_currentFirstAddress > m_memViewEnd - 16 * 16) + m_currentFirstAddress = m_memViewEnd - 16 * 16; + std::fill(m_memoryMsElapsedLastChange, m_memoryMsElapsedLastChange + 16 * 16, 0); + updateMemoryData(); + std::memcpy(m_lastRawMemoryData, m_updatedRawMemoryData, 16 * 16); + m_carretBetweenHex = false; + + m_disableScrollContentEvent = true; + verticalScrollBar()->setValue(((address & 0xFFFFFFF0) - m_memViewStart) / 16); + m_disableScrollContentEvent = false; + + viewport()->update(); + } +} + +void MemViewer::changeMemoryRegion(const bool isMEM2) +{ + m_memViewStart = isMEM2 ? Common::MEM2_START : Common::MEM1_START; + m_memViewEnd = isMEM2 ? Common::MEM2_END : Common::MEM1_END; + verticalScrollBar()->setRange(0, ((m_memViewEnd - m_memViewStart) / 16) - 1); +} + +void MemViewer::mousePressEvent(QMouseEvent* event) +{ + int x = event->pos().x(); + int y = event->pos().y(); + if (x >= m_rowHeaderWidth && x <= m_rowHeaderWidth + m_hexAreaWidth && y >= m_columnHeaderHeight) + { + int oldSelectedPosX = m_byteSelectedPosX; + int oldSelectedPosY = m_byteSelectedPosY; + m_byteSelectedPosX = (x - m_rowHeaderWidth) / (m_charWidthEm / 2 + m_charWidthEm * 2); + m_byteSelectedPosY = (y - m_columnHeaderHeight) / m_charHeight; + if (oldSelectedPosX == m_byteSelectedPosX && oldSelectedPosY == m_byteSelectedPosY) + m_carretBetweenHex = !m_carretBetweenHex; + else + m_carretBetweenHex = false; + viewport()->update(); + } +} + +bool MemViewer::handleNaviguationKey(const int key) +{ + switch (key) + { + case Qt::Key_Up: + if (m_byteSelectedPosY != 0) + { + m_byteSelectedPosY--; + viewport()->update(); + } + else + { + scrollContentsBy(0, 1); + } + return true; + case Qt::Key_Down: + if (m_byteSelectedPosY != 15) + { + m_byteSelectedPosY++; + viewport()->update(); + } + else + { + scrollContentsBy(0, -1); + } + return true; + case Qt::Key_Left: + if (m_byteSelectedPosX != 0) + { + m_byteSelectedPosX--; + viewport()->update(); + } + else if (m_byteSelectedPosY != 0) + { + m_byteSelectedPosX = 15; + m_byteSelectedPosY--; + viewport()->update(); + } + else + { + m_byteSelectedPosX = 15; + m_byteSelectedPosY = 15; + scrollContentsBy(0, 1); + } + return true; + case Qt::Key_Right: + if (m_byteSelectedPosX != 15) + { + m_byteSelectedPosX++; + viewport()->update(); + } + else if (m_byteSelectedPosY != 15) + { + m_byteSelectedPosX = 0; + m_byteSelectedPosY++; + viewport()->update(); + } + else + { + m_byteSelectedPosX = 0; + m_byteSelectedPosY = 0; + scrollContentsBy(0, -1); + } + return true; + case Qt::Key_PageUp: + scrollContentsBy(0, verticalScrollBar()->pageStep()); + return true; + case Qt::Key_PageDown: + scrollContentsBy(0, -verticalScrollBar()->pageStep()); + return true; + case Qt::Key_Home: + verticalScrollBar()->setValue(0); + return true; + case Qt::Key_End: + verticalScrollBar()->setValue((m_memViewEnd - m_memViewStart) / 16 - 1); + return true; + default: + return false; + } +} + +bool MemViewer::writeHexCharacterToSelectedMemory(const std::string hexCharToWrite) +{ + std::string editedByteStr = Common::formatMemoryToString( + m_updatedRawMemoryData + ((m_byteSelectedPosY * 16) + m_byteSelectedPosX), + Common::MemType::type_byteArray, 1, Common::MemBase::base_none, true); + if (m_carretBetweenHex) + editedByteStr[1] = hexCharToWrite[0]; + else + editedByteStr[0] = hexCharToWrite[0]; + + std::stringstream ss(editedByteStr); + unsigned int editedByteData = 0; + ss >> std::hex >> editedByteData; + char byteToWrite = static_cast(editedByteData); + u32 offsetToWrite = Common::dolphinAddrToOffset(m_currentFirstAddress + + (m_byteSelectedPosY * 16 + m_byteSelectedPosX)); + return DolphinComm::DolphinAccessor::writeToRAM(offsetToWrite, &byteToWrite, 1, false); +} + +void MemViewer::keyPressEvent(QKeyEvent* event) +{ + if (m_validMemory) + { + if (!m_hexKeyList.contains(event->key())) + { + if (!handleNaviguationKey(event->key())) + event->ignore(); + } + else + { + if (!writeHexCharacterToSelectedMemory(event->text().toUpper().toStdString())) + { + m_validMemory = false; + viewport()->update(); + emit memErrorOccured(); + } + else + { + if (!m_carretBetweenHex) + { + m_carretBetweenHex = true; + } + else + { + m_carretBetweenHex = false; + if (m_byteSelectedPosX == 15) + { + if (m_byteSelectedPosY != 15) + { + m_byteSelectedPosX = 0; + m_byteSelectedPosY++; + } + } + else + { + m_byteSelectedPosX++; + } + } + updateMemoryData(); + viewport()->update(); + } + } + } + else + { + event->ignore(); + } +} + +void MemViewer::scrollContentsBy(int dx, int dy) +{ + if (!m_disableScrollContentEvent && m_validMemory) + { + u32 newAddress = m_currentFirstAddress + 16 * (-dy); + if (newAddress < m_memViewStart) + newAddress = m_memViewStart; + else if (newAddress > m_memViewEnd - 16 * 16) + newAddress = m_memViewEnd - 16 * 16; + + if (newAddress != m_currentFirstAddress) + { + jumpToAddress(newAddress); + } + } +} + +void MemViewer::renderSeparatorLines(QPainter& painter) +{ + painter.drawLine(m_hexAsciiSeparatorPosX, 0, m_hexAsciiSeparatorPosX, + m_columnHeaderHeight + m_hexAreaHeight); + painter.drawLine(m_rowHeaderWidth - m_charWidthEm / 2, 0, m_rowHeaderWidth - m_charWidthEm / 2, + m_columnHeaderHeight + m_hexAreaHeight); + painter.drawLine(0, m_columnHeaderHeight, m_hexAsciiSeparatorPosX + 17 * m_charWidthEm, + m_columnHeaderHeight); +} + +void MemViewer::renderColumnsHeaderText(QPainter& painter) +{ + painter.drawText(m_charWidthEm / 2, m_charHeight, " Address"); + int posXHeaderText = m_rowHeaderWidth; + for (int i = 0; i < 16; ++i) + { + std::stringstream ss; + ss << std::hex << std::uppercase << i; + std::string headerText = "." + ss.str(); + painter.drawText(posXHeaderText, m_charHeight, QString::fromStdString(headerText)); + posXHeaderText += m_charWidthEm * 2 + m_charWidthEm / 2; + } + + painter.drawText(m_hexAsciiSeparatorPosX + m_charWidthEm / 2, m_charHeight, " Text (ASCII) "); +} + +void MemViewer::renderRowHeaderText(QPainter& painter, const int rowIndex) +{ + std::stringstream ss; + ss << std::setfill('0') << std::setw(sizeof(u32) * 2) << std::hex << std::uppercase + << m_currentFirstAddress + 16 * rowIndex; + painter.drawText(m_charWidthEm / 2, (rowIndex + 1) * m_charHeight + m_columnHeaderHeight, + QString::fromStdString(ss.str())); +} + +void MemViewer::renderCarret(QPainter& painter, const int rowIndex, const int columnIndex) +{ + int posXHex = m_rowHeaderWidth + (m_charWidthEm * 2 + m_charWidthEm / 2) * columnIndex; + QColor oldPenColor = painter.pen().color(); + int carretPosX = posXHex + (m_carretBetweenHex ? m_charWidthEm : 0); + painter.setPen(QColor(Qt::red)); + painter.drawLine(carretPosX, + rowIndex * m_charHeight + (m_charHeight - fontMetrics().overlinePos()) + + m_columnHeaderHeight, + carretPosX, + rowIndex * m_charHeight + (m_charHeight - fontMetrics().overlinePos()) + + m_columnHeaderHeight + m_charHeight); + painter.setPen(oldPenColor); +} + +void MemViewer::determineMemoryTextRenderProperties(const int rowIndex, const int columnIndex, + bool& drawCarret, QColor& bgColor, + QColor& fgColor) +{ + if (rowIndex == m_byteSelectedPosY && columnIndex == m_byteSelectedPosX) + { + bgColor = QColor(Qt::darkBlue); + fgColor = QColor(Qt::white); + drawCarret = true; + } + // If the byte changed since the last data update + else if (m_lastRawMemoryData[rowIndex * 16 + columnIndex] != + m_updatedRawMemoryData[rowIndex * 16 + columnIndex]) + { + m_memoryMsElapsedLastChange[rowIndex * 16 + columnIndex] = m_elapsedTimer.elapsed(); + bgColor = QColor(Qt::red); + } + // If the last changes is less than a second old + else if (m_memoryMsElapsedLastChange[rowIndex * 16 + columnIndex] != 0 && + m_elapsedTimer.elapsed() - m_memoryMsElapsedLastChange[rowIndex * 16 + columnIndex] < + 1000) + { + QColor baseColor = QColor(Qt::red); + float alphaPercentage = (1000 - (m_elapsedTimer.elapsed() - + m_memoryMsElapsedLastChange[rowIndex * 16 + columnIndex])) / + (1000 / 100); + int newAlpha = std::trunc(baseColor.alpha() * (alphaPercentage / 100)); + bgColor = QColor(baseColor.red(), baseColor.green(), baseColor.blue(), newAlpha); + } +} + +void MemViewer::renderHexByte(QPainter& painter, const int rowIndex, const int columnIndex, + QColor& bgColor, QColor& fgColor, bool drawCarret) +{ + int posXHex = m_rowHeaderWidth + (m_charWidthEm * 2 + m_charWidthEm / 2) * columnIndex; + std::string hexByte = Common::formatMemoryToString( + m_updatedRawMemoryData + ((rowIndex * 16) + columnIndex), Common::MemType::type_byteArray, 1, + Common::MemBase::base_none, true); + QRect* currentByteRect = new QRect(posXHex, + m_columnHeaderHeight + rowIndex * m_charHeight + + (m_charHeight - fontMetrics().overlinePos()), + m_charWidthEm * 2, m_charHeight); + + painter.fillRect(*currentByteRect, bgColor); + if (drawCarret) + { + renderCarret(painter, rowIndex, columnIndex); + } + painter.setPen(fgColor); + painter.drawText(posXHex, (rowIndex + 1) * m_charHeight + m_columnHeaderHeight, + QString::fromStdString(hexByte)); +} + +void MemViewer::renderASCIIText(QPainter& painter, const int rowIndex, const int columnIndex, + QColor& bgColor, QColor& fgColor) +{ + std::string asciiStr = Common::formatMemoryToString( + m_updatedRawMemoryData + ((rowIndex * 16) + columnIndex), Common::MemType::type_string, 1, + Common::MemBase::base_none, true); + int asciiByte = (int)asciiStr[0]; + if (asciiByte > 0x7E || asciiByte < 0x20) + asciiStr = "."; + QRect* currentCharRect = new QRect( + (columnIndex * m_charWidthEm) + m_hexAsciiSeparatorPosX + m_charWidthEm / 2, + rowIndex * m_charHeight + (m_charHeight - fontMetrics().overlinePos()) + m_columnHeaderHeight, + m_charWidthEm, m_charHeight); + painter.fillRect(*currentCharRect, bgColor); + painter.setPen(fgColor); + painter.drawText((columnIndex * m_charWidthEm) + m_hexAsciiSeparatorPosX + m_charWidthEm / 2, + (rowIndex + 1) * m_charHeight + m_columnHeaderHeight, + QString::fromStdString(asciiStr)); +} + +void MemViewer::renderMemory(QPainter& painter, const int rowIndex, const int columnIndex) +{ + QColor oldPenColor = painter.pen().color(); + int posXHex = m_rowHeaderWidth + (m_charWidthEm * 2 + m_charWidthEm / 2) * columnIndex; + if (!(m_currentFirstAddress + (16 * rowIndex + columnIndex) >= m_memViewStart && + m_currentFirstAddress + (16 * rowIndex + columnIndex) < m_memViewEnd) || + !DolphinComm::DolphinAccessor::isValidConsoleAddress(m_currentFirstAddress + + (16 * rowIndex + columnIndex)) || + !m_validMemory) + { + painter.drawText(posXHex, (rowIndex + 1) * m_charHeight + m_columnHeaderHeight, "??"); + painter.drawText((columnIndex * m_charWidthEm) + m_hexAsciiSeparatorPosX + m_charWidthEm / 2, + (rowIndex + 1) * m_charHeight + m_columnHeaderHeight, "?"); + } + else + { + QColor bgColor = QColor(Qt::transparent); + QColor fgColor = QColor(Qt::black); + bool drawCarret = false; + + determineMemoryTextRenderProperties(rowIndex, columnIndex, drawCarret, bgColor, fgColor); + + renderHexByte(painter, rowIndex, columnIndex, bgColor, fgColor, drawCarret); + renderASCIIText(painter, rowIndex, columnIndex, bgColor, fgColor); + painter.setPen(oldPenColor); + } +} + +void MemViewer::paintEvent(QPaintEvent* event) +{ + QPainter painter(viewport()); + painter.setPen(QColor(Qt::black)); + + renderSeparatorLines(painter); + renderColumnsHeaderText(painter); + + for (int i = 0; i < 16; ++i) + { + renderRowHeaderText(painter, i); + for (int j = 0; j < 16; ++j) + { + renderMemory(painter, i, j); + } + } +} \ No newline at end of file diff --git a/Source/GUI/MemViewer/MemViewer.h b/Source/GUI/MemViewer/MemViewer.h new file mode 100644 index 00000000..ad030fb9 --- /dev/null +++ b/Source/GUI/MemViewer/MemViewer.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "../../Common/CommonTypes.h" + +class MemViewer : public QAbstractScrollArea +{ + Q_OBJECT + +public: + MemViewer(QWidget* parent); + ~MemViewer(); + QSize sizeHint() const override; + void mousePressEvent(QMouseEvent* event) override; + void keyPressEvent(QKeyEvent* event) override; + void paintEvent(QPaintEvent* event) override; + void scrollContentsBy(int dx, int dy) override; + void jumpToAddress(const u32 address); + void updateViewer(); + void memoryValidityChanged(const bool valid); + +signals: + void memErrorOccured(); + +private: + bool handleNaviguationKey(const int key); + bool writeHexCharacterToSelectedMemory(const std::string hexCharToWrite); + void updateMemoryData(); + void changeMemoryRegion(const bool isMEM2); + void renderColumnsHeaderText(QPainter& painter); + void renderRowHeaderText(QPainter& painter, const int rowIndex); + void renderSeparatorLines(QPainter& painter); + void renderMemory(QPainter& painter, const int rowIndex, const int columnIndex); + void renderHexByte(QPainter& painter, const int rowIndex, const int columnIndex, QColor& bgColor, + QColor& fgColor, bool drawCarret); + void renderASCIIText(QPainter& painter, const int rowIndex, const int columnIndex, + QColor& bgColor, QColor& fgColor); + void renderCarret(QPainter& painter, const int rowIndex, const int columnIndex); + void determineMemoryTextRenderProperties(const int rowIndex, const int columnIndex, + bool& drawCarret, QColor& bgColor, QColor& fgColor); + + int m_byteSelectedPosX = -1; + int m_byteSelectedPosY = -1; + int m_charWidthEm = 0; + int m_charHeight = 0; + int m_hexAreaWidth = 0; + int m_hexAreaHeight = 0; + int m_rowHeaderWidth = 0; + int m_columnHeaderHeight = 0; + int m_hexAsciiSeparatorPosX = 0; + char* m_updatedRawMemoryData = nullptr; + char* m_lastRawMemoryData = nullptr; + int* m_memoryMsElapsedLastChange = nullptr; + bool m_carretBetweenHex = false; + bool m_disableScrollContentEvent = false; + bool m_validMemory = false; + u32 m_currentFirstAddress = 0; + u32 m_memViewStart = 0; + u32 m_memViewEnd = 0; + QRect* m_curosrRect; + QList m_hexKeyList; + QElapsedTimer m_elapsedTimer; +}; \ No newline at end of file diff --git a/Source/GUI/MemViewer/MemViewerWidget.cpp b/Source/GUI/MemViewer/MemViewerWidget.cpp new file mode 100644 index 00000000..7d8fbf4b --- /dev/null +++ b/Source/GUI/MemViewer/MemViewerWidget.cpp @@ -0,0 +1,78 @@ +#include "MemViewerWidget.h" + +#include + +#include +#include + +#include "../../DolphinProcess/DolphinAccessor.h" + +MemViewerWidget::MemViewerWidget(QWidget* parent, u32 consoleAddress) : QWidget(parent) +{ + QLabel* lblJumpToAddress = new QLabel("Jump to an address: "); + m_txtJumpAddress = new QLineEdit(this); + m_btnGoToMEM1Start = new QPushButton("Go to the start of MEM1"); + connect(m_btnGoToMEM1Start, static_cast(&QPushButton::clicked), this, + &MemViewerWidget::onGoToMEM1Start); + m_btnGoToMEM2Start = new QPushButton("Go to the start of MEM2"); + connect(m_btnGoToMEM2Start, static_cast(&QPushButton::clicked), this, + &MemViewerWidget::onGoToMEM2Start); + QHBoxLayout* controls_layout = new QHBoxLayout(); + controls_layout->addWidget(lblJumpToAddress); + controls_layout->addWidget(m_txtJumpAddress); + controls_layout->addWidget(m_btnGoToMEM1Start); + controls_layout->addWidget(m_btnGoToMEM2Start); + + QVBoxLayout* main_layout = new QVBoxLayout(); + connect(m_txtJumpAddress, &QLineEdit::textChanged, this, + &MemViewerWidget::onJumpToAddressTextChanged); + m_memViewer = new MemViewer(this); + main_layout->addLayout(controls_layout); + main_layout->addWidget(m_memViewer); + setLayout(main_layout); + layout()->setSizeConstraint(QLayout::SetFixedSize); + + connect(m_memViewer, &MemViewer::memErrorOccured, this, &MemViewerWidget::mustUnhook); + + m_updateMemoryTimer = new QTimer(this); + connect(m_updateMemoryTimer, &QTimer::timeout, m_memViewer, &MemViewer::updateViewer); +} + +QTimer* MemViewerWidget::getUpdateTimer() const +{ + return m_updateMemoryTimer; +} + +void MemViewerWidget::onJumpToAddressTextChanged() +{ + std::stringstream ss(m_txtJumpAddress->text().toStdString()); + u32 jumpAddress = 0; + ss >> std::hex >> std::uppercase >> jumpAddress; + if (!ss.fail()) + { + m_memViewer->jumpToAddress(jumpAddress); + } +} + +void MemViewerWidget::onGoToMEM1Start() +{ + m_memViewer->jumpToAddress(Common::MEM1_START); +} + +void MemViewerWidget::onGoToMEM2Start() +{ + m_memViewer->jumpToAddress(Common::MEM2_START); +} + +void MemViewerWidget::hookStatusChanged(bool hook) +{ + m_txtJumpAddress->setEnabled(hook); + m_btnGoToMEM1Start->setEnabled(hook); + m_btnGoToMEM2Start->setEnabled(hook); + m_memViewer->memoryValidityChanged(hook); +} + +void MemViewerWidget::goToAddress(u32 address) +{ + m_memViewer->jumpToAddress(address); +} \ No newline at end of file diff --git a/Source/GUI/MemViewer/MemViewerWidget.h b/Source/GUI/MemViewer/MemViewerWidget.h new file mode 100644 index 00000000..e5a14f13 --- /dev/null +++ b/Source/GUI/MemViewer/MemViewerWidget.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include + +#include "MemViewer.h" + +class MemViewerWidget : public QWidget +{ + Q_OBJECT + +public: + MemViewerWidget(QWidget* parent, u32 consoleAddress); + + void onJumpToAddressTextChanged(); + void onGoToMEM1Start(); + void onGoToMEM2Start(); + QTimer* getUpdateTimer() const; + void hookStatusChanged(bool hook); + void goToAddress(u32 address); + +signals: + void mustUnhook(); + +private: + QLineEdit* m_txtJumpAddress; + QPushButton* m_btnGoToMEM1Start; + QPushButton* m_btnGoToMEM2Start; + QTimer* m_updateMemoryTimer; + MemViewer* m_memViewer; +}; \ No newline at end of file From 47649abeeb2473c12df1c7130f07fa73433ca32f Mon Sep 17 00:00:00 2001 From: aldelaro5 Date: Sun, 10 Sep 2017 04:10:43 -0400 Subject: [PATCH 5/9] Integrate the memory viewer in the main window Just adds a button to open it which just shows it and a way to open it using an address which the watcher will use the next commit. Also, accordingly enable or disable the button whether the program is hooked or not. --- Source/GUI/MainWindow.cpp | 36 ++++++++++++++++++++++++++++++++++++ Source/GUI/MainWindow.h | 6 ++++++ 2 files changed, 42 insertions(+) diff --git a/Source/GUI/MainWindow.cpp b/Source/GUI/MainWindow.cpp index 44b93268..57f75c1e 100644 --- a/Source/GUI/MainWindow.cpp +++ b/Source/GUI/MainWindow.cpp @@ -55,11 +55,16 @@ MainWindow::MainWindow() m_mem2StatusWidget = new QWidget(); m_mem2StatusWidget->setLayout(mem2Status_layout); + m_btnOpenMemViewer = new QPushButton("Open memory viewer"); + connect(m_btnOpenMemViewer, static_cast(&QPushButton::clicked), this, + &MainWindow::onOpenMenViewer); + QVBoxLayout* main_layout = new QVBoxLayout; main_layout->addWidget(m_lblDolphinStatus); main_layout->addLayout(dolphinHookButtons_layout); main_layout->addWidget(m_mem2StatusWidget); main_layout->addWidget(m_scanner); + main_layout->addWidget(m_btnOpenMemViewer); main_layout->addWidget(m_watcher); QWidget* main_widget = new QWidget(this); @@ -89,6 +94,18 @@ MainWindow::MainWindow() connect(m_scanner, &MemScanWidget::mustUnhook, this, &MainWindow::onUnhook); connect(m_watcher, &MemWatchWidget::mustUnhook, this, &MainWindow::onUnhook); + DolphinComm::DolphinAccessor::init(); + + m_viewer = new MemViewerWidget(this, Common::MEM1_START); + connect(m_viewer, &MemViewerWidget::mustUnhook, this, &MainWindow::onUnhook); + connect(m_watcher, + static_cast(&MemWatchWidget::goToAddressInViewer), this, + static_cast(&MainWindow::onOpenMemViewerWithAddress)); + m_dlgViewer = new QDialog(this); + QVBoxLayout* layout = new QVBoxLayout; + layout->addWidget(m_viewer); + m_dlgViewer->setLayout(layout); + // First attempt to hook onHookAttempt(); if (DolphinComm::DolphinAccessor::getStatus() == @@ -126,6 +143,17 @@ void MainWindow::onToggleMem2() updateMem2Status(); } +void MainWindow::onOpenMenViewer() +{ + m_dlgViewer->show(); +} + +void MainWindow::onOpenMemViewerWithAddress(u32 address) +{ + m_viewer->goToAddress(address); + m_dlgViewer->show(); +} + void MainWindow::updateMem2Status() { if (DolphinComm::DolphinAccessor::isMem2Enabled()) @@ -145,6 +173,7 @@ void MainWindow::updateDolphinHookingStatus() QString::number(DolphinComm::DolphinAccessor::getEmuRAMAddressStart(), 16).toUpper()); m_scanner->setEnabled(true); m_watcher->setEnabled(true); + m_btnOpenMemViewer->setEnabled(true); m_btnAttempHook->hide(); m_btnUnhook->show(); break; @@ -154,6 +183,7 @@ void MainWindow::updateDolphinHookingStatus() m_lblDolphinStatus->setText("Cannot hook to Dolphin, the process is not running"); m_scanner->setDisabled(true); m_watcher->setDisabled(true); + m_btnOpenMemViewer->setDisabled(true); m_btnAttempHook->show(); m_btnUnhook->hide(); break; @@ -164,6 +194,7 @@ void MainWindow::updateDolphinHookingStatus() "Cannot hook to Dolphin, the process is running, but no emulation has been started"); m_scanner->setDisabled(true); m_watcher->setDisabled(true); + m_btnOpenMemViewer->setDisabled(true); m_btnAttempHook->show(); m_btnUnhook->hide(); break; @@ -173,6 +204,7 @@ void MainWindow::updateDolphinHookingStatus() m_lblDolphinStatus->setText("Unhooked, press \"Hook\" to hook to Dolphin again"); m_scanner->setDisabled(true); m_watcher->setDisabled(true); + m_btnOpenMemViewer->setDisabled(true); m_btnAttempHook->show(); m_btnUnhook->hide(); break; @@ -190,6 +222,8 @@ void MainWindow::onHookAttempt() m_scanner->getUpdateTimer()->start(100); m_watcher->getUpdateTimer()->start(10); m_watcher->getFreezeTimer()->start(10); + m_viewer->getUpdateTimer()->start(100); + m_viewer->hookStatusChanged(true); m_mem2StatusWidget->setEnabled(true); } else @@ -203,6 +237,8 @@ void MainWindow::onUnhook() m_scanner->getUpdateTimer()->stop(); m_watcher->getUpdateTimer()->stop(); m_watcher->getFreezeTimer()->stop(); + m_viewer->getUpdateTimer()->stop(); + m_viewer->hookStatusChanged(false); m_mem2StatusWidget->setDisabled(true); DolphinComm::DolphinAccessor::unHook(); updateDolphinHookingStatus(); diff --git a/Source/GUI/MainWindow.h b/Source/GUI/MainWindow.h index 97073255..6910d571 100644 --- a/Source/GUI/MainWindow.h +++ b/Source/GUI/MainWindow.h @@ -8,6 +8,7 @@ #include "../Common/CommonTypes.h" #include "../Common/MemoryCommon.h" #include "MemScanner/MemScanWidget.h" +#include "MemViewer/MemViewerWidget.h" #include "MemWatcher/MemWatchWidget.h" class MainWindow : public QMainWindow @@ -24,6 +25,8 @@ class MainWindow : public QMainWindow void onUnhook(); void onAutoDetectMem2(); void onToggleMem2(); + void onOpenMenViewer(); + void onOpenMemViewerWithAddress(u32 address); void updateMem2Status(); void onOpenWatchFile(); @@ -35,13 +38,16 @@ class MainWindow : public QMainWindow private: MemWatchWidget* m_watcher; MemScanWidget* m_scanner; + MemViewerWidget* m_viewer; + QDialog* m_dlgViewer; QLabel* m_lblDolphinStatus; QPushButton* m_btnAttempHook; QPushButton* m_btnUnhook; QLabel* m_lblMem2Status; QPushButton* m_btnToggleMem2; QPushButton* m_btnMem2AutoDetect; + QPushButton* m_btnOpenMemViewer; QWidget* m_mem2StatusWidget; QMenu* m_menuFile; From 9811b8c96e7e6343c0ed99ad588fd809d4fc362a Mon Sep 17 00:00:00 2001 From: aldelaro5 Date: Sun, 10 Sep 2017 04:12:47 -0400 Subject: [PATCH 6/9] Add a right click menu on watch entries to open in the viewer It just emits the event which the main window will forward properly to the viewer with the watch's address. --- Source/GUI/MemWatcher/MemWatchWidget.cpp | 24 +++++++++++++++++++----- Source/GUI/MemWatcher/MemWatchWidget.h | 1 + 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/Source/GUI/MemWatcher/MemWatchWidget.cpp b/Source/GUI/MemWatcher/MemWatchWidget.cpp index dac3336e..8c65a352 100644 --- a/Source/GUI/MemWatcher/MemWatchWidget.cpp +++ b/Source/GUI/MemWatcher/MemWatchWidget.cpp @@ -100,6 +100,13 @@ void MemWatchWidget::onMemWatchContextMenuRequested(const QPoint& pos) { QMenu* contextMenu = new QMenu(this); + QAction* showInViewer = new QAction("Browse memory at this address...", this); + connect(showInViewer, &QAction::triggered, this, + [=] { emit goToAddressInViewer(entry->getConsoleAddress()); }); + + contextMenu->addAction(showInViewer); + contextMenu->addSeparator(); + QAction* viewDec = new QAction("View as Decimal", this); QAction* viewHex = new QAction("View as Hexadecimal", this); QAction* viewOct = new QAction("View as Octal", this); @@ -126,10 +133,11 @@ void MemWatchWidget::onMemWatchContextMenuRequested(const QPoint& pos) contextMenu->addAction(viewHex); contextMenu->addAction(viewOct); contextMenu->addAction(viewBin); + contextMenu->addSeparator(); int baseIndex = static_cast(entry->getBase()); Common::MemBase theBase = static_cast(baseIndex); - contextMenu->actions().at(baseIndex)->setEnabled(false); + contextMenu->actions().at(baseIndex + 2)->setEnabled(false); if (theBase == Common::MemBase::base_decimal) { @@ -145,16 +153,22 @@ void MemWatchWidget::onMemWatchContextMenuRequested(const QPoint& pos) m_hasUnsavedChanges = true; }); - contextMenu->addSeparator(); contextMenu->addAction(viewSigned); contextMenu->addAction(viewUnsigned); if (entry->isUnsigned()) - contextMenu->actions().at(6)->setEnabled(false); + { + contextMenu->actions() + .at(static_cast(Common::MemBase::base_none) + 4) + ->setEnabled(false); + } else - contextMenu->actions().at(5)->setEnabled(false); + { + contextMenu->actions() + .at(static_cast(Common::MemBase::base_none) + 3) + ->setEnabled(false); + } } - contextMenu->popup(m_watchView->viewport()->mapToGlobal(pos)); } } diff --git a/Source/GUI/MemWatcher/MemWatchWidget.h b/Source/GUI/MemWatcher/MemWatchWidget.h index 549c40c5..38d3bb56 100644 --- a/Source/GUI/MemWatcher/MemWatchWidget.h +++ b/Source/GUI/MemWatcher/MemWatchWidget.h @@ -31,6 +31,7 @@ class MemWatchWidget : public QWidget signals: void mustUnhook(); + void goToAddressInViewer(u32 address); private: QTreeView* m_watchView; From 0b3d32c349f9299567268e75097a1031690f6d72 Mon Sep 17 00:00:00 2001 From: aldelaro5 Date: Sun, 10 Sep 2017 04:27:06 -0400 Subject: [PATCH 7/9] Update the readme to precise the memory requirement Since it now uses a common cache for the scanner and the viewer, the memory usage CAN get high at all time, not just when scanning since the viewer will be there in the background, just not always shown. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8d657e1f..4ffd2f6c 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ For binary releases of this program, refer to [the "releases" page](https://gith ## System requirements Any x86_64 based system should theoretically work, however, please note that Mac OS is currently _not_ supported. Additionally, 32-bit x86 based systems are unsupported as Dolphin does not support them either. (Support for them was dropped a while ago.) -You absolutely need to have Dolphin running ***and*** _have the emulation started_ for this program to be of any use. As such, your system needs to meet Dolphin's [system requirements](https://github.com/dolphin-emu/dolphin#system-requirements). Additionally, have at least 250 MB of memory free; especially when scanning with MEM2 enabled, as it should only take this much when doing that. +You absolutely need to have Dolphin running ***and*** _have the emulation started_ for this program to be of any use. As such, your system needs to meet Dolphin's [system requirements](https://github.com/dolphin-emu/dolphin#system-requirements). Additionally, have at least 250 MB of memory free. On Linux, you need to install the Qt 5 package of your respective distribution. From 65899447e78c9560ba2321514396f9316734c00e Mon Sep 17 00:00:00 2001 From: aldelaro5 Date: Sun, 10 Sep 2017 04:30:00 -0400 Subject: [PATCH 8/9] Update the roadmap - memory viewer is done yay! done! --- Roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Roadmap.md b/Roadmap.md index 43ab06dc..ce2f015f 100644 --- a/Roadmap.md +++ b/Roadmap.md @@ -5,7 +5,7 @@ This file explains the features that are planned so far in the future of the pro ## Features to be implemented before 1.0: -* Memory viewer with real time updates +* DONE - Memory viewer with real time updates * If possible, with a little animation showing what bytes updated * Must also provide in place editing. * Greatly improve the UI. From 3e4ed64405f82f756d6a991d1806014de5301e35 Mon Sep 17 00:00:00 2001 From: aldelaro5 Date: Sun, 10 Sep 2017 04:31:51 -0400 Subject: [PATCH 9/9] Bump the version to 0.2 Because the memory viewer is finally done, this is worth a release. --- Source/GUI/MainWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/GUI/MainWindow.cpp b/Source/GUI/MainWindow.cpp index 57f75c1e..a5efec69 100644 --- a/Source/GUI/MainWindow.cpp +++ b/Source/GUI/MainWindow.cpp @@ -263,7 +263,7 @@ void MainWindow::onSaveAsWatchFile() void MainWindow::onAbout() { QMessageBox::about(this, "About Dolphin memory engine", - "Beta version 0.1.2\n\nA RAM search made to facilitate research and " + "Beta version 0.2\n\nA RAM search made to facilitate research and " "reverse engineering of GameCube and Wii games using the Dolphin " "emulator.\n\nThis program is licensed under the MIT license. You " "should have received a copy of the MIT license along with this program");