ActionReplay: Fix ISOProperties corrupting active code set

ISOProperties loads codes using ActionReplay::LoadCodes which actually applies
the codes to the global state. If a game is running then that games receives
all the codes (and ACTIVE status) from the second game being shown in
ISOProperties which is not desirable.
This commit is contained in:
EmptyChaos 2016-04-19 21:19:31 +00:00
parent 0fa9233c1a
commit 25b072ff2b
9 changed files with 124 additions and 45 deletions

View File

@ -19,7 +19,10 @@
// Zero Codes: any code with no address. These codes are used to do special operations like memory copy, etc // Zero Codes: any code with no address. These codes are used to do special operations like memory copy, etc
// ------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------
#include <list>
#include <mutex>
#include <string> #include <string>
#include <utility>
#include <vector> #include <vector>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
@ -81,6 +84,9 @@ static std::vector<ARCode> activeCodes;
static bool logSelf = false; static bool logSelf = false;
static std::vector<std::string> arLog; static std::vector<std::string> arLog;
static std::mutex s_callbacks_lock;
static std::list<std::function<void()>> s_callbacks;
struct ARAddr struct ARAddr
{ {
union union
@ -100,16 +106,68 @@ struct ARAddr
operator u32() const { return address; } operator u32() const { return address; }
}; };
static void RunCodeChangeCallbacks()
{
std::lock_guard<std::mutex> guard(s_callbacks_lock);
for (const auto& cb : s_callbacks)
cb();
}
// ---------------------- // ----------------------
// AR Remote Functions // AR Remote Functions
void LoadCodes(const IniFile& globalIni, const IniFile& localIni, bool forceLoad) void ApplyCodes(const std::vector<ARCode>& codes)
{ {
// Parses the Action Replay section of a game ini file. if (!SConfig::GetInstance().bEnableCheats)
if (!SConfig::GetInstance().bEnableCheats &&
!forceLoad)
return; return;
arCodes.clear(); arCodes = codes;
UpdateActiveList();
RunCodeChangeCallbacks();
}
void AddCode(const ARCode& code)
{
if (!SConfig::GetInstance().bEnableCheats)
return;
arCodes.push_back(code);
if (code.active)
UpdateActiveList();
RunCodeChangeCallbacks();
}
void* RegisterCodeChangeCallback(std::function<void()> callback)
{
if (!callback)
return nullptr;
std::lock_guard<std::mutex> guard(s_callbacks_lock);
s_callbacks.emplace_back(std::move(callback));
return &s_callbacks.back();
}
void UnregisterCodeChangeCallback(void* token)
{
std::lock_guard<std::mutex> guard(s_callbacks_lock);
for (auto i = s_callbacks.begin(); i != s_callbacks.end(); ++i)
{
if (&*i == token)
{
s_callbacks.erase(i);
break;
}
}
}
void LoadAndApplyCodes(const IniFile& globalIni, const IniFile& localIni)
{
ApplyCodes(LoadCodes(globalIni, localIni));
}
// Parses the Action Replay section of a game ini file.
std::vector<ARCode> LoadCodes(const IniFile& globalIni, const IniFile& localIni)
{
std::vector<ARCode> codes;
std::vector<std::string> enabledLines; std::vector<std::string> enabledLines;
std::set<std::string> enabledNames; std::set<std::string> enabledNames;
@ -146,13 +204,13 @@ void LoadCodes(const IniFile& globalIni, const IniFile& localIni, bool forceLoad
{ {
if (currentCode.ops.size()) if (currentCode.ops.size())
{ {
arCodes.push_back(currentCode); codes.push_back(currentCode);
currentCode.ops.clear(); currentCode.ops.clear();
} }
if (encryptedLines.size()) if (encryptedLines.size())
{ {
DecryptARCode(encryptedLines, currentCode.ops); DecryptARCode(encryptedLines, currentCode.ops);
arCodes.push_back(currentCode); codes.push_back(currentCode);
currentCode.ops.clear(); currentCode.ops.clear();
encryptedLines.clear(); encryptedLines.clear();
} }
@ -204,22 +262,16 @@ void LoadCodes(const IniFile& globalIni, const IniFile& localIni, bool forceLoad
// Handle the last code correctly. // Handle the last code correctly.
if (currentCode.ops.size()) if (currentCode.ops.size())
{ {
arCodes.push_back(currentCode); codes.push_back(currentCode);
} }
if (encryptedLines.size()) if (encryptedLines.size())
{ {
DecryptARCode(encryptedLines, currentCode.ops); DecryptARCode(encryptedLines, currentCode.ops);
arCodes.push_back(currentCode); codes.push_back(currentCode);
} }
} }
UpdateActiveList(); return codes;
}
void LoadCodes(std::vector<ARCode> &_arCodes, IniFile &globalIni, IniFile& localIni)
{
LoadCodes(globalIni, localIni, true);
_arCodes = arCodes;
} }
@ -270,6 +322,7 @@ void SetARCode_IsActive(bool active, size_t index)
} }
arCodes[index].active = active; arCodes[index].active = active;
UpdateActiveList(); UpdateActiveList();
RunCodeChangeCallbacks();
} }
void UpdateActiveList() void UpdateActiveList()

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include <functional>
#include <string> #include <string>
#include <vector> #include <vector>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
@ -31,8 +32,12 @@ struct ARCode
void RunAllActive(); void RunAllActive();
bool RunCode(const ARCode &arcode); bool RunCode(const ARCode &arcode);
void LoadCodes(const IniFile &globalini, const IniFile &localIni, bool forceLoad); void ApplyCodes(const std::vector<ARCode>& codes);
void LoadCodes(std::vector<ARCode> &_arCodes, IniFile &globalini, IniFile &localIni); void AddCode(const ARCode& new_code);
void* RegisterCodeChangeCallback(std::function<void()> callback);
void UnregisterCodeChangeCallback(void* token);
void LoadAndApplyCodes(const IniFile& globalini, const IniFile& localIni);
std::vector<ARCode> LoadCodes(const IniFile& globalini, const IniFile& localIni);
size_t GetCodeListSize(); size_t GetCodeListSize();
ARCode GetARCode(size_t index); ARCode GetARCode(size_t index);
void SetARCode_IsActive(bool active, size_t index); void SetARCode_IsActive(bool active, size_t index);

