mirror of https://github.com/mgba-emu/mgba.git
Core: Basic memory search
This commit is contained in:
parent
202b7b1509
commit
f2db707bb2
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -84,6 +84,7 @@ set(SOURCE_FILES
|
||||||
LogController.cpp
|
LogController.cpp
|
||||||
LogView.cpp
|
LogView.cpp
|
||||||
MemoryModel.cpp
|
MemoryModel.cpp
|
||||||
|
MemorySearch.cpp
|
||||||
MemoryView.cpp
|
MemoryView.cpp
|
||||||
MessagePainter.cpp
|
MessagePainter.cpp
|
||||||
MultiplayerController.cpp
|
MultiplayerController.cpp
|
||||||
|
@ -115,6 +116,7 @@ set(UI_FILES
|
||||||
IOViewer.ui
|
IOViewer.ui
|
||||||
LoadSaveState.ui
|
LoadSaveState.ui
|
||||||
LogView.ui
|
LogView.ui
|
||||||
|
MemorySearch.ui
|
||||||
MemoryView.ui
|
MemoryView.ui
|
||||||
ObjView.ui
|
ObjView.ui
|
||||||
OverrideView.ui
|
OverrideView.ui
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
|
@ -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>
|
|
@ -34,6 +34,7 @@
|
||||||
#include "LoadSaveState.h"
|
#include "LoadSaveState.h"
|
||||||
#include "LogView.h"
|
#include "LogView.h"
|
||||||
#include "MultiplayerController.h"
|
#include "MultiplayerController.h"
|
||||||
|
#include "MemorySearch.h"
|
||||||
#include "MemoryView.h"
|
#include "MemoryView.h"
|
||||||
#include "OverrideView.h"
|
#include "OverrideView.h"
|
||||||
#include "ObjView.h"
|
#include "ObjView.h"
|
||||||
|
@ -1422,6 +1423,11 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
m_gameActions.append(memoryView);
|
m_gameActions.append(memoryView);
|
||||||
addControlledAction(toolsMenu, memoryView, "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
|
#ifdef M_CORE_GBA
|
||||||
QAction* ioViewer = new QAction(tr("View &I/O registers..."), toolsMenu);
|
QAction* ioViewer = new QAction(tr("View &I/O registers..."), toolsMenu);
|
||||||
connect(ioViewer, &QAction::triggered, openTView<IOViewer>());
|
connect(ioViewer, &QAction::triggered, openTView<IOViewer>());
|
||||||
|
|
Loading…
Reference in New Issue