Core: Basic memory search
Vicki Pfau vi@endrift.com
Mon, 05 Jun 2017 17:28:51 -0700
7 files changed,
702 insertions(+),
0 deletions(-)
A
include/mgba/core/mem-search.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef CORE_MEM_SEARCH_H +#define CORE_MEM_SEARCH_H + +#include <mgba-util/common.h> + +CXX_GUARD_START + +#include <mgba-util/vector.h> + +enum mCoreMemorySearchType { + mCORE_MEMORY_SEARCH_32, + mCORE_MEMORY_SEARCH_16, + mCORE_MEMORY_SEARCH_8, + mCORE_MEMORY_SEARCH_STRING, + mCORE_MEMORY_SEARCH_GUESS, +}; + +struct mCoreMemorySearchParams { + int memoryFlags; + enum mCoreMemorySearchType type; + union { + const char* valueStr; + uint32_t value32; + uint32_t value16; + uint32_t value8; + }; +}; + +struct mCoreMemorySearchResult { + uint32_t address; + int segment; + enum mCoreMemorySearchType type; +}; + +DECLARE_VECTOR(mCoreMemorySearchResults, struct mCoreMemorySearchResult); + +struct mCore; +void mCoreMemorySearch(struct mCore* core, const struct mCoreMemorySearchParams* params, struct mCoreMemorySearchResults* out, size_t limit); + +CXX_GUARD_END + +#endif
A
src/core/mem-search.c
@@ -0,0 +1,286 @@
+/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include <mgba/core/mem-search.h> + +#include <mgba/core/core.h> +#include <mgba/core/interface.h> + +DEFINE_VECTOR(mCoreMemorySearchResults, struct mCoreMemorySearchResult); + +static size_t _search32(const void* mem, size_t size, const struct mCoreMemoryBlock* block, uint32_t value32, struct mCoreMemorySearchResults* out, size_t limit) { + const uint32_t* mem32 = mem; + size_t found = 0; + uint32_t start = block->start; + uint32_t end = size; // TODO: Segments + size_t i; + // TODO: Big endian + for (i = 0; (!limit || found < limit) && i < end; i += 16) { + int mask = 0; + mask |= (mem32[(i >> 2) + 0] == value32) << 0; + mask |= (mem32[(i >> 2) + 1] == value32) << 1; + mask |= (mem32[(i >> 2) + 2] == value32) << 2; + mask |= (mem32[(i >> 2) + 3] == value32) << 3; + if (!mask) { + continue; + } + if ((mask & 1) && (!limit || found < limit)) { + struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); + res->address = start + i; + res->type = mCORE_MEMORY_SEARCH_32; + res->segment = -1; // TODO + ++found; + } + if ((mask & 2) && (!limit || found < limit)) { + struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); + res->address = start + i + 4; + res->type = mCORE_MEMORY_SEARCH_32; + res->segment = -1; // TODO + ++found; + } + if ((mask & 4) && (!limit || found < limit)) { + struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); + res->address = start + i + 8; + res->type = mCORE_MEMORY_SEARCH_32; + res->segment = -1; // TODO + ++found; + } + if ((mask & 8) && (!limit || found < limit)) { + struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); + res->address = start + i + 12; + res->type = mCORE_MEMORY_SEARCH_32; + res->segment = -1; // TODO + ++found; + } + } + // TODO: last 12 bytes + return found; +} + +static size_t _search16(const void* mem, size_t size, const struct mCoreMemoryBlock* block, uint16_t value16, struct mCoreMemorySearchResults* out, size_t limit) { + const uint16_t* mem16 = mem; + size_t found = 0; + uint32_t start = block->start; + uint32_t end = size; // TODO: Segments + size_t i; + // TODO: Big endian + for (i = 0; (!limit || found < limit) && i < end; i += 16) { + int mask = 0; + mask |= (mem16[(i >> 1) + 0] == value16) << 0; + mask |= (mem16[(i >> 1) + 1] == value16) << 1; + mask |= (mem16[(i >> 1) + 2] == value16) << 2; + mask |= (mem16[(i >> 1) + 3] == value16) << 3; + mask |= (mem16[(i >> 1) + 4] == value16) << 4; + mask |= (mem16[(i >> 1) + 5] == value16) << 5; + mask |= (mem16[(i >> 1) + 6] == value16) << 6; + mask |= (mem16[(i >> 1) + 7] == value16) << 7; + if (!mask) { + continue; + } + if ((mask & 1) && (!limit || found < limit)) { + struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); + res->address = start + i; + res->type = mCORE_MEMORY_SEARCH_16; + res->segment = -1; // TODO + ++found; + } + if ((mask & 2) && (!limit || found < limit)) { + struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); + res->address = start + i + 2; + res->type = mCORE_MEMORY_SEARCH_16; + res->segment = -1; // TODO + ++found; + } + if ((mask & 4) && (!limit || found < limit)) { + struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); + res->address = start + i + 4; + res->type = mCORE_MEMORY_SEARCH_16; + res->segment = -1; // TODO + ++found; + } + if ((mask & 8) && (!limit || found < limit)) { + struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); + res->address = start + i + 6; + res->type = mCORE_MEMORY_SEARCH_16; + res->segment = -1; // TODO + ++found; + } + if ((mask & 16) && (!limit || found < limit)) { + struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); + res->address = start + i + 8; + res->type = mCORE_MEMORY_SEARCH_16; + res->segment = -1; // TODO + ++found; + } + if ((mask & 32) && (!limit || found < limit)) { + struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); + res->address = start + i + 10; + res->type = mCORE_MEMORY_SEARCH_16; + res->segment = -1; // TODO + ++found; + } + if ((mask & 64) && (!limit || found < limit)) { + struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); + res->address = start + i + 12; + res->type = mCORE_MEMORY_SEARCH_16; + res->segment = -1; // TODO + ++found; + } + if ((mask & 128) && (!limit || found < limit)) { + struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); + res->address = start + i + 14; + res->type = mCORE_MEMORY_SEARCH_16; + res->segment = -1; // TODO + ++found; + } + } + // TODO: last 14 bytes + return found; +} + +static size_t _search8(const void* mem, size_t size, const struct mCoreMemoryBlock* block, uint8_t value8, struct mCoreMemorySearchResults* out, size_t limit) { + const uint8_t* mem8 = mem; + size_t found = 0; + uint32_t start = block->start; + uint32_t end = size; // TODO: Segments + size_t i; + for (i = 0; (!limit || found < limit) && i < end; i += 8) { + int mask = 0; + mask |= (mem8[i + 0] == value8) << 0; + mask |= (mem8[i + 1] == value8) << 1; + mask |= (mem8[i + 2] == value8) << 2; + mask |= (mem8[i + 3] == value8) << 3; + mask |= (mem8[i + 4] == value8) << 4; + mask |= (mem8[i + 5] == value8) << 5; + mask |= (mem8[i + 6] == value8) << 6; + mask |= (mem8[i + 7] == value8) << 7; + if (!mask) { + continue; + } + if ((mask & 1) && (!limit || found < limit)) { + struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); + res->address = start + i; + res->type = mCORE_MEMORY_SEARCH_8; + res->segment = -1; // TODO + ++found; + } + if ((mask & 2) && (!limit || found < limit)) { + struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); + res->address = start + i + 1; + res->type = mCORE_MEMORY_SEARCH_8; + res->segment = -1; // TODO + ++found; + } + if ((mask & 4) && (!limit || found < limit)) { + struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); + res->address = start + i + 2; + res->type = mCORE_MEMORY_SEARCH_8; + res->segment = -1; // TODO + ++found; + } + if ((mask & 8) && (!limit || found < limit)) { + struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); + res->address = start + i + 3; + res->type = mCORE_MEMORY_SEARCH_8; + res->segment = -1; // TODO + ++found; + } + if ((mask & 16) && (!limit || found < limit)) { + struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); + res->address = start + i + 4; + res->type = mCORE_MEMORY_SEARCH_8; + res->segment = -1; // TODO + ++found; + } + if ((mask & 32) && (!limit || found < limit)) { + struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); + res->address = start + i + 5; + res->type = mCORE_MEMORY_SEARCH_8; + res->segment = -1; // TODO + ++found; + } + if ((mask & 64) && (!limit || found < limit)) { + struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); + res->address = start + i + 6; + res->type = mCORE_MEMORY_SEARCH_8; + res->segment = -1; // TODO + ++found; + } + if ((mask & 128) && (!limit || found < limit)) { + struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); + res->address = start + i + 7; + res->type = mCORE_MEMORY_SEARCH_8; + res->segment = -1; // TODO + ++found; + } + } + // TODO: last 7 bytes + return found; +} + +static size_t _searchStr(const void* mem, size_t size, const struct mCoreMemoryBlock* block, const char* valueStr, struct mCoreMemorySearchResults* out, size_t limit) { + const char* memStr = mem; + size_t found = 0; + size_t len = strlen(valueStr); + uint32_t start = block->start; + uint32_t end = size; // TODO: Segments + size_t i; + for (i = 0; (!limit || found < limit) && i < end - len; ++i) { + if (!strncmp(valueStr, &memStr[i], len)) { + struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); + res->address = start + i; + res->type = mCORE_MEMORY_SEARCH_STRING; + res->segment = -1; // TODO + ++found; + } + } + return found; +} + +static size_t _searchGuess(const void* mem, size_t size, const struct mCoreMemoryBlock* block, const char* valueStr, struct mCoreMemorySearchResults* out, size_t limit) { + // TODO: As hex + // TODO: As decimal + // TODO: As BCD + // TODO: As str + return 0; +} + +static size_t _search(const void* mem, size_t size, const struct mCoreMemoryBlock* block, const struct mCoreMemorySearchParams* params, struct mCoreMemorySearchResults* out, size_t limit) { + switch (params->type) { + case mCORE_MEMORY_SEARCH_32: + return _search32(mem, size, block, params->value32, out, limit); + case mCORE_MEMORY_SEARCH_16: + return _search16(mem, size, block, params->value16, out, limit); + case mCORE_MEMORY_SEARCH_8: + return _search8(mem, size, block, params->value8, out, limit); + case mCORE_MEMORY_SEARCH_STRING: + return _searchStr(mem, size, block, params->valueStr, out, limit); + case mCORE_MEMORY_SEARCH_GUESS: + return _searchGuess(mem, size, block, params->valueStr, out, limit); + } +} + +void mCoreMemorySearch(struct mCore* core, const struct mCoreMemorySearchParams* params, struct mCoreMemorySearchResults* out, size_t limit) { + const struct mCoreMemoryBlock* blocks; + size_t nBlocks = core->listMemoryBlocks(core, &blocks); + size_t found = 0; + + size_t b; + for (b = 0; (!limit || found < limit) && b < nBlocks; ++b) { + size_t size; + const struct mCoreMemoryBlock* block = &blocks[b]; + if (!(block->flags & params->memoryFlags)) { + continue; + } + void* mem = core->getMemoryBlock(core, block->id, &size); + if (!mem) { + continue; + } + if (size > block->end - block->start) { + size = block->end - block->start; // TOOD: Segments + } + found += _search(mem, size, block, params, out, limit ? limit - found : 0); + } +}
M
src/platform/qt/CMakeLists.txt
→
src/platform/qt/CMakeLists.txt
@@ -84,6 +84,7 @@ LoadSaveState.cpp
LogController.cpp LogView.cpp MemoryModel.cpp + MemorySearch.cpp MemoryView.cpp MessagePainter.cpp MultiplayerController.cpp@@ -115,6 +116,7 @@ GIFView.ui
IOViewer.ui LoadSaveState.ui LogView.ui + MemorySearch.ui MemoryView.ui ObjView.ui OverrideView.ui
A
src/platform/qt/MemorySearch.cpp
@@ -0,0 +1,130 @@
+/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "MemorySearch.h" + +#include <mgba/core/core.h> +#include <mgba/core/mem-search.h> + +#include "GameController.h" + +using namespace QGBA; + +MemorySearch::MemorySearch(GameController* controller, QWidget* parent) + : QWidget(parent) + , m_controller(controller) +{ + m_ui.setupUi(this); + + connect(m_ui.search, &QPushButton::clicked, this, &MemorySearch::search); +} + +void MemorySearch::search() { + mCoreMemorySearchResults res; + mCoreMemorySearchResultsInit(&res, 0); + + mCoreMemorySearchParams params; + params.memoryFlags = mCORE_MEMORY_RW; + + GameController::Interrupter interrupter(m_controller); + if (!m_controller->isLoaded()) { + return; + } + mCore* core = m_controller->thread()->core; + + QByteArray string; + if (m_ui.typeNum->isChecked()) { + if (m_ui.bits8->isChecked()) { + params.type = mCORE_MEMORY_SEARCH_8; + } + if (m_ui.bits16->isChecked()) { + params.type = mCORE_MEMORY_SEARCH_16; + } + if (m_ui.bits32->isChecked()) { + params.type = mCORE_MEMORY_SEARCH_32; + } + if (m_ui.numHex->isChecked()) { + bool ok; + uint32_t v = m_ui.value->text().toUInt(&ok, 16); + if (ok) { + switch (params.type) { + case mCORE_MEMORY_SEARCH_8: + ok = v < 0x100; + params.value8 = v; + break; + case mCORE_MEMORY_SEARCH_16: + ok = v < 0x10000; + params.value16 = v; + break; + case mCORE_MEMORY_SEARCH_32: + params.value32 = v; + break; + default: + ok = false; + } + } + if (ok) { + mCoreMemorySearch(core, ¶ms, &res, 10000); + } + } + if (m_ui.numDec->isChecked()) { + bool ok; + uint32_t v = m_ui.value->text().toUInt(&ok, 10); + if (ok) { + switch (params.type) { + case mCORE_MEMORY_SEARCH_8: + ok = v < 0x100; + params.value8 = v; + break; + case mCORE_MEMORY_SEARCH_16: + ok = v < 0x10000; + params.value16 = v; + break; + case mCORE_MEMORY_SEARCH_32: + params.value32 = v; + break; + default: + ok = false; + } + } + if (ok) { + mCoreMemorySearch(core, ¶ms, &res, 10000); + } + } + } + if (m_ui.typeStr->isChecked()) { + params.type = mCORE_MEMORY_SEARCH_STRING; + string = m_ui.value->text().toLocal8Bit(); + params.valueStr = string; + mCoreMemorySearch(core, ¶ms, &res, 10000); + } + + m_ui.results->clear(); + m_ui.results->setRowCount(mCoreMemorySearchResultsSize(&res)); + for (size_t i = 0; i < mCoreMemorySearchResultsSize(&res); ++i) { + mCoreMemorySearchResult* result = mCoreMemorySearchResultsGetPointer(&res, i); + QTableWidgetItem* item = new QTableWidgetItem(QString("%1").arg(result->address, 8, 16, QChar('0'))); + m_ui.results->setItem(i, 0, item); + switch (result->type) { + case mCORE_MEMORY_SEARCH_8: + item = new QTableWidgetItem(QString("%1").arg(core->rawRead8(core, result->address, -1), 2, 16, QChar('0'))); + break; + case mCORE_MEMORY_SEARCH_16: + item = new QTableWidgetItem(QString("%1").arg(core->rawRead16(core, result->address, -1), 4, 16, QChar('0'))); + break; + case mCORE_MEMORY_SEARCH_GUESS: + case mCORE_MEMORY_SEARCH_32: + item = new QTableWidgetItem(QString("%1").arg(core->rawRead32(core, result->address, -1), 8, 16, QChar('0'))); + break; + case mCORE_MEMORY_SEARCH_STRING: + item = new QTableWidgetItem(params.valueStr); // TODO + } + m_ui.results->setItem(i, 1, item); + } + m_ui.results->sortItems(0); + + mCoreMemorySearchResultsDeinit(&res); +}
A
src/platform/qt/MemorySearch.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef QGBA_MEMORY_SEARCH +#define QGBA_MEMORY_SEARCH + +#include "ui_MemorySearch.h" + +namespace QGBA { + +class GameController; + +class MemorySearch : public QWidget { +Q_OBJECT + +public: + MemorySearch(GameController* controller, QWidget* parent = nullptr); + +public slots: + void search(); + +private: + Ui::MemorySearch m_ui; + + GameController* m_controller; +}; + +} + +#endif
A
src/platform/qt/MemorySearch.ui
@@ -0,0 +1,199 @@
+<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MemorySearch</class> + <widget class="QWidget" name="MemorySearch"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>631</width> + <height>378</height> + </rect> + </property> + <property name="minimumSize"> + <size> + <width>540</width> + <height>241</height> + </size> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="1"> + <widget class="QTableWidget" name="results"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="showGrid"> + <bool>false</bool> + </property> + <attribute name="verticalHeaderVisible"> + <bool>false</bool> + </attribute> + <column> + <property name="text"> + <string>Address</string> + </property> + </column> + <column> + <property name="text"> + <string>Current Value</string> + </property> + </column> + <column> + <property name="text"> + <string>Type</string> + </property> + </column> + </widget> + </item> + <item row="0" column="0"> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Value</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="value"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Type</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QRadioButton" name="typeNum"> + <property name="text"> + <string>Numeric</string> + </property> + <attribute name="buttonGroup"> + <string notr="true">type</string> + </attribute> + </widget> + </item> + <item row="2" column="1"> + <widget class="QRadioButton" name="typeStr"> + <property name="text"> + <string>Text</string> + </property> + <attribute name="buttonGroup"> + <string notr="true">type</string> + </attribute> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Width</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QRadioButton" name="bits8"> + <property name="text"> + <string>1 Byte (8-bit)</string> + </property> + <attribute name="buttonGroup"> + <string notr="true">width</string> + </attribute> + </widget> + </item> + <item row="4" column="1"> + <widget class="QRadioButton" name="bits16"> + <property name="text"> + <string>2 Bytes (16-bit)</string> + </property> + <attribute name="buttonGroup"> + <string notr="true">width</string> + </attribute> + </widget> + </item> + <item row="5" column="1"> + <widget class="QRadioButton" name="bits32"> + <property name="text"> + <string>4 Bytes (32-bit)</string> + </property> + <attribute name="buttonGroup"> + <string notr="true">width</string> + </attribute> + </widget> + </item> + <item row="6" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Number type</string> + </property> + </widget> + </item> + <item row="6" column="1"> + <widget class="QCheckBox" name="numHex"> + <property name="text"> + <string>Hexadecimal</string> + </property> + </widget> + </item> + <item row="7" column="1"> + <widget class="QCheckBox" name="numDec"> + <property name="text"> + <string>Decimal</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="2" column="0" colspan="2"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="standardButtons"> + <set>QDialogButtonBox::Close</set> + </property> + </widget> + </item> + <item row="1" column="0" colspan="2"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QPushButton" name="search"> + <property name="text"> + <string>Search</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="searchWithin"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Search Within</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="viewMem"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>View in Memory View</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> + <buttongroups> + <buttongroup name="type"/> + <buttongroup name="width"/> + </buttongroups> +</ui>
M
src/platform/qt/Window.cpp
→
src/platform/qt/Window.cpp
@@ -34,6 +34,7 @@ #include "IOViewer.h"
#include "LoadSaveState.h" #include "LogView.h" #include "MultiplayerController.h" +#include "MemorySearch.h" #include "MemoryView.h" #include "OverrideView.h" #include "ObjView.h"@@ -1421,6 +1422,11 @@ QAction* memoryView = new QAction(tr("View memory..."), toolsMenu);
connect(memoryView, &QAction::triggered, openTView<MemoryView>()); m_gameActions.append(memoryView); addControlledAction(toolsMenu, memoryView, "memoryView"); + + QAction* memorySearch = new QAction(tr("Search memory..."), toolsMenu); + connect(memorySearch, &QAction::triggered, openTView<MemorySearch>()); + m_gameActions.append(memorySearch); + addControlledAction(toolsMenu, memorySearch, "memorySearch"); #ifdef M_CORE_GBA QAction* ioViewer = new QAction(tr("View &I/O registers..."), toolsMenu);