View File

@ -166,7 +166,7 @@ void LoadPatches()
IniFile localIni = SConfig::GetInstance().LoadLocalGameIni(); IniFile localIni = SConfig::GetInstance().LoadLocalGameIni();
LoadPatchSection("OnFrame", onFrame, globalIni, localIni); LoadPatchSection("OnFrame", onFrame, globalIni, localIni);
ActionReplay::LoadCodes(globalIni, localIni, false); ActionReplay::LoadAndApplyCodes(globalIni, localIni);
// lil silly // lil silly
std::vector<Gecko::GeckoCode> gcodes; std::vector<Gecko::GeckoCode> gcodes;

View File

@ -6,6 +6,7 @@
#include <cstddef> #include <cstddef>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <functional>
#include <string> #include <string>
#include <vector> #include <vector>
#include <wx/button.h> #include <wx/button.h>
@ -38,12 +39,19 @@
#include "DolphinWX/Cheats/CreateCodeDialog.h" #include "DolphinWX/Cheats/CreateCodeDialog.h"
#include "DolphinWX/Cheats/GeckoCodeDiag.h" #include "DolphinWX/Cheats/GeckoCodeDiag.h"
namespace
{
wxDEFINE_EVENT(DOLPHIN_EVT_UPDATE_CHEAT_LIST, wxThreadEvent);
}
wxCheatsWindow::wxCheatsWindow(wxWindow* const parent) wxCheatsWindow::wxCheatsWindow(wxWindow* const parent)
: wxDialog(parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX | wxMINIMIZE_BOX | wxDIALOG_NO_PARENT) : wxDialog(parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX | wxMINIMIZE_BOX | wxDIALOG_NO_PARENT)
{ {
// Create the GUI controls // Create the GUI controls
Init_ChildControls(); Init_ChildControls();
m_ar_callback_token = ActionReplay::RegisterCodeChangeCallback(std::bind(&wxCheatsWindow::OnActionReplayModified, this));
// load codes // load codes
UpdateGUI(); UpdateGUI();
@ -54,6 +62,7 @@ wxCheatsWindow::wxCheatsWindow(wxWindow* const parent)
wxCheatsWindow::~wxCheatsWindow() wxCheatsWindow::~wxCheatsWindow()
{ {
ActionReplay::UnregisterCodeChangeCallback(m_ar_callback_token);
main_frame->g_CheatsWindow = nullptr; main_frame->g_CheatsWindow = nullptr;
} }
@ -126,7 +135,7 @@ void wxCheatsWindow::Init_ChildControls()
button_cancel->Bind(wxEVT_BUTTON, &wxCheatsWindow::OnEvent_ButtonClose_Press, this); button_cancel->Bind(wxEVT_BUTTON, &wxCheatsWindow::OnEvent_ButtonClose_Press, this);
Bind(wxEVT_CLOSE_WINDOW, &wxCheatsWindow::OnEvent_Close, this); Bind(wxEVT_CLOSE_WINDOW, &wxCheatsWindow::OnEvent_Close, this);
Bind(UPDATE_CHEAT_LIST_EVENT, &wxCheatsWindow::OnEvent_CheatsList_Update, this); Bind(DOLPHIN_EVT_UPDATE_CHEAT_LIST, &wxCheatsWindow::OnEvent_CheatsList_Update, this);
wxStdDialogButtonSizer* const sButtons = new wxStdDialogButtonSizer(); wxStdDialogButtonSizer* const sButtons = new wxStdDialogButtonSizer();
sButtons->AddButton(m_button_apply); sButtons->AddButton(m_button_apply);
@ -232,24 +241,30 @@ void wxCheatsWindow::OnEvent_CheatsList_ItemToggled(wxCommandEvent& WXUNUSED(eve
{ {
if ((int)code_index.uiIndex == index) if ((int)code_index.uiIndex == index)
{ {
m_ar_ignore_callback = true;
ActionReplay::SetARCode_IsActive(m_checklistbox_cheats_list->IsChecked(index), code_index.index); ActionReplay::SetARCode_IsActive(m_checklistbox_cheats_list->IsChecked(index), code_index.index);
} }
} }
} }
void wxCheatsWindow::OnEvent_CheatsList_Update(wxCommandEvent& event) void wxCheatsWindow::OnEvent_CheatsList_Update(wxThreadEvent&)
{ {
if (m_ar_ignore_callback)
{
m_ar_ignore_callback = false;
return;
}
Load_ARCodes(); Load_ARCodes();
} }
void wxCheatsWindow::OnActionReplayModified()
{
// NOTE: This is an arbitrary thread context
GetEventHandler()->QueueEvent(new wxThreadEvent(DOLPHIN_EVT_UPDATE_CHEAT_LIST));
}
void wxCheatsWindow::OnEvent_ApplyChanges_Press(wxCommandEvent& ev) void wxCheatsWindow::OnEvent_ApplyChanges_Press(wxCommandEvent& ev)
{ {
// Apply AR Code changes
for (const ARCodeIndex& code_index : m_index_list)
{
ActionReplay::SetARCode_IsActive(m_checklistbox_cheats_list->IsChecked(code_index.uiIndex), code_index.index);
}
// Apply Gecko Code changes // Apply Gecko Code changes
Gecko::SetActiveCodes(m_geckocode_panel->GetCodes()); Gecko::SetActiveCodes(m_geckocode_panel->GetCodes());
@ -265,11 +280,15 @@ void wxCheatsWindow::OnEvent_ApplyChanges_Press(wxCommandEvent& ev)
void wxCheatsWindow::OnEvent_ButtonUpdateLog_Press(wxCommandEvent& WXUNUSED(event)) void wxCheatsWindow::OnEvent_ButtonUpdateLog_Press(wxCommandEvent& WXUNUSED(event))
{ {
wxBeginBusyCursor();
m_textctrl_log->Freeze();
m_textctrl_log->Clear(); m_textctrl_log->Clear();
for (const std::string& text : ActionReplay::GetSelfLog()) for (const std::string& text : ActionReplay::GetSelfLog())
{ {
m_textctrl_log->AppendText(StrToWxStr(text)); m_textctrl_log->AppendText(StrToWxStr(text));
} }
m_textctrl_log->Thaw();
wxEndBusyCursor();
} }
void wxCheatsWindow::OnEvent_CheckBoxEnableLogging_StateChange(wxCommandEvent& WXUNUSED(event)) void wxCheatsWindow::OnEvent_CheckBoxEnableLogging_StateChange(wxCommandEvent& WXUNUSED(event))

View File

@ -72,6 +72,10 @@ private:
IniFile m_gameini_local; IniFile m_gameini_local;
std::string m_gameini_local_path; std::string m_gameini_local_path;
// ActionReplay::UnregisterCodeChangeCallback handle
void* m_ar_callback_token = nullptr;
bool m_ar_ignore_callback = false;
void Init_ChildControls(); void Init_ChildControls();
void Load_ARCodes(); void Load_ARCodes();
@ -86,7 +90,8 @@ private:
// Cheats List // Cheats List
void OnEvent_CheatsList_ItemSelected(wxCommandEvent& event); void OnEvent_CheatsList_ItemSelected(wxCommandEvent& event);
void OnEvent_CheatsList_ItemToggled(wxCommandEvent& event); void OnEvent_CheatsList_ItemToggled(wxCommandEvent& event);
void OnEvent_CheatsList_Update(wxCommandEvent& event); void OnEvent_CheatsList_Update(wxThreadEvent& event);
void OnActionReplayModified();
// Apply Changes Button // Apply Changes Button
void OnEvent_ApplyChanges_Press(wxCommandEvent& event); void OnEvent_ApplyChanges_Press(wxCommandEvent& event);

View File

@ -14,9 +14,6 @@
#include "DolphinWX/WxUtils.h" #include "DolphinWX/WxUtils.h"
#include "DolphinWX/Cheats/CreateCodeDialog.h" #include "DolphinWX/Cheats/CreateCodeDialog.h"
// Fired when an ActionReplay code is created.
wxDEFINE_EVENT(UPDATE_CHEAT_LIST_EVENT, wxCommandEvent);
CreateCodeDialog::CreateCodeDialog(wxWindow* const parent, const u32 address) CreateCodeDialog::CreateCodeDialog(wxWindow* const parent, const u32 address)
: wxDialog(parent, wxID_ANY, _("Create AR Code")) : wxDialog(parent, wxID_ANY, _("Create AR Code"))
, m_code_address(address) , m_code_address(address)
@ -80,24 +77,21 @@ void CreateCodeDialog::PressOK(wxCommandEvent& ev)
// create the new code // create the new code
ActionReplay::ARCode new_cheat; ActionReplay::ARCode new_cheat;
new_cheat.active = false; new_cheat.active = false;
new_cheat.user_defined = true;
new_cheat.name = WxStrToStr(code_name); new_cheat.name = WxStrToStr(code_name);
const ActionReplay::AREntry new_entry(m_code_address, code_value); new_cheat.ops.emplace_back(ActionReplay::AREntry(m_code_address, code_value));
new_cheat.ops.push_back(new_entry); ActionReplay::AddCode(new_cheat);
// pretty hacky - add the code to the gameini // pretty hacky - add the code to the gameini
// FIXME: The save logic should be ActionReplay since it mirrors the parser
{ {
CISOProperties isoprops(GameListItem(SConfig::GetInstance().m_LastFilename, std::unordered_map<std::string, std::string>()), this); CISOProperties isoprops(GameListItem(SConfig::GetInstance().m_LastFilename, {}), this);
// add the code to the isoproperties arcode list // add the code to the isoproperties arcode list
isoprops.AddARCode(new_cheat); isoprops.AddARCode(new_cheat);
// save the gameini // save the gameini
isoprops.SaveGameConfig(); isoprops.SaveGameConfig();
isoprops.ActionReplayList_Load(); // loads the new arcodes
//ActionReplay::UpdateActiveList();
} }
// Propagate back to the parent frame to update the cheat list.
GetEventHandler()->AddPendingEvent(wxCommandEvent(UPDATE_CHEAT_LIST_EVENT));
Close(); Close();
} }

View File

@ -12,8 +12,6 @@
class wxCheckBox; class wxCheckBox;
class wxTextCtrl; class wxTextCtrl;
wxDECLARE_EVENT(UPDATE_CHEAT_LIST_EVENT, wxCommandEvent);
class CreateCodeDialog final : public wxDialog class CreateCodeDialog final : public wxDialog
{ {
public: public:

View File

@ -95,6 +95,7 @@ BEGIN_EVENT_TABLE(CISOProperties, wxDialog)
EVT_MENU(IDM_EXTRACTDOL, CISOProperties::OnExtractDataFromHeader) EVT_MENU(IDM_EXTRACTDOL, CISOProperties::OnExtractDataFromHeader)
EVT_MENU(IDM_CHECKINTEGRITY, CISOProperties::CheckPartitionIntegrity) EVT_MENU(IDM_CHECKINTEGRITY, CISOProperties::CheckPartitionIntegrity)
EVT_CHOICE(ID_LANG, CISOProperties::OnChangeBannerLang) EVT_CHOICE(ID_LANG, CISOProperties::OnChangeBannerLang)
EVT_CHECKLISTBOX(ID_CHEATS_LIST, CISOProperties::OnActionReplayCodeChecked)
END_EVENT_TABLE() END_EVENT_TABLE()
CISOProperties::CISOProperties(const GameListItem& game_list_item, wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& position, const wxSize& size, long style) CISOProperties::CISOProperties(const GameListItem& game_list_item, wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& position, const wxSize& size, long style)
@ -1314,6 +1315,11 @@ void CISOProperties::ListSelectionChanged(wxCommandEvent& event)
} }
} }
void CISOProperties::OnActionReplayCodeChecked(wxCommandEvent& event)
{
arCodes[event.GetSelection()].active = Cheats->IsChecked(event.GetSelection());
}
void CISOProperties::PatchList_Load() void CISOProperties::PatchList_Load()
{ {
onFrame.clear(); onFrame.clear();
@ -1399,9 +1405,8 @@ void CISOProperties::PatchButtonClicked(wxCommandEvent& event)
void CISOProperties::ActionReplayList_Load() void CISOProperties::ActionReplayList_Load()
{ {
arCodes.clear();
Cheats->Clear(); Cheats->Clear();
ActionReplay::LoadCodes(arCodes, GameIniDefault, GameIniLocal); arCodes = ActionReplay::LoadCodes(GameIniDefault, GameIniLocal);
u32 index = 0; u32 index = 0;
for (const ActionReplay::ARCode& arCode : arCodes) for (const ActionReplay::ARCode& arCode : arCodes)
@ -1422,8 +1427,7 @@ void CISOProperties::ActionReplayList_Save()
u32 cheats_chkbox_count = Cheats->GetCount(); u32 cheats_chkbox_count = Cheats->GetCount();
for (const ActionReplay::ARCode& code : arCodes) for (const ActionReplay::ARCode& code : arCodes)
{ {
// Check the index against the count because of the hacky way codes are added from the "Cheat Search" dialog if (code.active)
if ((index < cheats_chkbox_count) && Cheats->IsChecked(index))
enabledLines.push_back("$" + code.name); enabledLines.push_back("$" + code.name);
// Do not save default cheats. // Do not save default cheats.

View File

@ -208,6 +208,7 @@ private:
void OnComputeMD5Sum(wxCommandEvent& event); void OnComputeMD5Sum(wxCommandEvent& event);
void OnShowDefaultConfig(wxCommandEvent& event); void OnShowDefaultConfig(wxCommandEvent& event);
void ListSelectionChanged(wxCommandEvent& event); void ListSelectionChanged(wxCommandEvent& event);
void OnActionReplayCodeChecked(wxCommandEvent& event);
void PatchButtonClicked(wxCommandEvent& event); void PatchButtonClicked(wxCommandEvent& event);
void ActionReplayButtonClicked(wxCommandEvent& event); void ActionReplayButtonClicked(wxCommandEvent& event);
void RightClickOnBanner(wxMouseEvent& event); void RightClickOnBanner(wxMouseEvent& event);