Memory search: Save, Load, Undo and Redo
This commit is contained in:
parent
ae643213e9
commit
504dcb912e
5 changed files with 338 additions and 46 deletions
|
@ -74,12 +74,30 @@ public:
|
|||
template<typename T> void s_seqge() throw();
|
||||
template<typename T> void s_seqgt() throw();
|
||||
template<typename T> T v_read(uint64_t addr) throw();
|
||||
template<typename T> T v_readold(uint64_t addr) throw();
|
||||
template<typename T> void v_write(uint64_t addr, T val) throw();
|
||||
|
||||
static bool searchable_region(memory_region* r)
|
||||
{
|
||||
return (r && !r->readonly && !r->special);
|
||||
}
|
||||
/**
|
||||
* Savestate type.
|
||||
*/
|
||||
enum savestate_type
|
||||
{
|
||||
ST_PREVMEM,
|
||||
ST_SET,
|
||||
ST_ALL
|
||||
};
|
||||
/**
|
||||
* Save state.
|
||||
*/
|
||||
void savestate(std::vector<char>& buffer, enum savestate_type type) const;
|
||||
/**
|
||||
* Load state.
|
||||
*/
|
||||
void loadstate(const std::vector<char>& buffer);
|
||||
private:
|
||||
memory_space& mspace;
|
||||
std::vector<uint8_t> previous_content;
|
||||
|
|
|
@ -58,6 +58,9 @@ extern single_type filetype_sox;
|
|||
extern single_type filetype_sub;
|
||||
extern single_type filetype_png;
|
||||
extern single_type filetype_hexbookmarks;
|
||||
extern single_type filetype_memorysearch;
|
||||
extern single_type filetype_textfile;
|
||||
|
||||
|
||||
filedialog_output_params show_filedialog(wxWindow* parent, const std::string& title, const std::string& basepath,
|
||||
const filedialog_input_params& p, const std::string& defaultname, bool saving);
|
||||
|
|
|
@ -369,6 +369,34 @@ template<typename T> void memory_search::s_seqgt() throw() { search(search_seqgt
|
|||
template<typename T> T memory_search::v_read(uint64_t addr) throw() { return mspace.read<T>(addr); }
|
||||
template<typename T> void memory_search::v_write(uint64_t addr, T val) throw() { mspace.write<T>(addr, val); }
|
||||
|
||||
template<typename T> T memory_search::v_readold(uint64_t addr) throw()
|
||||
{
|
||||
uint64_t i = 0;
|
||||
auto t = mspace.lookup_linear(0);
|
||||
if(!t.first)
|
||||
return 0;
|
||||
//Search for linear range containing the address.
|
||||
while(true) {
|
||||
t = mspace.lookup_linear(i);
|
||||
if(!t.first)
|
||||
return 0;
|
||||
if(t.first->base <= addr && t.first->base + t.first->size > addr) {
|
||||
//Global address t.first->base <=> linear address i.
|
||||
uint64_t linaddr = addr - t.first->base + i;
|
||||
uint64_t maxr = t.first->size + t.first->base - addr;
|
||||
maxr = min(maxr, (uint64_t)sizeof(T));
|
||||
char buf[sizeof(T)] = {0};
|
||||
if(previous_content.size() < linaddr + maxr)
|
||||
return 0;
|
||||
memcpy(buf, &previous_content[linaddr], maxr);
|
||||
return read_of_endian<T>(buf, t.first->endian);
|
||||
}
|
||||
i += t.first->size - t.second;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename T> void memorysearch_pull_type(memory_search& s)
|
||||
{
|
||||
T val;
|
||||
|
@ -385,6 +413,7 @@ template<typename T> void memorysearch_pull_type(memory_search& s)
|
|||
eat_argument(&memory_search::s_seqge<T>);
|
||||
eat_argument(&memory_search::s_seqgt<T>);
|
||||
eat_argument(&memory_search::v_read<T>);
|
||||
eat_argument(&memory_search::v_readold<T>);
|
||||
eat_argument(&memory_search::v_write<T>);
|
||||
}
|
||||
|
||||
|
@ -400,6 +429,7 @@ template<typename T> void memorysearch_pull_type2(memory_search& s)
|
|||
eat_argument(&memory_search::s_ge<T>);
|
||||
eat_argument(&memory_search::s_gt<T>);
|
||||
eat_argument(&memory_search::v_read<T>);
|
||||
eat_argument(&memory_search::v_readold<T>);
|
||||
eat_argument(&memory_search::v_write<T>);
|
||||
}
|
||||
|
||||
|
@ -564,3 +594,62 @@ void memory_search::reset() throw(std::bad_alloc)
|
|||
i += t.first->size - t.second;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void memory_search::savestate(std::vector<char>& buffer, enum savestate_type type) const
|
||||
{
|
||||
size_t size;
|
||||
uint64_t linsize = mspace.get_linear_size();
|
||||
if(type == ST_PREVMEM)
|
||||
size = 9 + linsize;
|
||||
else if(type == ST_SET)
|
||||
size = 17 + (linsize + 63) / 64 * 8;
|
||||
else if(type == ST_ALL)
|
||||
size = 17 + linsize + (linsize + 63) / 64 * 8;
|
||||
else
|
||||
throw std::runtime_error("Invalid savestate type");
|
||||
buffer.resize(size);
|
||||
buffer[0] = type;
|
||||
write64ube(&buffer[1], linsize);
|
||||
size_t offset = 9;
|
||||
if(type == ST_PREVMEM || type == ST_ALL) {
|
||||
memcpy(&buffer[offset], &previous_content[0], min(linsize, (uint64_t)previous_content.size()));
|
||||
offset += linsize;
|
||||
}
|
||||
if(type == ST_SET || type == ST_ALL) {
|
||||
write64ube(&buffer[offset], candidates);
|
||||
offset += 8;
|
||||
size_t bound = min((linsize + 63) / 64, (uint64_t)still_in.size());
|
||||
for(unsigned i = 0; i < bound; i++) {
|
||||
write64ube(&buffer[offset], still_in[i]);
|
||||
offset += 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void memory_search::loadstate(const std::vector<char>& buffer)
|
||||
{
|
||||
if(buffer.size() < 9 || buffer[0] < ST_PREVMEM || buffer[0] > ST_ALL)
|
||||
throw std::runtime_error("Invalid memory search save");
|
||||
uint64_t linsize = read64ube(&buffer[1]);
|
||||
if(linsize != mspace.get_linear_size())
|
||||
throw std::runtime_error("Save size mismatch (not from this game)");
|
||||
if(!previous_content.size())
|
||||
reset();
|
||||
savestate_type type = (savestate_type)buffer[0];
|
||||
size_t offset = 9;
|
||||
if(type == ST_PREVMEM || type == ST_ALL) {
|
||||
memcpy(&previous_content[0], &buffer[offset], min(linsize, (uint64_t)previous_content.size()));
|
||||
offset += linsize;
|
||||
}
|
||||
if(type == ST_SET || type == ST_ALL) {
|
||||
candidates = read64ube(&buffer[offset]);
|
||||
offset += 8;
|
||||
size_t bound = min((linsize + 63) / 64, (uint64_t)still_in.size());
|
||||
for(unsigned i = 0; i < bound; i++) {
|
||||
still_in[i] = read64ube(&buffer[offset]);
|
||||
offset += 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -99,4 +99,5 @@ single_type filetype_sox("sox", "SoX file");
|
|||
single_type filetype_sub("sub", "Microsub subtitles");
|
||||
single_type filetype_png("png", "Portable Network Graphics");
|
||||
single_type filetype_hexbookmarks("lhb", "Hex editor bookmarks");
|
||||
|
||||
single_type filetype_memorysearch("lms", "Memory search save");
|
||||
single_type filetype_textfile("txt", "Text file");
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
#include "core/dispatch.hpp"
|
||||
#include "core/memorymanip.hpp"
|
||||
#include "core/memorywatch.hpp"
|
||||
#include "core/project.hpp"
|
||||
#include "library/string.hpp"
|
||||
#include "library/memorysearch.hpp"
|
||||
#include "library/int24.hpp"
|
||||
#include "library/zip.hpp"
|
||||
|
||||
#include "platform/wxwidgets/loadsave.hpp"
|
||||
#include "platform/wxwidgets/platform.hpp"
|
||||
#include "platform/wxwidgets/scrollbar.hpp"
|
||||
#include "platform/wxwidgets/textrender.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
@ -27,6 +31,13 @@
|
|||
#define wxID_DISQUALIFY (wxID_HIGHEST + 8)
|
||||
#define wxID_POKE (wxID_HIGHEST + 9)
|
||||
#define wxID_SHOW_HEXEDITOR (wxID_HIGHEST + 10)
|
||||
#define wxID_MENU_SAVE_PREVMEM (wxID_HIGHEST + 11)
|
||||
#define wxID_MENU_SAVE_SET (wxID_HIGHEST + 12)
|
||||
#define wxID_MENU_SAVE_ALL (wxID_HIGHEST + 13)
|
||||
#define wxID_MENU_LOAD (wxID_HIGHEST + 14)
|
||||
#define wxID_MENU_UNDO (wxID_HIGHEST + 15)
|
||||
#define wxID_MENU_REDO (wxID_HIGHEST + 16)
|
||||
#define wxID_MENU_DUMP_CANDIDATES (wxID_HIGHEST + 17)
|
||||
#define wxID_BUTTONS_BASE (wxID_HIGHEST + 128)
|
||||
|
||||
#define DATATYPES 12
|
||||
|
@ -37,6 +48,7 @@ memory_search* wxwindow_memorysearch_active();
|
|||
|
||||
namespace
|
||||
{
|
||||
unsigned UNDOHISTORY_MAXSIZE = 48;
|
||||
const char* watchchars = "bBwWoOdDqQfF";
|
||||
|
||||
wxwindow_memorysearch* mwatch;
|
||||
|
@ -255,6 +267,28 @@ public:
|
|||
template<void(memory_search::*sfn)()> void search_0();
|
||||
template<typename T, typename T2, void(memory_search::*sfn)(T2 val)> void search_1();
|
||||
template<typename T> void _do_poke_addr(uint64_t addr);
|
||||
template<typename T> std::string _do_format_signed(uint64_t addr, bool hex, bool old)
|
||||
{
|
||||
if(old)
|
||||
return format_number_signed<T>(msearch->v_readold<T>(addr), hex);
|
||||
else
|
||||
return format_number_signed<T>(lsnes_memory.read<T>(addr), hex);
|
||||
}
|
||||
template<typename T> std::string _do_format_unsigned(uint64_t addr, bool hex, bool old)
|
||||
{
|
||||
if(old)
|
||||
return format_number_unsigned<T>(msearch->v_readold<T>(addr), hex);
|
||||
else
|
||||
return format_number_unsigned<T>(lsnes_memory.read<T>(addr), hex);
|
||||
}
|
||||
template<typename T> std::string _do_format_float(uint64_t addr, bool hex, bool old)
|
||||
{
|
||||
if(old)
|
||||
return format_number_float(msearch->v_readold<T>(addr));
|
||||
else
|
||||
return format_number_float(lsnes_memory.read<T>(addr));
|
||||
}
|
||||
void dump_candidates_text();
|
||||
private:
|
||||
friend memory_search* wxwindow_memorysearch_active();
|
||||
friend class panel;
|
||||
|
@ -264,6 +298,10 @@ private:
|
|||
void on_mouse0(wxMouseEvent& e, bool polarity);
|
||||
void on_mousedrag();
|
||||
void on_mouse2(wxMouseEvent& e);
|
||||
void handle_undo_redo(bool redo);
|
||||
void push_undo();
|
||||
void handle_save(memory_search::savestate_type type);
|
||||
void handle_load();
|
||||
wxStaticText* count;
|
||||
scroll_bar* scroll;
|
||||
panel* matches;
|
||||
|
@ -280,6 +318,10 @@ private:
|
|||
bool toomany;
|
||||
int scroll_delta;
|
||||
std::set<std::string> vmas_enabled;
|
||||
wxMenuItem* undoitem;
|
||||
wxMenuItem* redoitem;
|
||||
std::list<std::vector<char>> undohistory;
|
||||
std::list<std::vector<char>> redohistory;
|
||||
};
|
||||
|
||||
namespace
|
||||
|
@ -299,6 +341,22 @@ namespace
|
|||
&wxwindow_memorysearch::_do_poke_addr<float>,
|
||||
&wxwindow_memorysearch::_do_poke_addr<double>,
|
||||
};
|
||||
|
||||
typedef std::string (wxwindow_memorysearch::*displayfn_t)(uint64_t, bool hexmode, bool old);
|
||||
displayfn_t displays[] = {
|
||||
&wxwindow_memorysearch::_do_format_signed<uint8_t>,
|
||||
&wxwindow_memorysearch::_do_format_unsigned<uint8_t>,
|
||||
&wxwindow_memorysearch::_do_format_signed<uint16_t>,
|
||||
&wxwindow_memorysearch::_do_format_unsigned<uint16_t>,
|
||||
&wxwindow_memorysearch::_do_format_signed<ss_uint24_t>,
|
||||
&wxwindow_memorysearch::_do_format_unsigned<ss_uint24_t>,
|
||||
&wxwindow_memorysearch::_do_format_signed<uint32_t>,
|
||||
&wxwindow_memorysearch::_do_format_signed<uint32_t>,
|
||||
&wxwindow_memorysearch::_do_format_unsigned<uint64_t>,
|
||||
&wxwindow_memorysearch::_do_format_unsigned<uint64_t>,
|
||||
&wxwindow_memorysearch::_do_format_float<float>,
|
||||
&wxwindow_memorysearch::_do_format_float<double>,
|
||||
};
|
||||
|
||||
struct searchtype searchtbl[] = {
|
||||
{
|
||||
|
@ -578,6 +636,38 @@ wxwindow_memorysearch::wxwindow_memorysearch()
|
|||
if(memory_search::searchable_region(i))
|
||||
vmas_enabled.insert(i->name);
|
||||
|
||||
wxMenuBar* menubar = new wxMenuBar();
|
||||
SetMenuBar(menubar);
|
||||
wxMenu* filemenu = new wxMenu();
|
||||
filemenu->Append(wxID_MENU_DUMP_CANDIDATES, wxT("Dump candidates..."));
|
||||
filemenu->AppendSeparator();
|
||||
filemenu->Append(wxID_MENU_SAVE_PREVMEM, wxT("Save previous memory..."));
|
||||
filemenu->Append(wxID_MENU_SAVE_SET, wxT("Save set of addresses..."));
|
||||
filemenu->Append(wxID_MENU_SAVE_ALL, wxT("Save previous memory and set of addresses..."));
|
||||
filemenu->AppendSeparator();
|
||||
filemenu->Append(wxID_MENU_LOAD, wxT("Load save..."));
|
||||
menubar->Append(filemenu, wxT("File"));
|
||||
wxMenu* editmenu = new wxMenu();
|
||||
undoitem = editmenu->Append(wxID_UNDO, wxT("Undo"));
|
||||
redoitem = editmenu->Append(wxID_REDO, wxT("Redo"));
|
||||
undoitem->Enable(false);
|
||||
redoitem->Enable(false);
|
||||
menubar->Append(editmenu, wxT("Edit"));
|
||||
Connect(wxID_MENU_DUMP_CANDIDATES, wxEVT_COMMAND_MENU_SELECTED,
|
||||
wxCommandEventHandler(wxwindow_memorysearch::on_button_click));
|
||||
Connect(wxID_MENU_SAVE_PREVMEM, wxEVT_COMMAND_MENU_SELECTED,
|
||||
wxCommandEventHandler(wxwindow_memorysearch::on_button_click));
|
||||
Connect(wxID_MENU_SAVE_SET, wxEVT_COMMAND_MENU_SELECTED,
|
||||
wxCommandEventHandler(wxwindow_memorysearch::on_button_click));
|
||||
Connect(wxID_MENU_SAVE_ALL, wxEVT_COMMAND_MENU_SELECTED,
|
||||
wxCommandEventHandler(wxwindow_memorysearch::on_button_click));
|
||||
Connect(wxID_MENU_LOAD, wxEVT_COMMAND_MENU_SELECTED,
|
||||
wxCommandEventHandler(wxwindow_memorysearch::on_button_click));
|
||||
Connect(wxID_UNDO, wxEVT_COMMAND_MENU_SELECTED,
|
||||
wxCommandEventHandler(wxwindow_memorysearch::on_button_click));
|
||||
Connect(wxID_REDO, wxEVT_COMMAND_MENU_SELECTED,
|
||||
wxCommandEventHandler(wxwindow_memorysearch::on_button_click));
|
||||
|
||||
dragging = false;
|
||||
toomany = true;
|
||||
matches->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(wxwindow_memorysearch::on_mouse), NULL, this);
|
||||
|
@ -634,51 +724,10 @@ void wxwindow_memorysearch::panel::prepare_paint()
|
|||
long j = 0;
|
||||
for(auto i : addrs2) {
|
||||
std::string row = hexformat_address(i) + " ";
|
||||
switch(_parent->typecode) {
|
||||
case 0:
|
||||
row += format_number_signed(lsnes_memory.read<uint8_t>(i), _parent->hexmode);
|
||||
break;
|
||||
case 1:
|
||||
row += format_number_unsigned(lsnes_memory.read<uint8_t>(i),
|
||||
_parent->hexmode);
|
||||
break;
|
||||
case 2:
|
||||
row += format_number_signed(lsnes_memory.read<uint16_t>(i), _parent->hexmode);
|
||||
break;
|
||||
case 3:
|
||||
row += format_number_unsigned(lsnes_memory.read<uint16_t>(i),
|
||||
_parent->hexmode);
|
||||
break;
|
||||
case 4:
|
||||
row += format_number_signed(lsnes_memory.read<ss_uint24_t>(i),
|
||||
_parent->hexmode);
|
||||
break;
|
||||
case 5:
|
||||
row += format_number_unsigned(lsnes_memory.read<ss_uint24_t>(i),
|
||||
_parent->hexmode);
|
||||
break;
|
||||
case 6:
|
||||
row += format_number_signed(lsnes_memory.read<uint32_t>(i), _parent->hexmode);
|
||||
break;
|
||||
case 7:
|
||||
row += format_number_unsigned(lsnes_memory.read<uint32_t>(i),
|
||||
_parent->hexmode);
|
||||
break;
|
||||
case 8:
|
||||
row += format_number_signed(lsnes_memory.read<uint64_t>(i),
|
||||
_parent->hexmode);
|
||||
break;
|
||||
case 9:
|
||||
row += format_number_unsigned(lsnes_memory.read<uint64_t>(i),
|
||||
_parent->hexmode);
|
||||
break;
|
||||
case 10:
|
||||
row += format_number_float(lsnes_memory.read<float>(i));
|
||||
break;
|
||||
case 11:
|
||||
row += format_number_float(lsnes_memory.read<double>(i));
|
||||
break;
|
||||
};
|
||||
row += (_parent->*displays[_parent->typecode])(i, _parent->hexmode, false);
|
||||
row += " (Was: ";
|
||||
row += (_parent->*displays[_parent->typecode])(i, _parent->hexmode, true);
|
||||
row += ')';
|
||||
if(j >= first && j < last)
|
||||
lines.push_back(row);
|
||||
addrs[j++] = i;
|
||||
|
@ -733,6 +782,110 @@ bool wxwindow_memorysearch::ShouldPreventAppExit() const
|
|||
return false;
|
||||
}
|
||||
|
||||
void wxwindow_memorysearch::dump_candidates_text()
|
||||
{
|
||||
try {
|
||||
std::string filename = choose_file_save(this, "Dump memory search", project_otherpath(),
|
||||
filetype_textfile);
|
||||
std::ofstream out(filename);
|
||||
auto ms = msearch;
|
||||
runemufn([ms, this, &out]() {
|
||||
std::list<uint64_t> addrs2 = ms->get_candidates();
|
||||
long j = 0;
|
||||
for(auto i : addrs2) {
|
||||
std::string row = hexformat_address(i) + " ";
|
||||
row += (this->*displays[this->typecode])(i, this->hexmode, false);
|
||||
row += " (Was: ";
|
||||
row += (this->*displays[this->typecode])(i, this->hexmode, true);
|
||||
row += ')';
|
||||
out << row << std::endl;
|
||||
}
|
||||
});
|
||||
if(!out)
|
||||
throw std::runtime_error("Can't write save file");
|
||||
} catch(canceled_exception& e) {
|
||||
} catch(std::exception& e) {
|
||||
show_message_ok(this, "Save error", std::string(e.what()), wxICON_WARNING);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void wxwindow_memorysearch::handle_save(memory_search::savestate_type type)
|
||||
{
|
||||
try {
|
||||
std::vector<char> state;
|
||||
msearch->savestate(state, type);
|
||||
std::string filename = choose_file_save(this, "Save memory search", project_otherpath(),
|
||||
filetype_memorysearch);
|
||||
std::ofstream out(filename, std::ios::binary);
|
||||
out.write(&state[0], state.size());
|
||||
if(!out)
|
||||
throw std::runtime_error("Can't write save file");
|
||||
} catch(canceled_exception& e) {
|
||||
} catch(std::exception& e) {
|
||||
show_message_ok(this, "Save error", std::string(e.what()), wxICON_WARNING);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void wxwindow_memorysearch::handle_load()
|
||||
{
|
||||
try {
|
||||
std::string filename = choose_file_load(this, "Load memory search", project_otherpath(),
|
||||
filetype_memorysearch);
|
||||
std::vector<char> state = read_file_relative(filename, "");
|
||||
push_undo();
|
||||
msearch->loadstate(state);
|
||||
update();
|
||||
} catch(canceled_exception& e) {
|
||||
} catch(std::exception& e) {
|
||||
show_message_ok(this, "Load error", std::string(e.what()), wxICON_WARNING);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void wxwindow_memorysearch::handle_undo_redo(bool redo)
|
||||
{
|
||||
std::list<std::vector<char>>& a = *(redo ? &redohistory : &undohistory);
|
||||
std::list<std::vector<char>>& b = *(redo ? &undohistory : &redohistory);
|
||||
if(!a.size()) {
|
||||
show_message_ok(this, "Undo/Redo error", "Can't find state to undo/redo to", wxICON_WARNING);
|
||||
return;
|
||||
}
|
||||
bool pushed = false;
|
||||
try {
|
||||
std::vector<char> state;
|
||||
msearch->savestate(state, memory_search::ST_SET);
|
||||
b.push_back(state);
|
||||
pushed = true;
|
||||
msearch->loadstate(a.back());
|
||||
a.pop_back();
|
||||
} catch(std::exception& e) {
|
||||
if(pushed)
|
||||
b.pop_back();
|
||||
show_message_ok(this, "Undo/Redo error", std::string(e.what()), wxICON_WARNING);
|
||||
return;
|
||||
}
|
||||
undoitem->Enable(undohistory.size());
|
||||
redoitem->Enable(redohistory.size());
|
||||
update();
|
||||
}
|
||||
|
||||
void wxwindow_memorysearch::push_undo()
|
||||
{
|
||||
try {
|
||||
std::vector<char> state;
|
||||
msearch->savestate(state, memory_search::ST_SET);
|
||||
undohistory.push_back(state);
|
||||
if(undohistory.size() > UNDOHISTORY_MAXSIZE)
|
||||
undohistory.pop_front();
|
||||
redohistory.clear();
|
||||
undoitem->Enable(undohistory.size());
|
||||
redoitem->Enable(redohistory.size());
|
||||
} catch(...) {
|
||||
}
|
||||
}
|
||||
|
||||
void wxwindow_memorysearch::on_mouse(wxMouseEvent& e)
|
||||
{
|
||||
if(e.RightUp() || (e.LeftUp() && e.ControlDown()))
|
||||
|
@ -825,6 +978,7 @@ void wxwindow_memorysearch::on_button_click(wxCommandEvent& e)
|
|||
{
|
||||
int id = e.GetId();
|
||||
if(id == wxID_RESET) {
|
||||
push_undo();
|
||||
msearch->reset();
|
||||
for(auto i : lsnes_memory.get_regions())
|
||||
if(memory_search::searchable_region(i) && !vmas_enabled.count(i->name))
|
||||
|
@ -869,6 +1023,7 @@ void wxwindow_memorysearch::on_button_click(wxCommandEvent& e)
|
|||
start = act_line;
|
||||
end = act_line + 1;
|
||||
}
|
||||
push_undo();
|
||||
for(long r = start; r < end; r++) {
|
||||
if(!addresses.count(r))
|
||||
return;
|
||||
|
@ -882,7 +1037,12 @@ void wxwindow_memorysearch::on_button_click(wxCommandEvent& e)
|
|||
wxwindow_memorysearch_vmasel* d = new wxwindow_memorysearch_vmasel(this, vmas_enabled);
|
||||
if(d->ShowModal() == wxID_OK)
|
||||
vmas_enabled = d->get_vmas();
|
||||
else {
|
||||
d->Destroy();
|
||||
return;
|
||||
}
|
||||
d->Destroy();
|
||||
push_undo();
|
||||
for(auto i : lsnes_memory.get_regions())
|
||||
if(memory_search::searchable_region(i) && !vmas_enabled.count(i->name))
|
||||
msearch->dq_range(i->base, i->last_address());
|
||||
|
@ -919,8 +1079,29 @@ void wxwindow_memorysearch::on_button_click(wxCommandEvent& e)
|
|||
}
|
||||
} else if(id >= wxID_BUTTONS_BASE && id < wxID_BUTTONS_BASE + (sizeof(searchtbl)/sizeof(searchtbl[0]))) {
|
||||
int button = id - wxID_BUTTONS_BASE;
|
||||
push_undo();
|
||||
uint64_t old_count = msearch->get_candidate_count();
|
||||
(this->*(searchtbl[button].searches[typecode]))();
|
||||
uint64_t new_count = msearch->get_candidate_count();
|
||||
if(old_count == new_count) {
|
||||
undohistory.pop_back(); //Shouldn't be undoable.
|
||||
undoitem->Enable(undohistory.size());
|
||||
}
|
||||
wxeditor_hexeditor_update();
|
||||
} else if(id == wxID_MENU_DUMP_CANDIDATES) {
|
||||
dump_candidates_text();
|
||||
} else if(id == wxID_MENU_SAVE_PREVMEM) {
|
||||
handle_save(memory_search::ST_PREVMEM);
|
||||
} else if(id == wxID_MENU_SAVE_SET) {
|
||||
handle_save(memory_search::ST_SET);
|
||||
} else if(id == wxID_MENU_SAVE_ALL) {
|
||||
handle_save(memory_search::ST_ALL);
|
||||
} else if(id == wxID_MENU_LOAD) {
|
||||
handle_load();
|
||||
} else if(id == wxID_UNDO) {
|
||||
handle_undo_redo(false);
|
||||
} else if(id == wxID_REDO) {
|
||||
handle_undo_redo(true);
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue