Core: Basic memory search

This commit is contained in:
Vicki Pfau 2017-06-05 17:28:51 -07:00
parent 202b7b1509
commit f2db707bb2
7 changed files with 702 additions and 0 deletions

View File

@ -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

286
src/core/mem-search.c Normal file
View File

@ -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);
}
}

View File

@ -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

View File

@ -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, &params, &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, &params, &res, 10000);
}
}
}
if (m_ui.typeStr->isChecked()) {
params.type = mCORE_MEMORY_SEARCH_STRING;
string = m_ui.value->text().toLocal8Bit();
params.valueStr = string;
mCoreMemorySearch(core, &params, &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);
}

View File

@ -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

View File

@ -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>

View File

@ -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<MemorySearch>());
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<IOViewer>());