From f2db707bb235436fe539f53090b216ae8b0c8629 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 5 Jun 2017 17:28:51 -0700 Subject: [PATCH] Core: Basic memory search --- include/mgba/core/mem-search.h | 47 +++++ src/core/mem-search.c | 286 +++++++++++++++++++++++++++++++ src/platform/qt/CMakeLists.txt | 2 + src/platform/qt/MemorySearch.cpp | 130 ++++++++++++++ src/platform/qt/MemorySearch.h | 32 ++++ src/platform/qt/MemorySearch.ui | 199 +++++++++++++++++++++ src/platform/qt/Window.cpp | 6 + 7 files changed, 702 insertions(+) create mode 100644 include/mgba/core/mem-search.h create mode 100644 src/core/mem-search.c create mode 100644 src/platform/qt/MemorySearch.cpp create mode 100644 src/platform/qt/MemorySearch.h create mode 100644 src/platform/qt/MemorySearch.ui diff --git a/include/mgba/core/mem-search.h b/include/mgba/core/mem-search.h new file mode 100644 index 000000000..96d8947b5 --- /dev/null +++ b/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 + +CXX_GUARD_START + +#include + +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 diff --git a/src/core/mem-search.c b/src/core/mem-search.c new file mode 100644 index 000000000..7e6c3e8a8 --- /dev/null +++ b/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 + +#include +#include + +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); + } +} diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 711e15b8c..088e26d23 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -84,6 +84,7 @@ set(SOURCE_FILES LogController.cpp LogView.cpp MemoryModel.cpp + MemorySearch.cpp MemoryView.cpp MessagePainter.cpp MultiplayerController.cpp @@ -115,6 +116,7 @@ set(UI_FILES IOViewer.ui LoadSaveState.ui LogView.ui + MemorySearch.ui MemoryView.ui ObjView.ui OverrideView.ui diff --git a/src/platform/qt/MemorySearch.cpp b/src/platform/qt/MemorySearch.cpp new file mode 100644 index 000000000..2c700b163 --- /dev/null +++ b/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 +#include + +#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); +} diff --git a/src/platform/qt/MemorySearch.h b/src/platform/qt/MemorySearch.h new file mode 100644 index 000000000..7d2c6faa5 --- /dev/null +++ b/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 diff --git a/src/platform/qt/MemorySearch.ui b/src/platform/qt/MemorySearch.ui new file mode 100644 index 000000000..b68b19ee2 --- /dev/null +++ b/src/platform/qt/MemorySearch.ui @@ -0,0 +1,199 @@ + + + MemorySearch + + + + 0 + 0 + 631 + 378 + + + + + 540 + 241 + + + + Form + + + + + + + 1 + 0 + + + + false + + + false + + + + Address + + + + + Current Value + + + + + Type + + + + + + + + + + Value + + + + + + + + + + Type + + + + + + + Numeric + + + type + + + + + + + Text + + + type + + + + + + + Width + + + + + + + 1 Byte (8-bit) + + + width + + + + + + + 2 Bytes (16-bit) + + + width + + + + + + + 4 Bytes (32-bit) + + + width + + + + + + + Number type + + + + + + + Hexadecimal + + + + + + + Decimal + + + + + + + + + QDialogButtonBox::Close + + + + + + + + + Search + + + + + + + false + + + Search Within + + + + + + + false + + + View in Memory View + + + + + + + + + + + + + + diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 27a4dcaae..2406ed5a8 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -34,6 +34,7 @@ #include "LoadSaveState.h" #include "LogView.h" #include "MultiplayerController.h" +#include "MemorySearch.h" #include "MemoryView.h" #include "OverrideView.h" #include "ObjView.h" @@ -1422,6 +1423,11 @@ void Window::setupMenu(QMenuBar* menubar) { m_gameActions.append(memoryView); addControlledAction(toolsMenu, memoryView, "memoryView"); + QAction* memorySearch = new QAction(tr("Search memory..."), toolsMenu); + connect(memorySearch, &QAction::triggered, openTView()); + m_gameActions.append(memorySearch); + addControlledAction(toolsMenu, memorySearch, "memorySearch"); + #ifdef M_CORE_GBA QAction* ioViewer = new QAction(tr("View &I/O registers..."), toolsMenu); connect(ioViewer, &QAction::triggered, openTView());