Skip to content

Commit

Permalink
Merge pull request #1334 from lioncash/cheats
Browse files Browse the repository at this point in the history
Break Cheat Manager components out into their own source files.
  • Loading branch information
skidau committed Oct 21, 2014
2 parents 3022da5 + 6501923 commit 833ff4a
Show file tree
Hide file tree
Showing 15 changed files with 966 additions and 867 deletions.
6 changes: 4 additions & 2 deletions Source/Core/DolphinWX/CMakeLists.txt
Expand Up @@ -24,8 +24,11 @@ endif()
set(GUI_SRCS
ARCodeAddEdit.cpp
AboutDolphin.cpp
CheatsWindow.cpp
ConfigMain.cpp
Cheats/CheatSearchTab.cpp
Cheats/CheatsWindow.cpp
Cheats/CreateCodeDialog.cpp
Cheats/GeckoCodeDiag.cpp
Debugger/BreakpointDlg.cpp
Debugger/BreakpointView.cpp
Debugger/BreakpointWindow.cpp
Expand All @@ -47,7 +50,6 @@ set(GUI_SRCS
FrameAui.cpp
FrameTools.cpp
GameListCtrl.cpp
GeckoCodeDiag.cpp
HotkeyDlg.cpp
ISOFile.cpp
ISOProperties.cpp
Expand Down
319 changes: 319 additions & 0 deletions Source/Core/DolphinWX/Cheats/CheatSearchTab.cpp
@@ -0,0 +1,319 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.

#include <wx/button.h>
#include <wx/choice.h>
#include <wx/event.h>
#include <wx/listbox.h>
#include <wx/panel.h>
#include <wx/radiobut.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/string.h>
#include <wx/textctrl.h>
#include <wx/window.h>

#include "Common/CommonFuncs.h"
#include "Common/CommonTypes.h"
#include "Common/StringUtil.h"
#include "Core/HW/Memmap.h"
#include "DolphinWX/WxUtils.h"
#include "DolphinWX/Cheats/CheatSearchTab.h"
#include "DolphinWX/Cheats/CreateCodeDialog.h"

namespace
{
const int MAX_CHEAT_SEARCH_RESULTS_DISPLAY = 1024;
}

CheatSearchTab::CheatSearchTab(wxWindow* const parent)
: wxPanel(parent, -1)
{
// first scan button
m_btn_init_scan = new wxButton(this, -1, _("New Scan"));
m_btn_init_scan->Bind(wxEVT_BUTTON, &CheatSearchTab::StartNewSearch, this);

// next scan button
m_btn_next_scan = new wxButton(this, -1, _("Next Scan"));
m_btn_next_scan->Bind(wxEVT_BUTTON, &CheatSearchTab::FilterCheatSearchResults, this);
m_btn_next_scan->Disable();

// data size radio buttons
m_size_radiobtn.rad_8 = new wxRadioButton(this, -1, _("8 bit"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
m_size_radiobtn.rad_16 = new wxRadioButton(this, -1, _("16 bit"));
m_size_radiobtn.rad_32 = new wxRadioButton(this, -1, _("32 bit"));
m_size_radiobtn.rad_8->SetValue(true);

// data sizes groupbox
wxStaticBoxSizer* const sizer_cheat_new_search = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Data Size"));
sizer_cheat_new_search->Add(m_size_radiobtn.rad_8, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxALIGN_CENTER_VERTICAL, 5);
sizer_cheat_new_search->Add(m_size_radiobtn.rad_16, 0, wxRIGHT | wxBOTTOM | wxALIGN_CENTER_VERTICAL, 5);
sizer_cheat_new_search->Add(m_size_radiobtn.rad_32, 0, wxRIGHT | wxBOTTOM | wxALIGN_CENTER_VERTICAL, 5);

// result controls
m_lbox_search_results = new wxListBox(this, -1);
m_label_results_count = new wxStaticText(this, -1, _("Count:"));

// create AR code button
wxButton* const button_cheat_search_copy_address = new wxButton(this, -1, _("Create AR Code"));
button_cheat_search_copy_address->Bind(wxEVT_BUTTON, &CheatSearchTab::CreateARCode, this);

// results groupbox
wxStaticBoxSizer* const sizer_cheat_search_results = new wxStaticBoxSizer(wxVERTICAL, this, _("Results"));
sizer_cheat_search_results->Add(m_label_results_count, 0, wxALIGN_LEFT | wxALL, 5);
sizer_cheat_search_results->Add(m_lbox_search_results, 1, wxEXPAND | wxALL, 5);
sizer_cheat_search_results->Add(button_cheat_search_copy_address, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 5);

// Search value radio buttons
m_value_x_radiobtn.rad_oldvalue = new wxRadioButton(this, -1, _("Previous Value"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
m_value_x_radiobtn.rad_uservalue = new wxRadioButton(this, -1, "");
m_value_x_radiobtn.rad_oldvalue->SetValue(true);

// search value textbox
m_textctrl_value_x = new wxTextCtrl(this, -1, "0x0", wxDefaultPosition, wxSize(96,-1));
m_textctrl_value_x->Bind(wxEVT_SET_FOCUS, &CheatSearchTab::ApplyFocus, this);

wxBoxSizer* const sizer_cheat_filter_text = new wxBoxSizer(wxHORIZONTAL);
sizer_cheat_filter_text->Add(m_value_x_radiobtn.rad_uservalue, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
sizer_cheat_filter_text->Add(m_textctrl_value_x, 1, wxALIGN_CENTER_VERTICAL, 5);

// value groupbox
wxStaticBoxSizer* const sizer_cheat_search_filter_x = new wxStaticBoxSizer(wxVERTICAL, this, _("Value"));
sizer_cheat_search_filter_x->Add(m_value_x_radiobtn.rad_oldvalue, 0, wxLEFT | wxRIGHT | wxBOTTOM, 5);
sizer_cheat_search_filter_x->Add(sizer_cheat_filter_text, 0, wxALL | wxEXPAND, 5);

// filter types in the compare dropdown
static const wxString searches[] = {
_("Unknown"),
_("Not Equal"),
_("Equal"),
_("Greater Than"),
_("Less Than"),
// TODO: Implement between search.
//_("Between"),
};

m_search_type = new wxChoice(this, -1, wxDefaultPosition, wxDefaultSize, sizeof(searches)/sizeof(*searches), searches);
m_search_type->Select(0);

wxStaticBoxSizer* const sizer_cheat_search_filter = new wxStaticBoxSizer(wxVERTICAL, this, _("Search Filter"));
sizer_cheat_search_filter->Add(sizer_cheat_search_filter_x, 0, wxALL | wxEXPAND, 5);
sizer_cheat_search_filter->Add(m_search_type, 0, wxALL, 5);

// left sizer
wxBoxSizer* const sizer_left = new wxBoxSizer(wxVERTICAL);
sizer_left->Add(sizer_cheat_search_results, 1, wxEXPAND, 5);

// button sizer
wxBoxSizer* boxButtons = new wxBoxSizer(wxHORIZONTAL);
boxButtons->Add(m_btn_init_scan, 1, wxRIGHT, 5);
boxButtons->Add(m_btn_next_scan, 1);

// right sizer
wxBoxSizer* const sizer_right = new wxBoxSizer(wxVERTICAL);
sizer_right->Add(sizer_cheat_new_search, 0, wxBOTTOM, 5);
sizer_right->Add(sizer_cheat_search_filter, 0, wxEXPAND | wxBOTTOM, 5);
sizer_right->AddStretchSpacer(1);
sizer_right->Add(boxButtons, 0, wxTOP | wxEXPAND, 5);

// main sizer
wxBoxSizer* const sizer_main = new wxBoxSizer(wxHORIZONTAL);
sizer_main->Add(sizer_left, 1, wxEXPAND | wxALL, 5);
sizer_main->Add(sizer_right, 0, wxEXPAND | wxALL, 5);

SetSizerAndFit(sizer_main);
}

void CheatSearchTab::StartNewSearch(wxCommandEvent& WXUNUSED (event))
{
const u8* const memptr = Memory::GetPointer(0);
if (memptr == nullptr)
{
WxUtils::ShowErrorDialog(_("A game is not currently running."));
return;
}

// Determine the user-selected data size for this search.
m_search_type_size =
m_size_radiobtn.rad_8->GetValue() +
(m_size_radiobtn.rad_16->GetValue() << 1) +
(m_size_radiobtn.rad_32->GetValue() << 2);

// Set up the search results efficiently to prevent automatic re-allocations.
m_search_results.clear();
m_search_results.reserve(Memory::RAM_SIZE / m_search_type_size);

// Enable the "Next Scan" button.
m_btn_next_scan->Enable();

CheatSearchResult r;
// can I assume cheatable values will be aligned like this?
for (u32 addr = 0; addr != Memory::RAM_SIZE; addr += m_search_type_size)
{
r.address = addr;
memcpy(&r.old_value, memptr + addr, m_search_type_size);
m_search_results.push_back(r);
}

UpdateCheatSearchResultsList();
}


void CheatSearchTab::FilterCheatSearchResults(wxCommandEvent&)
{
const u8* const memptr = Memory::GetPointer(0);
if (memptr == nullptr)
{
WxUtils::ShowErrorDialog(_("A game is not currently running."));
return;
}

// Set up the sub-search results efficiently to prevent automatic re-allocations.
std::vector<CheatSearchResult> filtered_results;
filtered_results.reserve(m_search_results.size());

// Determine the selected filter
// 1 : equal
// 2 : greater-than
// 4 : less-than

const int filters[] = {7, 6, 1, 2, 4};
int filter_mask = filters[m_search_type->GetSelection()];

if (m_value_x_radiobtn.rad_oldvalue->GetValue()) // using old value comparison
{
for (CheatSearchResult& result : m_search_results)
{
// with big endian, can just use memcmp for ><= comparison
int cmp_result = memcmp(memptr + result.address, &result.old_value,m_search_type_size);
if (cmp_result < 0)
cmp_result = 4;
else
cmp_result = cmp_result ? 2 : 1;

if (cmp_result & filter_mask)
{
memcpy(&result.old_value, memptr + result.address, m_search_type_size);
filtered_results.push_back(result);
}
}
}
else // using user entered x value comparison
{
u32 user_x_val;

// parse the user entered x value
if (filter_mask != 7) // don't need the value for the "None" filter
{
unsigned long parsed_x_val = 0;
wxString x_val = m_textctrl_value_x->GetValue();

if (!x_val.ToULong(&parsed_x_val, 0))
{
WxUtils::ShowErrorDialog(_("You must enter a valid decimal, hexadecimal or octal value."));
return;
}

user_x_val = (u32)parsed_x_val;

// #ifdef LIL_ENDIAN :p
switch (m_search_type_size)
{
case 1 :
break;
case 2 :
*(u16*)&user_x_val = Common::swap16((u8*)&user_x_val);
break;
case 4 :
user_x_val = Common::swap32(user_x_val);
break;
}
// #elseif BIG_ENDIAN
// would have to move <u32 vals (8/16bit) to start of the user_x_val for the comparisons i use below
// #endif
}

for (CheatSearchResult& result : m_search_results)
{
// with big endian, can just use memcmp for ><= comparison
int cmp_result = memcmp(memptr + result.address, &user_x_val, m_search_type_size);
if (cmp_result < 0)
cmp_result = 4;
else if (cmp_result)
cmp_result = 2;
else
cmp_result = 1;

if (cmp_result & filter_mask)
{
memcpy(&result.old_value, memptr + result.address, m_search_type_size);
filtered_results.push_back(result);
}
}
}

m_search_results.swap(filtered_results);

UpdateCheatSearchResultsList();
}

void CheatSearchTab::ApplyFocus(wxEvent& ev)
{
ev.Skip();
m_value_x_radiobtn.rad_uservalue->SetValue(true);
}

void CheatSearchTab::UpdateCheatSearchResultsList()
{
m_lbox_search_results->Clear();

wxString count_label = _("Count:") + wxString::Format(" %lu",
(unsigned long)m_search_results.size());
if (m_search_results.size() > MAX_CHEAT_SEARCH_RESULTS_DISPLAY)
{
count_label += _(" (too many to display)");
}
else
{
for (const CheatSearchResult& result : m_search_results)
{
u32 display_value = result.old_value;

// #ifdef LIL_ENDIAN :p
switch (m_search_type_size)
{
case 1 :
break;
case 2 :
*(u16*)&display_value = Common::swap16((u8*)&display_value);
break;
case 4 :
display_value = Common::swap32(display_value);
break;
}
// #elseif BIG_ENDIAN
// need to do some stuff in here (for 8 and 16bit) for bigendian
// #endif
std::string rowfmt = StringFromFormat("0x%%08x 0x%%0%ux %%u/%%i", m_search_type_size*2);

m_lbox_search_results->Append(
wxString::Format(rowfmt.c_str(), result.address, display_value, display_value, display_value));
}
}

m_label_results_count->SetLabel(count_label);
}

void CheatSearchTab::CreateARCode(wxCommandEvent&)
{
const int sel = m_lbox_search_results->GetSelection();
if (sel >= 0)
{
const u32 address = m_search_results[sel].address | ((m_search_type_size & ~1) << 24);

CreateCodeDialog arcode_dlg(this, address);
arcode_dlg.SetExtraStyle(arcode_dlg.GetExtraStyle() & ~wxWS_EX_BLOCK_EVENTS);
arcode_dlg.ShowModal();
}
}

0 comments on commit 833ff4a

Please sign in to comment.