mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-01-09 16:39:22 +01:00
752 lines
20 KiB
C++
752 lines
20 KiB
C++
#include "gui/wxgui.h"
|
|
|
|
#include "gui/MemorySearcherTool.h"
|
|
|
|
#include <vector>
|
|
#include <sstream>
|
|
#include <thread>
|
|
|
|
#include "config/ActiveSettings.h"
|
|
#include "gui/helpers/wxHelpers.h"
|
|
#include "Common/FileStream.h"
|
|
#include "util/IniParser/IniParser.h"
|
|
#include "util/helpers/StringHelpers.h"
|
|
#include "Cafe/CafeSystem.h"
|
|
|
|
enum
|
|
{
|
|
COMBOBOX_DATATYPE = wxID_HIGHEST + 1,
|
|
TEXT_VALUE,
|
|
BUTTON_START,
|
|
BUTTON_FILTER,
|
|
LIST_RESULTS,
|
|
LIST_ENTRYTABLE,
|
|
TIMER_REFRESH,
|
|
LIST_ENTRY_ADD,
|
|
LIST_ENTRY_REMOVE,
|
|
};
|
|
|
|
wxDEFINE_EVENT(wxEVT_SEARCH_FINISHED, wxCommandEvent);
|
|
|
|
wxBEGIN_EVENT_TABLE(MemorySearcherTool, wxFrame)
|
|
EVT_CLOSE(MemorySearcherTool::OnClose)
|
|
EVT_BUTTON(BUTTON_START, MemorySearcherTool::OnSearch)
|
|
EVT_BUTTON(BUTTON_FILTER, MemorySearcherTool::OnFilter)
|
|
EVT_TIMER(TIMER_REFRESH, MemorySearcherTool::OnTimerTick)
|
|
wxEND_EVENT_TABLE()
|
|
|
|
constexpr auto kMaxResultCount = 5000;
|
|
|
|
const wxString kDatatypeFloat = "float";
|
|
const wxString kDatatypeDouble = "double";
|
|
const wxString kDatatypeString = "string";
|
|
const wxString kDatatypeInt8 = "int8";
|
|
const wxString kDatatypeInt16 = "int16";
|
|
const wxString kDatatypeInt32 = "int32";
|
|
const wxString kDatatypeInt64 = "int64";
|
|
const wxString kDataTypeNames[] = {kDatatypeFloat,kDatatypeDouble,/*DATATYPE_STRING,*/kDatatypeInt8,kDatatypeInt16,kDatatypeInt32,kDatatypeInt64};
|
|
|
|
MemorySearcherTool::MemorySearcherTool(wxFrame* parent)
|
|
: wxFrame(parent, wxID_ANY, _("Memory Searcher"), wxDefaultPosition, wxSize(600, 540), wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL)
|
|
{
|
|
this->SetSizeHints(wxDefaultSize, wxDefaultSize);
|
|
this->wxWindowBase::SetBackgroundColour(*wxWHITE);
|
|
this->wxTopLevelWindowBase::SetMinSize(wxSize(600, 540));
|
|
|
|
auto* sizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
auto* row1 = new wxFlexGridSizer(0, 4, 0, 0);
|
|
row1->AddGrowableCol(1);
|
|
|
|
m_cbDataType = new wxComboBox(this, COMBOBOX_DATATYPE, kDatatypeFloat, wxDefaultPosition, wxDefaultSize, std::size(kDataTypeNames), kDataTypeNames, wxCB_READONLY);
|
|
m_textValue = new wxTextCtrl(this, TEXT_VALUE);
|
|
m_buttonStart = new wxButton(this, BUTTON_START, _("Search"));
|
|
m_buttonFilter = new wxButton(this, BUTTON_FILTER, _("Filter"));
|
|
m_buttonFilter->Disable();
|
|
|
|
row1->Add(m_cbDataType, 0, wxALL, 5);
|
|
row1->Add(m_textValue, 0, wxALL | wxEXPAND, 5);
|
|
row1->Add(m_buttonStart, 0, wxALL, 5);
|
|
row1->Add(m_buttonFilter, 0, wxALL, 5);
|
|
|
|
sizer->Add(row1, 0, wxEXPAND, 5);
|
|
|
|
auto* row2 = new wxFlexGridSizer(0, 1, 0, 0);
|
|
row2->AddGrowableCol(0);
|
|
|
|
m_gauge = new wxGauge(this, wxID_ANY, 100, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL);
|
|
m_gauge->SetValue(0);
|
|
m_gauge->Enable(false);
|
|
|
|
m_textEntryTable = new wxStaticText(this, wxID_ANY, _("Results"));
|
|
m_listResults = new wxListCtrl(this, LIST_RESULTS, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SORT_ASCENDING);
|
|
m_listResults->Bind(wxEVT_LEFT_DCLICK, &MemorySearcherTool::OnResultListClick, this);
|
|
{
|
|
wxListItem col0;
|
|
col0.SetId(0);
|
|
col0.SetText(_("Address"));
|
|
col0.SetWidth(100);
|
|
m_listResults->InsertColumn(0, col0);
|
|
wxListItem col1;
|
|
col1.SetId(1);
|
|
col1.SetText(_("Value"));
|
|
col1.SetWidth(250);
|
|
m_listResults->InsertColumn(1, col1);
|
|
}
|
|
|
|
auto textEntryTable = new wxStaticText(this, wxID_ANY, _("Stored Entries"));
|
|
m_listEntryTable = new wxDataViewListCtrl(this, LIST_ENTRYTABLE, wxDefaultPosition, wxSize(420, 200), wxDV_HORIZ_RULES);
|
|
m_listEntryTable->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &MemorySearcherTool::OnEntryListRightClick, this);
|
|
m_listEntryTable->Bind(wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_DONE, &MemorySearcherTool::OnItemEdited, this);
|
|
{
|
|
m_listEntryTable->AppendTextColumn(_("Description"), wxDATAVIEW_CELL_EDITABLE, 150, wxALIGN_LEFT, wxDATAVIEW_COL_SORTABLE);
|
|
m_listEntryTable->AppendTextColumn(_("Address"), wxDATAVIEW_CELL_INERT, 100, wxALIGN_LEFT, wxDATAVIEW_COL_SORTABLE);
|
|
m_listEntryTable->AppendTextColumn(_("Type"));
|
|
m_listEntryTable->AppendTextColumn(_("Value"), wxDATAVIEW_CELL_EDITABLE);
|
|
m_listEntryTable->AppendToggleColumn(_("Freeze"), wxDATAVIEW_CELL_ACTIVATABLE, 50, wxALIGN_LEFT, 0);
|
|
}
|
|
|
|
row2->AddGrowableRow(3);
|
|
row2->AddGrowableRow(5);
|
|
|
|
row2->Add(m_gauge, 0, wxALL | wxEXPAND, 5);
|
|
row2->Add(0, 10, 1, wxEXPAND, 5);
|
|
row2->Add(m_textEntryTable, 0, wxALL, 5);
|
|
row2->Add(m_listResults, 1, wxALL | wxEXPAND, 5);
|
|
row2->Add(textEntryTable, 0, wxALL, 5);
|
|
row2->Add(m_listEntryTable, 1, wxALL | wxEXPAND, 5);
|
|
|
|
sizer->Add(row2, 1, wxEXPAND, 5);
|
|
|
|
// load stored entries
|
|
Load();
|
|
|
|
this->Bind(wxEVT_SEARCH_FINISHED, &MemorySearcherTool::OnSearchFinished, this);
|
|
this->Bind(wxEVT_SET_GAUGE_VALUE, &MemorySearcherTool::OnUpdateGauge, this);
|
|
|
|
m_refresh_timer = new wxTimer(this, TIMER_REFRESH);
|
|
m_refresh_timer->Start(250);
|
|
|
|
this->SetSizer(sizer);
|
|
this->wxWindowBase::Layout();
|
|
|
|
this->Centre(wxBOTH);
|
|
}
|
|
|
|
MemorySearcherTool::~MemorySearcherTool()
|
|
{
|
|
m_refresh_timer->Stop();
|
|
|
|
m_running = false;
|
|
if (m_worker.joinable())
|
|
m_worker.join();
|
|
}
|
|
|
|
void MemorySearcherTool::OnTimerTick(wxTimerEvent& event)
|
|
{
|
|
RefreshResultList();
|
|
RefreshStashList();
|
|
}
|
|
|
|
void MemorySearcherTool::OnClose(wxCloseEvent& event)
|
|
{
|
|
Save();
|
|
event.Skip();
|
|
}
|
|
|
|
void MemorySearcherTool::OnSearchFinished(wxCommandEvent&)
|
|
{
|
|
FillResultList();
|
|
m_search_running = false;
|
|
m_buttonStart->Enable();
|
|
m_buttonFilter->Enable();
|
|
}
|
|
|
|
void MemorySearcherTool::OnUpdateGauge(wxSetGaugeValue& event)
|
|
{
|
|
auto* gauge = event.GetGauge();
|
|
const auto value = event.GetValue();
|
|
if (event.GetRange() != 0)
|
|
{
|
|
gauge->SetRange(event.GetRange());
|
|
gauge->SetValue(value);
|
|
return;
|
|
}
|
|
|
|
debug_printf("update gauge: %d + %d = %d (/%d)\n", gauge->GetValue(), value, gauge->GetValue() + value, gauge->GetRange());
|
|
gauge->SetValue(gauge->GetValue() + value);
|
|
}
|
|
|
|
void MemorySearcherTool::OnSearch(wxCommandEvent&)
|
|
{
|
|
if (m_clear_state)
|
|
{
|
|
Reset();
|
|
return;
|
|
}
|
|
|
|
if (m_search_running)
|
|
return;
|
|
|
|
if (m_textValue->IsEmpty())
|
|
return;
|
|
|
|
SetSearchDataType();
|
|
|
|
if (!VerifySearchValue())
|
|
{
|
|
wxMessageBox(_("Your entered value is not valid for the selected datatype."), _("Error"), wxICON_ERROR);
|
|
return;
|
|
}
|
|
m_buttonStart->Disable();
|
|
m_buttonFilter->Disable();
|
|
m_cbDataType->Disable();
|
|
m_buttonStart->SetLabelText(_("Clear"));
|
|
m_clear_state = true;
|
|
|
|
if (m_worker.joinable())
|
|
m_worker.join();
|
|
|
|
m_worker = std::thread([this]()
|
|
{
|
|
m_search_jobs.clear();
|
|
uint32 total_size = 0;
|
|
for (const auto& itr : memory_getMMURanges())
|
|
{
|
|
if (!m_running)
|
|
return;
|
|
|
|
if (!itr->isMapped())
|
|
continue;
|
|
|
|
void* ptr = itr->getPtr();
|
|
const uint32 size = itr->getSize();
|
|
|
|
total_size += (size / kGaugeStep);
|
|
m_search_jobs.emplace_back(std::async(std::launch::async, [this, ptr, size]() { return SearchValues(m_searchDataType, ptr, size); }));
|
|
}
|
|
|
|
wxQueueEvent(this, new wxSetGaugeValue(0, total_size, m_gauge));
|
|
|
|
ListType_t tmp;
|
|
for (auto& it : m_search_jobs)
|
|
{
|
|
const auto result = it.get();
|
|
tmp.insert(tmp.end(), result.cbegin(), result.cend());
|
|
}
|
|
|
|
std::unique_lock lock(m_mutex);
|
|
m_searchBuffer.swap(tmp);
|
|
lock.unlock();
|
|
|
|
wxQueueEvent(this, new wxCommandEvent(wxEVT_SEARCH_FINISHED));
|
|
});
|
|
}
|
|
|
|
void MemorySearcherTool::OnFilter(wxCommandEvent& event)
|
|
{
|
|
m_buttonStart->Disable();
|
|
m_buttonFilter->Disable();
|
|
|
|
if (m_worker.joinable())
|
|
m_worker.join();
|
|
|
|
m_worker = std::thread([this]()
|
|
{
|
|
const auto count = (uint32)m_searchBuffer.size();
|
|
wxQueueEvent(this, new wxSetGaugeValue(0, count, m_gauge));
|
|
|
|
auto tmp = FilterValues(m_searchDataType);
|
|
|
|
std::unique_lock lock(m_mutex);
|
|
m_searchBuffer.swap(tmp);
|
|
lock.unlock();
|
|
|
|
wxQueueEvent(this, new wxCommandEvent(wxEVT_SEARCH_FINISHED));
|
|
});
|
|
|
|
m_gauge->SetValue(0);
|
|
}
|
|
|
|
void MemorySearcherTool::Load()
|
|
{
|
|
const auto memorySearcherPath = ActiveSettings::GetUserDataPath("memorySearcher/{:016x}.ini", CafeSystem::GetForegroundTitleId());
|
|
auto memSearcherIniContents = FileStream::LoadIntoMemory(memorySearcherPath);
|
|
if (!memSearcherIniContents)
|
|
return;
|
|
|
|
IniParser iniParser(*memSearcherIniContents, _pathToUtf8(memorySearcherPath));
|
|
while (iniParser.NextSection())
|
|
{
|
|
auto option_description = iniParser.FindOption("description");
|
|
auto option_address = iniParser.FindOption("address");
|
|
auto option_type = iniParser.FindOption("type");
|
|
auto option_value = iniParser.FindOption("value");
|
|
|
|
if (!option_description || !option_address || !option_type || !option_value)
|
|
continue;
|
|
|
|
try
|
|
{
|
|
const auto addr = StringHelpers::ToInt64(*option_address);
|
|
if (!IsAddressValid(addr))
|
|
continue;
|
|
}
|
|
catch (const std::invalid_argument&)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bool found = false;
|
|
for (const auto& entry : kDataTypeNames)
|
|
{
|
|
if (boost::iequals(entry.ToStdString(), *option_type))
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found && !boost::iequals(kDatatypeString.ToStdString(), *option_type))
|
|
continue;
|
|
|
|
wxVector<wxVariant> data;
|
|
data.push_back(std::string(*option_description).c_str());
|
|
data.push_back(std::string(*option_address).c_str());
|
|
data.push_back(std::string(*option_type).c_str());
|
|
data.push_back(std::string(*option_value).c_str());
|
|
data.push_back(!option_value->empty());
|
|
m_listEntryTable->AppendItem(data);
|
|
}
|
|
}
|
|
|
|
void MemorySearcherTool::Save()
|
|
{
|
|
const auto memorySearcherPath = ActiveSettings::GetUserDataPath("memorySearcher/{:016x}.ini", CafeSystem::GetForegroundTitleId());
|
|
FileStream* fs = FileStream::createFile2(memorySearcherPath);
|
|
if (fs)
|
|
{
|
|
for (int i = 0; i < m_listEntryTable->GetItemCount(); ++i)
|
|
{
|
|
fs->writeLine("[Entry]");
|
|
|
|
std::string tmp = "description=" + std::string(m_listEntryTable->GetTextValue(i, 0).mbc_str());
|
|
fs->writeLine(tmp.c_str());
|
|
|
|
tmp = "address=" + std::string(m_listEntryTable->GetTextValue(i, 1).mbc_str());
|
|
fs->writeLine(tmp.c_str());
|
|
|
|
tmp = "type=" + std::string(m_listEntryTable->GetTextValue(i, 2).mbc_str());
|
|
fs->writeLine(tmp.c_str());
|
|
|
|
// only save value when FREEZE is active
|
|
if (m_listEntryTable->GetToggleValue(i, 4))
|
|
{
|
|
tmp = "value=" + std::string(m_listEntryTable->GetTextValue(i, 3).mbc_str());
|
|
}
|
|
else
|
|
{
|
|
tmp = "value=";
|
|
}
|
|
fs->writeLine(tmp.c_str());
|
|
|
|
fs->writeLine("");
|
|
}
|
|
delete fs;
|
|
}
|
|
}
|
|
|
|
bool MemorySearcherTool::IsAddressValid(uint32 addr)
|
|
{
|
|
for (const auto& itr : memory_getMMURanges())
|
|
{
|
|
if (!itr->isMapped())
|
|
continue;
|
|
|
|
void* ptr = itr->getPtr();
|
|
const uint32 size = itr->getSize();
|
|
|
|
MEMPTR start((uint8*)ptr);
|
|
MEMPTR end((uint8*)ptr + size);
|
|
if (start.GetMPTR() <= addr && addr < end.GetMPTR())
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void MemorySearcherTool::OnEntryListRightClick(wxDataViewEvent& event)
|
|
{
|
|
//void *data = reinterpret_cast<void *>(event.GetItem().GetData());
|
|
wxMenu mnu;
|
|
//mnu.SetClientData(data);
|
|
mnu.Append(LIST_ENTRY_ADD, _("&Add new entry"))->Enable(false);
|
|
mnu.Append(LIST_ENTRY_REMOVE, _("&Remove entry"));
|
|
mnu.Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MemorySearcherTool::OnPopupClick), nullptr, this);
|
|
PopupMenu(&mnu);
|
|
}
|
|
|
|
void MemorySearcherTool::OnResultListClick(wxMouseEvent& event)
|
|
{
|
|
long selectedIndex = -1;
|
|
|
|
while (true)
|
|
{
|
|
selectedIndex = m_listResults->GetNextItem(selectedIndex, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
|
if (selectedIndex == -1)
|
|
break;
|
|
|
|
long address = m_listResults->GetItemData(selectedIndex);
|
|
auto currValue = m_listResults->GetItemText(selectedIndex, 1);
|
|
|
|
char addressString[256];
|
|
sprintf(addressString, "0x%08lx", address);
|
|
|
|
// description, address, type, value, freeze
|
|
wxVector<wxVariant> data;
|
|
data.push_back("");
|
|
data.push_back(addressString);
|
|
data.push_back(m_cbDataType->GetValue());
|
|
data.push_back(currValue);
|
|
data.push_back(false);
|
|
m_listEntryTable->AppendItem(data);
|
|
}
|
|
}
|
|
|
|
void MemorySearcherTool::Reset()
|
|
{
|
|
m_searchBuffer.clear();
|
|
m_buttonStart->SetLabelText(_("Search"));
|
|
m_textEntryTable->SetLabelText(_("Results"));
|
|
m_buttonFilter->Disable();
|
|
m_cbDataType->Enable();
|
|
m_textValue->SetValue("");
|
|
m_listResults->DeleteAllItems();
|
|
m_clear_state = false;
|
|
}
|
|
|
|
bool MemorySearcherTool::VerifySearchValue() const
|
|
{
|
|
const auto s1 = m_textValue->GetValue();
|
|
const auto s2 = s1.c_str();
|
|
const auto inputString = s2.AsChar();
|
|
|
|
switch (m_searchDataType)
|
|
{
|
|
case SearchDataType_String:
|
|
return true;
|
|
case SearchDataType_Float:
|
|
{
|
|
float value;
|
|
return ConvertStringToType(inputString, value);
|
|
}
|
|
case SearchDataType_Double:
|
|
{
|
|
double value;
|
|
return ConvertStringToType(inputString, value);
|
|
}
|
|
case SearchDataType_Int8:
|
|
{
|
|
sint8 value;
|
|
return ConvertStringToType(inputString, value);
|
|
}
|
|
case SearchDataType_Int16:
|
|
{
|
|
sint16 value;
|
|
return ConvertStringToType(inputString, value);
|
|
}
|
|
case SearchDataType_Int32:
|
|
{
|
|
sint32 value;
|
|
return ConvertStringToType(inputString, value);
|
|
}
|
|
case SearchDataType_Int64:
|
|
{
|
|
sint64 value;
|
|
return ConvertStringToType(inputString, value);
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void MemorySearcherTool::FillResultList()
|
|
{
|
|
auto text = formatWxString(_("Results ({0})"), m_searchBuffer.size());
|
|
m_textEntryTable->SetLabelText(text);
|
|
|
|
m_listResults->DeleteAllItems();
|
|
|
|
if (m_searchBuffer.empty() || m_searchBuffer.size() > kMaxResultCount)
|
|
return;
|
|
|
|
for (const auto& address : m_searchBuffer)
|
|
{
|
|
const auto index = m_listResults->InsertItem(0, fmt::format("{:#08x}", address.GetMPTR()));
|
|
m_listResults->SetItemData(index, address.GetMPTR());
|
|
m_listResults->SetItem(index, 1, m_textValue->GetValue());
|
|
}
|
|
}
|
|
|
|
void MemorySearcherTool::RefreshResultList()
|
|
{
|
|
std::unique_lock lock(m_mutex);
|
|
if (m_searchBuffer.empty() || m_searchBuffer.size() > kMaxResultCount)
|
|
return;
|
|
|
|
for (int i = 0; i < m_listResults->GetItemCount(); ++i)
|
|
{
|
|
const auto addr = m_listResults->GetItemData(i);
|
|
switch (m_searchDataType)
|
|
{
|
|
case SearchDataType_String:
|
|
{
|
|
// TODO Peter
|
|
break;
|
|
}
|
|
case SearchDataType_Float:
|
|
{
|
|
const auto value = memory_read<float>(addr);
|
|
m_listResults->SetItem(i, 1, fmt::format("{}", value));
|
|
break;
|
|
}
|
|
case SearchDataType_Double:
|
|
{
|
|
const auto value = memory_read<double>(addr);
|
|
m_listResults->SetItem(i, 1, fmt::format("{}", value));
|
|
break;
|
|
}
|
|
case SearchDataType_Int8:
|
|
{
|
|
const auto value = memory_read<sint8>(addr);
|
|
m_listResults->SetItem(i, 1, fmt::format("{}", value));
|
|
break;
|
|
}
|
|
case SearchDataType_Int16:
|
|
{
|
|
const auto value = memory_read<sint16>(addr);
|
|
m_listResults->SetItem(i, 1, fmt::format("{}", value));
|
|
break;
|
|
}
|
|
case SearchDataType_Int32:
|
|
{
|
|
const auto value = memory_read<sint32>(addr);
|
|
m_listResults->SetItem(i, 1, fmt::format("{}", value));
|
|
break;
|
|
}
|
|
case SearchDataType_Int64:
|
|
{
|
|
const auto value = memory_read<sint64>(addr);
|
|
m_listResults->SetItem(i, 1, fmt::format("{}", value));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MemorySearcherTool::RefreshStashList()
|
|
{
|
|
for (int i = 0; i < m_listEntryTable->GetItemCount(); ++i)
|
|
{
|
|
auto freeze = m_listEntryTable->GetToggleValue(i, 4);
|
|
|
|
auto addressText = std::string(m_listEntryTable->GetTextValue(i, 1).mbc_str());
|
|
auto type = std::string(m_listEntryTable->GetTextValue(i, 2).mbc_str());
|
|
|
|
auto address = stol(addressText, nullptr, 16);
|
|
|
|
// if freeze is activated, set the value instead of refreshing it
|
|
if (freeze)
|
|
{
|
|
wxVariant var;
|
|
m_listEntryTable->GetValue(var, i, 3);
|
|
if (type == kDatatypeFloat)
|
|
{
|
|
auto value = (float)var.GetDouble();
|
|
memory_writeFloat(address, value);
|
|
}
|
|
else if (type == kDatatypeDouble)
|
|
{
|
|
auto value = var.GetDouble();
|
|
memory_writeDouble(address, value);
|
|
}
|
|
else if (type == kDatatypeInt8)
|
|
{
|
|
auto value = var.GetInteger();
|
|
memory_writeU8(address, value);
|
|
}
|
|
else if (type == kDatatypeInt16)
|
|
{
|
|
auto value = var.GetInteger();
|
|
memory_writeU16(address, value);
|
|
}
|
|
else if (type == kDatatypeInt32)
|
|
{
|
|
auto value = var.GetInteger();
|
|
memory_writeU32(address, value);
|
|
}
|
|
else if (type == kDatatypeInt64)
|
|
{
|
|
auto valueText = std::string(var.GetString().mbc_str());
|
|
auto value = stoull(valueText);
|
|
memory_writeU64(address, value);
|
|
}
|
|
else if (type == kDatatypeString)
|
|
{
|
|
auto valueText = std::string(var.GetString().mbc_str());
|
|
for (int i = 0; i < valueText.size(); ++i)
|
|
{
|
|
memory_writeU8(address + i, valueText[i]);
|
|
}
|
|
memory_writeU8(address + valueText.size(), 0x00);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (type == kDatatypeFloat)
|
|
{
|
|
auto value = memory_readFloat(address);
|
|
m_listEntryTable->SetValue(fmt::format("{}", value), i, 3);
|
|
}
|
|
else if (type == kDatatypeDouble)
|
|
{
|
|
auto value = memory_readDouble(address);
|
|
m_listEntryTable->SetValue(fmt::format("{}", value), i, 3);
|
|
}
|
|
else if (type == kDatatypeInt8)
|
|
{
|
|
auto value = (sint8)memory_readU8(address);
|
|
m_listEntryTable->SetValue(fmt::format("{}", (int)value), i, 3);
|
|
}
|
|
else if (type == kDatatypeInt16)
|
|
{
|
|
auto value = (sint16)memory_readU16(address);
|
|
m_listEntryTable->SetValue(fmt::format("{}", value), i, 3);
|
|
}
|
|
else if (type == kDatatypeInt32)
|
|
{
|
|
auto value = (sint32)memory_readU32(address);
|
|
m_listEntryTable->SetValue(fmt::format("{}", value), i, 3);
|
|
}
|
|
else if (type == kDatatypeInt64)
|
|
{
|
|
auto value = (sint64)memory_readU64(address);
|
|
m_listEntryTable->SetValue(fmt::format("{}", value), i, 3);
|
|
}
|
|
else if (type == kDatatypeString)
|
|
{
|
|
// TODO Peter
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MemorySearcherTool::SetSearchDataType()
|
|
{
|
|
const auto type = m_cbDataType->GetStringSelection();
|
|
if (type == kDatatypeFloat)
|
|
m_searchDataType = SearchDataType_Float;
|
|
else if (type == kDatatypeDouble)
|
|
m_searchDataType = SearchDataType_Double;
|
|
else if (type == kDatatypeInt8)
|
|
m_searchDataType = SearchDataType_Int8;
|
|
else if (type == kDatatypeInt16)
|
|
m_searchDataType = SearchDataType_Int16;
|
|
else if (type == kDatatypeInt32)
|
|
m_searchDataType = SearchDataType_Int32;
|
|
else if (type == kDatatypeInt64)
|
|
m_searchDataType = SearchDataType_Int64;
|
|
else if (type == kDatatypeString)
|
|
m_searchDataType = SearchDataType_String;
|
|
else
|
|
m_searchDataType = SearchDataType_None;
|
|
}
|
|
|
|
template <>
|
|
bool MemorySearcherTool::ConvertStringToType<signed char>(const char* inValue, sint8& outValue) const
|
|
{
|
|
sint16 tmp;
|
|
std::istringstream iss(inValue);
|
|
iss >> std::noskipws >> tmp;
|
|
if (iss && iss.eof())
|
|
{
|
|
if (SCHAR_MIN <= tmp && tmp <= SCHAR_MAX)
|
|
{
|
|
outValue = tmp;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void MemorySearcherTool::OnPopupClick(wxCommandEvent& event)
|
|
{
|
|
if (event.GetId() == LIST_ENTRY_REMOVE)
|
|
{
|
|
const int row = m_listEntryTable->GetSelectedRow();
|
|
if (row == -1)
|
|
return;
|
|
|
|
m_listEntryTable->DeleteItem(row);
|
|
}
|
|
}
|
|
|
|
void MemorySearcherTool::OnItemEdited(wxDataViewEvent& event)
|
|
{
|
|
auto column = event.GetColumn();
|
|
// Edit description
|
|
if (column == 0) { }
|
|
// Edit value
|
|
else if (column == 3)
|
|
{
|
|
auto row = m_listEntryTable->GetSelectedRow();
|
|
if (row == -1)
|
|
return;
|
|
|
|
auto addressText = std::string(m_listEntryTable->GetTextValue(row, 1).mbc_str());
|
|
uint32 address = stoul(addressText, nullptr, 16);
|
|
|
|
auto type = m_listEntryTable->GetTextValue(row, 2);
|
|
if (type == kDatatypeFloat)
|
|
{
|
|
auto value = (float)event.GetValue().GetDouble();
|
|
memory_writeFloat(address, value);
|
|
}
|
|
else if (type == kDatatypeDouble)
|
|
{
|
|
auto value = event.GetValue().GetDouble();
|
|
memory_writeDouble(address, value);
|
|
}
|
|
else if (type == kDatatypeInt8)
|
|
{
|
|
auto value = event.GetValue().GetInteger();
|
|
memory_writeU8(address, value);
|
|
}
|
|
else if (type == kDatatypeInt16)
|
|
{
|
|
auto value = event.GetValue().GetInteger();
|
|
memory_writeU16(address, value);
|
|
}
|
|
else if (type == kDatatypeInt32)
|
|
{
|
|
auto value = event.GetValue().GetInteger();
|
|
memory_writeU32(address, value);
|
|
}
|
|
else if (type == kDatatypeInt64)
|
|
{
|
|
auto valueText = std::string(event.GetValue().GetString().mbc_str());
|
|
auto value = stoull(valueText);
|
|
memory_writeU64(address, value);
|
|
}
|
|
else if (type == kDatatypeString)
|
|
{
|
|
auto valueText = std::string(event.GetValue().GetString().mbc_str());
|
|
for (int i = 0; i < valueText.size(); ++i)
|
|
{
|
|
memory_writeU8(address + i, valueText[i]);
|
|
}
|
|
memory_writeU8(address + valueText.size(), 0x00);
|
|
}
|
|
}
|
|
}
|