From 25b072ff2ba6839b300b05a45ce76e94d84ded32 Mon Sep 17 00:00:00 2001 From: EmptyChaos Date: Tue, 19 Apr 2016 21:19:31 +0000 Subject: [PATCH 1/3] 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. --- Source/Core/Core/ActionReplay.cpp | 85 +++++++++++++++---- Source/Core/Core/ActionReplay.h | 9 +- Source/Core/Core/PatchEngine.cpp | 2 +- Source/Core/DolphinWX/Cheats/CheatsWindow.cpp | 35 ++++++-- Source/Core/DolphinWX/Cheats/CheatsWindow.h | 7 +- .../DolphinWX/Cheats/CreateCodeDialog.cpp | 16 ++-- .../Core/DolphinWX/Cheats/CreateCodeDialog.h | 2 - Source/Core/DolphinWX/ISOProperties.cpp | 12 ++- Source/Core/DolphinWX/ISOProperties.h | 1 + 9 files changed, 124 insertions(+), 45 deletions(-) diff --git a/Source/Core/Core/ActionReplay.cpp b/Source/Core/Core/ActionReplay.cpp index b6bd722069..fc4748f478 100644 --- a/Source/Core/Core/ActionReplay.cpp +++ b/Source/Core/Core/ActionReplay.cpp @@ -19,7 +19,10 @@ // Zero Codes: any code with no address. These codes are used to do special operations like memory copy, etc // ------------------------------------------------------------------------------------------------------------- +#include +#include #include +#include #include #include "Common/CommonTypes.h" @@ -81,6 +84,9 @@ static std::vector activeCodes; static bool logSelf = false; static std::vector arLog; +static std::mutex s_callbacks_lock; +static std::list> s_callbacks; + struct ARAddr { union @@ -100,16 +106,68 @@ struct ARAddr operator u32() const { return address; } }; +static void RunCodeChangeCallbacks() +{ + std::lock_guard guard(s_callbacks_lock); + for (const auto& cb : s_callbacks) + cb(); +} + // ---------------------- // AR Remote Functions -void LoadCodes(const IniFile& globalIni, const IniFile& localIni, bool forceLoad) +void ApplyCodes(const std::vector& codes) { - // Parses the Action Replay section of a game ini file. - if (!SConfig::GetInstance().bEnableCheats && - !forceLoad) + if (!SConfig::GetInstance().bEnableCheats) 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 callback) +{ + if (!callback) + return nullptr; + + std::lock_guard guard(s_callbacks_lock); + s_callbacks.emplace_back(std::move(callback)); + return &s_callbacks.back(); +} + +void UnregisterCodeChangeCallback(void* token) +{ + std::lock_guard 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 LoadCodes(const IniFile& globalIni, const IniFile& localIni) +{ + std::vector codes; std::vector enabledLines; std::set enabledNames; @@ -146,13 +204,13 @@ void LoadCodes(const IniFile& globalIni, const IniFile& localIni, bool forceLoad { if (currentCode.ops.size()) { - arCodes.push_back(currentCode); + codes.push_back(currentCode); currentCode.ops.clear(); } if (encryptedLines.size()) { DecryptARCode(encryptedLines, currentCode.ops); - arCodes.push_back(currentCode); + codes.push_back(currentCode); currentCode.ops.clear(); encryptedLines.clear(); } @@ -204,22 +262,16 @@ void LoadCodes(const IniFile& globalIni, const IniFile& localIni, bool forceLoad // Handle the last code correctly. if (currentCode.ops.size()) { - arCodes.push_back(currentCode); + codes.push_back(currentCode); } if (encryptedLines.size()) { DecryptARCode(encryptedLines, currentCode.ops); - arCodes.push_back(currentCode); + codes.push_back(currentCode); } } - UpdateActiveList(); -} - -void LoadCodes(std::vector &_arCodes, IniFile &globalIni, IniFile& localIni) -{ - LoadCodes(globalIni, localIni, true); - _arCodes = arCodes; + return codes; } @@ -270,6 +322,7 @@ void SetARCode_IsActive(bool active, size_t index) } arCodes[index].active = active; UpdateActiveList(); + RunCodeChangeCallbacks(); } void UpdateActiveList() diff --git a/Source/Core/Core/ActionReplay.h b/Source/Core/Core/ActionReplay.h index d2bf3621bf..be6c4d0bbc 100644 --- a/Source/Core/Core/ActionReplay.h +++ b/Source/Core/Core/ActionReplay.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include #include "Common/CommonTypes.h" @@ -31,8 +32,12 @@ struct ARCode void RunAllActive(); bool RunCode(const ARCode &arcode); -void LoadCodes(const IniFile &globalini, const IniFile &localIni, bool forceLoad); -void LoadCodes(std::vector &_arCodes, IniFile &globalini, IniFile &localIni); +void ApplyCodes(const std::vector& codes); +void AddCode(const ARCode& new_code); +void* RegisterCodeChangeCallback(std::function callback); +void UnregisterCodeChangeCallback(void* token); +void LoadAndApplyCodes(const IniFile& globalini, const IniFile& localIni); +std::vector LoadCodes(const IniFile& globalini, const IniFile& localIni); size_t GetCodeListSize(); ARCode GetARCode(size_t index); void SetARCode_IsActive(bool active, size_t index); diff --git a/Source/Core/Core/PatchEngine.cpp b/Source/Core/Core/PatchEngine.cpp index 13fb24f5e6..48211a61e0 100644 --- a/Source/Core/Core/PatchEngine.cpp +++ b/Source/Core/Core/PatchEngine.cpp @@ -166,7 +166,7 @@ void LoadPatches() IniFile localIni = SConfig::GetInstance().LoadLocalGameIni(); LoadPatchSection("OnFrame", onFrame, globalIni, localIni); - ActionReplay::LoadCodes(globalIni, localIni, false); + ActionReplay::LoadAndApplyCodes(globalIni, localIni); // lil silly std::vector gcodes; diff --git a/Source/Core/DolphinWX/Cheats/CheatsWindow.cpp b/Source/Core/DolphinWX/Cheats/CheatsWindow.cpp index 49a58504a5..83089b58c8 100644 --- a/Source/Core/DolphinWX/Cheats/CheatsWindow.cpp +++ b/Source/Core/DolphinWX/Cheats/CheatsWindow.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -38,12 +39,19 @@ #include "DolphinWX/Cheats/CreateCodeDialog.h" #include "DolphinWX/Cheats/GeckoCodeDiag.h" +namespace +{ +wxDEFINE_EVENT(DOLPHIN_EVT_UPDATE_CHEAT_LIST, wxThreadEvent); +} + wxCheatsWindow::wxCheatsWindow(wxWindow* const parent) : wxDialog(parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX | wxMINIMIZE_BOX | wxDIALOG_NO_PARENT) { // Create the GUI controls Init_ChildControls(); + m_ar_callback_token = ActionReplay::RegisterCodeChangeCallback(std::bind(&wxCheatsWindow::OnActionReplayModified, this)); + // load codes UpdateGUI(); @@ -54,6 +62,7 @@ wxCheatsWindow::wxCheatsWindow(wxWindow* const parent) wxCheatsWindow::~wxCheatsWindow() { + ActionReplay::UnregisterCodeChangeCallback(m_ar_callback_token); main_frame->g_CheatsWindow = nullptr; } @@ -126,7 +135,7 @@ void wxCheatsWindow::Init_ChildControls() button_cancel->Bind(wxEVT_BUTTON, &wxCheatsWindow::OnEvent_ButtonClose_Press, 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(); sButtons->AddButton(m_button_apply); @@ -232,24 +241,30 @@ void wxCheatsWindow::OnEvent_CheatsList_ItemToggled(wxCommandEvent& WXUNUSED(eve { if ((int)code_index.uiIndex == index) { + m_ar_ignore_callback = true; 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(); } +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) { - // 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 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)) { + wxBeginBusyCursor(); + m_textctrl_log->Freeze(); m_textctrl_log->Clear(); for (const std::string& text : ActionReplay::GetSelfLog()) { m_textctrl_log->AppendText(StrToWxStr(text)); } + m_textctrl_log->Thaw(); + wxEndBusyCursor(); } void wxCheatsWindow::OnEvent_CheckBoxEnableLogging_StateChange(wxCommandEvent& WXUNUSED(event)) diff --git a/Source/Core/DolphinWX/Cheats/CheatsWindow.h b/Source/Core/DolphinWX/Cheats/CheatsWindow.h index 990219cac3..34e66bb7ff 100644 --- a/Source/Core/DolphinWX/Cheats/CheatsWindow.h +++ b/Source/Core/DolphinWX/Cheats/CheatsWindow.h @@ -72,6 +72,10 @@ private: IniFile m_gameini_local; 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 Load_ARCodes(); @@ -86,7 +90,8 @@ private: // Cheats List void OnEvent_CheatsList_ItemSelected(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 void OnEvent_ApplyChanges_Press(wxCommandEvent& event); diff --git a/Source/Core/DolphinWX/Cheats/CreateCodeDialog.cpp b/Source/Core/DolphinWX/Cheats/CreateCodeDialog.cpp index 364dfbad41..d53136987a 100644 --- a/Source/Core/DolphinWX/Cheats/CreateCodeDialog.cpp +++ b/Source/Core/DolphinWX/Cheats/CreateCodeDialog.cpp @@ -14,9 +14,6 @@ #include "DolphinWX/WxUtils.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) : wxDialog(parent, wxID_ANY, _("Create AR Code")) , m_code_address(address) @@ -80,24 +77,21 @@ void CreateCodeDialog::PressOK(wxCommandEvent& ev) // create the new code ActionReplay::ARCode new_cheat; new_cheat.active = false; + new_cheat.user_defined = true; new_cheat.name = WxStrToStr(code_name); - const ActionReplay::AREntry new_entry(m_code_address, code_value); - new_cheat.ops.push_back(new_entry); + new_cheat.ops.emplace_back(ActionReplay::AREntry(m_code_address, code_value)); + ActionReplay::AddCode(new_cheat); // 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()), this); + CISOProperties isoprops(GameListItem(SConfig::GetInstance().m_LastFilename, {}), this); // add the code to the isoproperties arcode list isoprops.AddARCode(new_cheat); // save the gameini 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(); } diff --git a/Source/Core/DolphinWX/Cheats/CreateCodeDialog.h b/Source/Core/DolphinWX/Cheats/CreateCodeDialog.h index 7c5b752320..bc9ca0ad2b 100644 --- a/Source/Core/DolphinWX/Cheats/CreateCodeDialog.h +++ b/Source/Core/DolphinWX/Cheats/CreateCodeDialog.h @@ -12,8 +12,6 @@ class wxCheckBox; class wxTextCtrl; -wxDECLARE_EVENT(UPDATE_CHEAT_LIST_EVENT, wxCommandEvent); - class CreateCodeDialog final : public wxDialog { public: diff --git a/Source/Core/DolphinWX/ISOProperties.cpp b/Source/Core/DolphinWX/ISOProperties.cpp index d7606c9dad..77c64cd150 100644 --- a/Source/Core/DolphinWX/ISOProperties.cpp +++ b/Source/Core/DolphinWX/ISOProperties.cpp @@ -95,6 +95,7 @@ BEGIN_EVENT_TABLE(CISOProperties, wxDialog) EVT_MENU(IDM_EXTRACTDOL, CISOProperties::OnExtractDataFromHeader) EVT_MENU(IDM_CHECKINTEGRITY, CISOProperties::CheckPartitionIntegrity) EVT_CHOICE(ID_LANG, CISOProperties::OnChangeBannerLang) + EVT_CHECKLISTBOX(ID_CHEATS_LIST, CISOProperties::OnActionReplayCodeChecked) 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) @@ -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() { onFrame.clear(); @@ -1399,9 +1405,8 @@ void CISOProperties::PatchButtonClicked(wxCommandEvent& event) void CISOProperties::ActionReplayList_Load() { - arCodes.clear(); Cheats->Clear(); - ActionReplay::LoadCodes(arCodes, GameIniDefault, GameIniLocal); + arCodes = ActionReplay::LoadCodes(GameIniDefault, GameIniLocal); u32 index = 0; for (const ActionReplay::ARCode& arCode : arCodes) @@ -1422,8 +1427,7 @@ void CISOProperties::ActionReplayList_Save() u32 cheats_chkbox_count = Cheats->GetCount(); 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 ((index < cheats_chkbox_count) && Cheats->IsChecked(index)) + if (code.active) enabledLines.push_back("$" + code.name); // Do not save default cheats. diff --git a/Source/Core/DolphinWX/ISOProperties.h b/Source/Core/DolphinWX/ISOProperties.h index d1b05e047d..9ef253d8ea 100644 --- a/Source/Core/DolphinWX/ISOProperties.h +++ b/Source/Core/DolphinWX/ISOProperties.h @@ -208,6 +208,7 @@ private: void OnComputeMD5Sum(wxCommandEvent& event); void OnShowDefaultConfig(wxCommandEvent& event); void ListSelectionChanged(wxCommandEvent& event); + void OnActionReplayCodeChecked(wxCommandEvent& event); void PatchButtonClicked(wxCommandEvent& event); void ActionReplayButtonClicked(wxCommandEvent& event); void RightClickOnBanner(wxMouseEvent& event); From 6ab1b2747714179783d0a2946acc9408eef2d950 Mon Sep 17 00:00:00 2001 From: EmptyChaos Date: Fri, 22 Apr 2016 10:42:16 +0000 Subject: [PATCH 2/3] ActionReplay: UI Consistency and Cleanup Cleanup code style. Move ActionReplay code->INI saving into ActionReplay namespace. Threadsafety Cleanup: ActionReplay is accessed from the Host, Emu and CPU Threads so the internal storage needs to be protected by a lock to prevent vectors/strings being deleted/moved while in use by the CPU Thread. UI Consistency: Make ARCodes behave like Gecko Codes - only apply changes when Apply is pressed. Save changes to INI from CheatsWindow. ISOProperties/CheatsWindow now synchronize with each other. --- Source/Core/Core/ActionReplay.cpp | 372 ++++++++---------- Source/Core/Core/ActionReplay.h | 21 +- Source/Core/DolphinWX/ARCodeAddEdit.cpp | 1 + .../Core/DolphinWX/Cheats/CheatSearchTab.cpp | 1 - Source/Core/DolphinWX/Cheats/CheatsWindow.cpp | 167 ++++---- Source/Core/DolphinWX/Cheats/CheatsWindow.h | 25 +- .../DolphinWX/Cheats/CreateCodeDialog.cpp | 18 +- Source/Core/DolphinWX/Frame.cpp | 1 + Source/Core/DolphinWX/Globals.h | 11 +- Source/Core/DolphinWX/ISOProperties.cpp | 70 ++-- Source/Core/DolphinWX/ISOProperties.h | 15 +- 11 files changed, 328 insertions(+), 374 deletions(-) diff --git a/Source/Core/Core/ActionReplay.cpp b/Source/Core/Core/ActionReplay.cpp index fc4748f478..2f34a49699 100644 --- a/Source/Core/Core/ActionReplay.cpp +++ b/Source/Core/Core/ActionReplay.cpp @@ -19,9 +19,12 @@ // Zero Codes: any code with no address. These codes are used to do special operations like memory copy, etc // ------------------------------------------------------------------------------------------------------------- -#include +#include +#include +#include #include #include +#include #include #include @@ -75,17 +78,14 @@ enum SUB_MASTER_CODE = 0x03, }; +// General lock. Protects codes list and internal log. +static std::mutex s_lock; +static std::vector s_active_codes; +static std::vector s_internal_log; +static std::atomic s_use_internal_log{ false }; // pointer to the code currently being run, (used by log messages that include the code name) -static ARCode const* current_code = nullptr; - -static bool b_RanOnce = false; -static std::vector arCodes; -static std::vector activeCodes; -static bool logSelf = false; -static std::vector arLog; - -static std::mutex s_callbacks_lock; -static std::list> s_callbacks; +static const ARCode* s_current_code = nullptr; +static bool s_disable_logging = false; struct ARAddr { @@ -106,13 +106,6 @@ struct ARAddr operator u32() const { return address; } }; -static void RunCodeChangeCallbacks() -{ - std::lock_guard guard(s_callbacks_lock); - for (const auto& cb : s_callbacks) - cb(); -} - // ---------------------- // AR Remote Functions void ApplyCodes(const std::vector& codes) @@ -120,73 +113,59 @@ void ApplyCodes(const std::vector& codes) if (!SConfig::GetInstance().bEnableCheats) return; - arCodes = codes; - UpdateActiveList(); - RunCodeChangeCallbacks(); + std::lock_guard guard(s_lock); + s_disable_logging = false; + s_active_codes.clear(); + std::copy_if(codes.begin(), codes.end(), std::back_inserter(s_active_codes), [](const ARCode& code) + { + return code.active; + }); + s_active_codes.shrink_to_fit(); } -void AddCode(const ARCode& code) +void AddCode(ARCode code) { if (!SConfig::GetInstance().bEnableCheats) return; - arCodes.push_back(code); if (code.active) - UpdateActiveList(); - RunCodeChangeCallbacks(); -} - -void* RegisterCodeChangeCallback(std::function callback) -{ - if (!callback) - return nullptr; - - std::lock_guard guard(s_callbacks_lock); - s_callbacks.emplace_back(std::move(callback)); - return &s_callbacks.back(); -} - -void UnregisterCodeChangeCallback(void* token) -{ - std::lock_guard guard(s_callbacks_lock); - for (auto i = s_callbacks.begin(); i != s_callbacks.end(); ++i) { - if (&*i == token) - { - s_callbacks.erase(i); - break; - } + std::lock_guard guard(s_lock); + s_disable_logging = false; + s_active_codes.emplace_back(std::move(code)); } } -void LoadAndApplyCodes(const IniFile& globalIni, const IniFile& localIni) +void LoadAndApplyCodes(const IniFile& global_ini, const IniFile& local_ini) { - ApplyCodes(LoadCodes(globalIni, localIni)); + ApplyCodes(LoadCodes(global_ini, local_ini)); } // Parses the Action Replay section of a game ini file. -std::vector LoadCodes(const IniFile& globalIni, const IniFile& localIni) +std::vector LoadCodes(const IniFile& global_ini, const IniFile& local_ini) { std::vector codes; - std::vector enabledLines; - std::set enabledNames; - localIni.GetLines("ActionReplay_Enabled", &enabledLines); - for (const std::string& line : enabledLines) + std::unordered_set enabled_names; { - if (line.size() != 0 && line[0] == '$') + std::vector enabled_lines; + local_ini.GetLines("ActionReplay_Enabled", &enabled_lines); + for (const std::string& line : enabled_lines) { - std::string name = line.substr(1, line.size() - 1); - enabledNames.insert(name); + if (line.size() != 0 && line[0] == '$') + { + std::string name = line.substr(1, line.size() - 1); + enabled_names.insert(name); + } } } - const IniFile* inis[2] = {&globalIni, &localIni}; + const IniFile* inis[2] = {&global_ini, &local_ini}; for (const IniFile* ini : inis) { std::vector lines; - std::vector encryptedLines; - ARCode currentCode; + std::vector encrypted_lines; + ARCode current_code; ini->GetLines("ActionReplay", &lines); @@ -202,22 +181,22 @@ std::vector LoadCodes(const IniFile& globalIni, const IniFile& localIni) // Check if the line is a name of the code if (line[0] == '$') { - if (currentCode.ops.size()) + if (current_code.ops.size()) { - codes.push_back(currentCode); - currentCode.ops.clear(); + codes.push_back(current_code); + current_code.ops.clear(); } - if (encryptedLines.size()) + if (encrypted_lines.size()) { - DecryptARCode(encryptedLines, currentCode.ops); - codes.push_back(currentCode); - currentCode.ops.clear(); - encryptedLines.clear(); + DecryptARCode(encrypted_lines, current_code.ops); + codes.push_back(current_code); + current_code.ops.clear(); + encrypted_lines.clear(); } - currentCode.name = line.substr(1, line.size() - 1); - currentCode.active = enabledNames.find(currentCode.name) != enabledNames.end(); - currentCode.user_defined = (ini == &localIni); + current_code.name = line.substr(1, line.size() - 1); + current_code.active = enabled_names.find(current_code.name) != enabled_names.end(); + current_code.user_defined = (ini == &local_ini); } else { @@ -232,7 +211,7 @@ std::vector LoadCodes(const IniFile& globalIni, const IniFile& localIni) if (success_addr && success_val) { - currentCode.ops.push_back(op); + current_code.ops.push_back(op); } else { @@ -253,105 +232,91 @@ std::vector LoadCodes(const IniFile& globalIni, const IniFile& localIni) // Encrypted AR code // Decryption is done in "blocks", so we must push blocks into a vector, // then send to decrypt when a new block is encountered, or if it's the last block. - encryptedLines.push_back(pieces[0]+pieces[1]+pieces[2]); + encrypted_lines.emplace_back(pieces[0] + pieces[1] + pieces[2]); } } } } // Handle the last code correctly. - if (currentCode.ops.size()) + if (current_code.ops.size()) { - codes.push_back(currentCode); + codes.push_back(current_code); } - if (encryptedLines.size()) + if (encrypted_lines.size()) { - DecryptARCode(encryptedLines, currentCode.ops); - codes.push_back(currentCode); + DecryptARCode(encrypted_lines, current_code.ops); + codes.push_back(current_code); } } return codes; } - -static void LogInfo(const char *format, ...) +void SaveCodes(IniFile* local_ini, const std::vector& codes) { - if (!b_RanOnce) + std::vector lines; + std::vector enabled_lines; + for (const ActionReplay::ARCode& code : codes) { - if (LogManager::GetMaxLevel() >= LogTypes::LINFO || logSelf) - { - va_list args; - va_start(args, format); - std::string text = StringFromFormatV(format, args); - va_end(args); - INFO_LOG(ACTIONREPLAY, "%s", text.c_str()); + if (code.active) + enabled_lines.emplace_back("$" + code.name); - if (logSelf) + if (code.user_defined) + { + lines.emplace_back("$" + code.name); + for (const ActionReplay::AREntry& op : code.ops) { - text += '\n'; - arLog.push_back(text); + lines.emplace_back(StringFromFormat("%08X %08X", op.cmd_addr, op.value)); } } } + local_ini->SetLines("ActionReplay_Enabled", enabled_lines); + local_ini->SetLines("ActionReplay", lines); } -size_t GetCodeListSize() -{ - return arCodes.size(); -} -ARCode GetARCode(size_t index) +static void LogInfo(const char* format, ...) { - if (index > arCodes.size()) - { - PanicAlertT("GetARCode: Index is greater than " - "ar code list size %zu", index); - return ARCode(); - } - return arCodes[index]; -} - -void SetARCode_IsActive(bool active, size_t index) -{ - if (index > arCodes.size()) - { - PanicAlertT("SetARCode_IsActive: Index is greater than " - "ar code list size %zu", index); + if (s_disable_logging) + return; + bool use_internal_log = s_use_internal_log.load(std::memory_order_relaxed); + if (LogManager::GetMaxLevel() < LogTypes::LINFO && !use_internal_log) return; - } - arCodes[index].active = active; - UpdateActiveList(); - RunCodeChangeCallbacks(); -} -void UpdateActiveList() -{ - bool old_value = SConfig::GetInstance().bEnableCheats; - SConfig::GetInstance().bEnableCheats = false; - b_RanOnce = false; - activeCodes.clear(); - for (auto& arCode : arCodes) + va_list args; + va_start(args, format); + std::string text = StringFromFormatV(format, args); + va_end(args); + INFO_LOG(ACTIONREPLAY, "%s", text.c_str()); + + if (use_internal_log) { - if (arCode.active) - activeCodes.push_back(arCode); + text += '\n'; + s_internal_log.emplace_back(std::move(text)); } - SConfig::GetInstance().bEnableCheats = old_value; } void EnableSelfLogging(bool enable) { - logSelf = enable; + s_use_internal_log.store(enable, std::memory_order_relaxed); } -const std::vector &GetSelfLog() +std::vector GetSelfLog() { - return arLog; + std::lock_guard guard(s_lock); + return s_internal_log; +} + +void ClearSelfLog() +{ + std::lock_guard guard(s_lock); + s_internal_log.clear(); } bool IsSelfLogging() { - return logSelf; + return s_use_internal_log.load(std::memory_order_relaxed); } // ---------------------- @@ -405,8 +370,8 @@ static bool Subtype_RamWriteAndFill(const ARAddr& addr, const u32 data) default: LogInfo("Bad Size"); PanicAlertT("Action Replay Error: Invalid size " - "(%08x : address = %08x) in Ram Write And Fill (%s)", - addr.size, addr.gcaddr, current_code->name.c_str()); + "(%08x : address = %08x) in Ram Write And Fill (%s)", + addr.size, addr.gcaddr, s_current_code->name.c_str()); return false; } @@ -443,7 +408,7 @@ static bool Subtype_WriteToPointer(const ARAddr& addr, const u32 data) LogInfo("Write 16-bit to pointer"); LogInfo("--------"); const u16 theshort = data & 0xFFFF; - const u32 offset = (data >> 16) << 1; + const u32 offset = (data >> 16) << 1; LogInfo("Pointer: %08x", ptr); LogInfo("Byte: %08x", theshort); LogInfo("Offset: %08x", offset); @@ -465,8 +430,8 @@ static bool Subtype_WriteToPointer(const ARAddr& addr, const u32 data) default: LogInfo("Bad Size"); PanicAlertT("Action Replay Error: Invalid size " - "(%08x : address = %08x) in Write To Pointer (%s)", - addr.size, addr.gcaddr, current_code->name.c_str()); + "(%08x : address = %08x) in Write To Pointer (%s)", + addr.size, addr.gcaddr, s_current_code->name.c_str()); return false; } return true; @@ -512,8 +477,10 @@ static bool Subtype_AddCode(const ARAddr& addr, const u32 data) LogInfo("--------"); const u32 read = PowerPC::HostRead_U32(new_addr); - const float fread = *((float*)&read) + (float)data; // data contains an integer value - const u32 newval = *((u32*)&fread); + const float read_float = reinterpret_cast(read); + // data contains an (unsigned?) integer value + const float fread = read_float + static_cast(data); + const u32 newval = reinterpret_cast(fread); PowerPC::HostWrite_U32(newval, new_addr); LogInfo("Old Value %08x", read); LogInfo("Increment %08x", data); @@ -525,8 +492,8 @@ static bool Subtype_AddCode(const ARAddr& addr, const u32 data) default: LogInfo("Bad Size"); PanicAlertT("Action Replay Error: Invalid size " - "(%08x : address = %08x) in Add Code (%s)", - addr.size, addr.gcaddr, current_code->name.c_str()); + "(%08x : address = %08x) in Add Code (%s)", + addr.size, addr.gcaddr, s_current_code->name.c_str()); return false; } return true; @@ -540,18 +507,20 @@ static bool Subtype_MasterCodeAndWriteToCCXXXXXX(const ARAddr& addr, const u32 d // u8 mcode_count = (data & 0xFF00) >> 8; // u8 mcode_number = data & 0xFF; PanicAlertT("Action Replay Error: Master Code and Write To CCXXXXXX not implemented (%s)\n" - "Master codes are not needed. Do not use master codes.", current_code->name.c_str()); + "Master codes are not needed. Do not use master codes.", + s_current_code->name.c_str()); return false; } -static bool ZeroCode_FillAndSlide(const u32 val_last, const ARAddr& addr, const u32 data) // This needs more testing +// This needs more testing +static bool ZeroCode_FillAndSlide(const u32 val_last, const ARAddr& addr, const u32 data) { - const u32 new_addr = ((ARAddr*)&val_last)->GCAddress(); - const u8 size = ((ARAddr*)&val_last)->size; + const u32 new_addr = ARAddr(val_last).GCAddress(); + const u8 size = ARAddr(val_last).size; - const s16 addr_incr = (s16)(data & 0xFFFF); - const s8 val_incr = (s8)(data >> 24); - const u8 write_num = (data & 0xFF0000) >> 16; + const s16 addr_incr = static_cast(data & 0xFFFF); + const s8 val_incr = static_cast(data >> 24); + const u8 write_num = static_cast((data & 0xFF0000) >> 16); u32 val = addr; u32 curr_addr = new_addr; @@ -612,7 +581,8 @@ static bool ZeroCode_FillAndSlide(const u32 val_last, const ARAddr& addr, const default: LogInfo("Bad Size"); - PanicAlertT("Action Replay Error: Invalid size (%08x : address = %08x) in Fill and Slide (%s)", size, new_addr, current_code->name.c_str()); + PanicAlertT("Action Replay Error: Invalid size (%08x : address = %08x) in Fill and Slide (%s)", + size, new_addr, s_current_code->name.c_str()); return false; } return true; @@ -659,7 +629,8 @@ static bool ZeroCode_MemoryCopy(const u32 val_last, const ARAddr& addr, const u3 else { LogInfo("Bad Value"); - PanicAlertT("Action Replay Error: Invalid value (%08x) in Memory Copy (%s)", (data & ~0x7FFF), current_code->name.c_str()); + PanicAlertT("Action Replay Error: Invalid value (%08x) in Memory Copy (%s)", + (data & ~0x7FFF), s_current_code->name.c_str()); return false; } return true; @@ -695,9 +666,9 @@ static bool NormalCode(const ARAddr& addr, const u32 data) default: LogInfo("Bad Subtype"); - PanicAlertT("Action Replay: Normal Code 0: Invalid Subtype %08x (%s)", addr.subtype, current_code->name.c_str()); + PanicAlertT("Action Replay: Normal Code 0: Invalid Subtype %08x (%s)", addr.subtype, + s_current_code->name.c_str()); return false; - break; } return true; @@ -709,43 +680,36 @@ static bool CompareValues(const u32 val1, const u32 val2, const int type) { case CONDTIONAL_EQUAL: LogInfo("Type 1: If Equal"); - return (val1 == val2); - break; + return val1 == val2; case CONDTIONAL_NOT_EQUAL: LogInfo("Type 2: If Not Equal"); - return (val1 != val2); - break; + return val1 != val2; case CONDTIONAL_LESS_THAN_SIGNED: LogInfo("Type 3: If Less Than (Signed)"); - return ((int)val1 < (int)val2); - break; + return static_cast(val1) < static_cast(val2); case CONDTIONAL_GREATER_THAN_SIGNED: LogInfo("Type 4: If Greater Than (Signed)"); - return ((int)val1 >(int)val2); - break; + return static_cast(val1) > static_cast(val2); case CONDTIONAL_LESS_THAN_UNSIGNED: LogInfo("Type 5: If Less Than (Unsigned)"); - return (val1 < val2); - break; + return val1 < val2; case CONDTIONAL_GREATER_THAN_UNSIGNED: LogInfo("Type 6: If Greater Than (Unsigned)"); - return (val1 > val2); - break; + return val1 > val2; case CONDTIONAL_AND: LogInfo("Type 7: If And"); return !!(val1 & val2); // bitwise AND - break; default: LogInfo("Unknown Compare type"); - PanicAlertT("Action Replay: Invalid Normal Code Type %08x (%s)", type, current_code->name.c_str()); + PanicAlertT("Action Replay: Invalid Normal Code Type %08x (%s)", + type, s_current_code->name.c_str()); return false; - break; } } @@ -761,11 +725,11 @@ static bool ConditionalCode(const ARAddr& addr, const u32 data, int* const pSkip switch (addr.size) { case DATATYPE_8BIT: - result = CompareValues((u32)PowerPC::HostRead_U8(new_addr), (data & 0xFF), addr.type); + result = CompareValues(PowerPC::HostRead_U8(new_addr), (data & 0xFF), addr.type); break; case DATATYPE_16BIT: - result = CompareValues((u32)PowerPC::HostRead_U16(new_addr), (data & 0xFFFF), addr.type); + result = CompareValues(PowerPC::HostRead_U16(new_addr), (data & 0xFFFF), addr.type); break; case DATATYPE_32BIT_FLOAT: @@ -775,9 +739,9 @@ static bool ConditionalCode(const ARAddr& addr, const u32 data, int* const pSkip default: LogInfo("Bad Size"); - PanicAlertT("Action Replay: Conditional Code: Invalid Size %08x (%s)", addr.size, current_code->name.c_str()); + PanicAlertT("Action Replay: Conditional Code: Invalid Size %08x (%s)", addr.size, + s_current_code->name.c_str()); return false; - break; } // if the comparison failed we need to skip some lines @@ -794,58 +758,41 @@ static bool ConditionalCode(const ARAddr& addr, const u32 data, int* const pSkip // Skip lines until a "00000000 40000000" line is reached case CONDTIONAL_ALL_LINES: case CONDTIONAL_ALL_LINES_UNTIL: - *pSkipCount = -(int) addr.subtype; + *pSkipCount = -static_cast(addr.subtype); break; default: LogInfo("Bad Subtype"); - PanicAlertT("Action Replay: Normal Code %i: Invalid subtype %08x (%s)", 1, addr.subtype, current_code->name.c_str()); + PanicAlertT("Action Replay: Normal Code %i: Invalid subtype %08x (%s)", + 1, addr.subtype, s_current_code->name.c_str()); return false; - break; } } return true; } - -void RunAllActive() -{ - if (SConfig::GetInstance().bEnableCheats) - { - for (auto& activeCode : activeCodes) - { - if (activeCode.active) - { - activeCode.active = RunCode(activeCode); - LogInfo("\n"); - } - } - - b_RanOnce = true; - } -} - -bool RunCode(const ARCode &arcode) +// NOTE: Lock needed to give mutual exclusion to s_current_code and LogInfo +static bool RunCodeLocked(const ARCode& arcode) { // The mechanism is different than what the real AR uses, so there may be compatibility problems. - bool doFillNSlide = false; - bool doMemoryCopy = false; + bool do_fill_and_slide = false; + bool do_memory_copy = false; // used for conditional codes int skip_count = 0; u32 val_last = 0; - current_code = &arcode; + s_current_code = &arcode; LogInfo("Code Name: %s", arcode.name.c_str()); LogInfo("Number of codes: %zu", arcode.ops.size()); for (const AREntry& entry : arcode.ops) { - const ARAddr& addr = *(ARAddr*)&entry.cmd_addr; + const ARAddr addr(entry.cmd_addr); const u32 data = entry.value; // after a conditional code, skip lines if needed @@ -877,9 +824,9 @@ bool RunCode(const ARCode &arcode) //LogInfo("Command: %08x", cmd); // Do Fill & Slide - if (doFillNSlide) + if (do_fill_and_slide) { - doFillNSlide = false; + do_fill_and_slide = false; LogInfo("Doing Fill And Slide"); if (false == ZeroCode_FillAndSlide(val_last, addr, data)) return false; @@ -887,9 +834,9 @@ bool RunCode(const ARCode &arcode) } // Memory Copy - if (doMemoryCopy) + if (do_memory_copy) { - doMemoryCopy = false; + do_memory_copy = false; LogInfo("Doing Memory Copy"); if (false == ZeroCode_MemoryCopy(val_last, addr, data)) return false; @@ -912,7 +859,7 @@ bool RunCode(const ARCode &arcode) // Zero codes if (0x0 == addr) // Check if the code is a zero code { - const u8 zcode = (data >> 29); + const u8 zcode = data >> 29; LogInfo("Doing Zero Code %08x", zcode); @@ -921,7 +868,6 @@ bool RunCode(const ARCode &arcode) case ZCODE_END: // END OF CODES LogInfo("ZCode: End Of Codes"); return true; - break; // TODO: the "00000000 40000000"(end if) codes fall into this case, I don't think that is correct case ZCODE_NORM: // Normal execution of codes @@ -934,19 +880,18 @@ bool RunCode(const ARCode &arcode) LogInfo("ZCode: Executes all codes in the same row, Set register 1BB4 to 1 (zcode not supported)"); PanicAlertT("Zero 3 code not supported"); return false; - break; case ZCODE_04: // Fill & Slide or Memory Copy if (0x3 == ((data >> 25) & 0x03)) { LogInfo("ZCode: Memory Copy"); - doMemoryCopy = true; + do_memory_copy = true; val_last = data; } else { LogInfo("ZCode: Fill And Slide"); - doFillNSlide = true; + do_fill_and_slide = true; val_last = data; } break; @@ -955,7 +900,6 @@ bool RunCode(const ARCode &arcode) LogInfo("ZCode: Unknown"); PanicAlertT("Zero code unknown to Dolphin: %08x", zcode); return false; - break; } // done handling zero codes @@ -981,9 +925,25 @@ bool RunCode(const ARCode &arcode) } } - b_RanOnce = true; - return true; } +void RunAllActive() +{ + if (!SConfig::GetInstance().bEnableCheats) + return; + + // If the mutex is idle then acquiring it should be cheap, fast mutexes + // are only atomic ops unless contested. It should be rare for this to + // be contested. + std::lock_guard guard(s_lock); + s_active_codes.erase(std::remove_if(s_active_codes.begin(), s_active_codes.end(), [](const ARCode& code) + { + bool success = RunCodeLocked(code); + LogInfo("\n"); + return !success; + }), s_active_codes.end()); + s_disable_logging = true; +} + } // namespace ActionReplay diff --git a/Source/Core/Core/ActionReplay.h b/Source/Core/Core/ActionReplay.h index be6c4d0bbc..49f4746181 100644 --- a/Source/Core/Core/ActionReplay.h +++ b/Source/Core/Core/ActionReplay.h @@ -4,7 +4,6 @@ #pragma once -#include #include #include #include "Common/CommonTypes.h" @@ -31,18 +30,16 @@ struct ARCode }; void RunAllActive(); -bool RunCode(const ARCode &arcode); + void ApplyCodes(const std::vector& codes); -void AddCode(const ARCode& new_code); -void* RegisterCodeChangeCallback(std::function callback); -void UnregisterCodeChangeCallback(void* token); -void LoadAndApplyCodes(const IniFile& globalini, const IniFile& localIni); -std::vector LoadCodes(const IniFile& globalini, const IniFile& localIni); -size_t GetCodeListSize(); -ARCode GetARCode(size_t index); -void SetARCode_IsActive(bool active, size_t index); -void UpdateActiveList(); +void AddCode(ARCode new_code); +void LoadAndApplyCodes(const IniFile& global_ini, const IniFile& local_ini); + +std::vector LoadCodes(const IniFile& global_ini, const IniFile& local_ini); +void SaveCodes(IniFile* local_ini, const std::vector& codes); + void EnableSelfLogging(bool enable); -const std::vector &GetSelfLog(); +std::vector GetSelfLog(); +void ClearSelfLog(); bool IsSelfLogging(); } // namespace diff --git a/Source/Core/DolphinWX/ARCodeAddEdit.cpp b/Source/Core/DolphinWX/ARCodeAddEdit.cpp index b1ad736a38..e5f721c754 100644 --- a/Source/Core/DolphinWX/ARCodeAddEdit.cpp +++ b/Source/Core/DolphinWX/ARCodeAddEdit.cpp @@ -160,6 +160,7 @@ void CARCodeAddEdit::SaveCheatData(wxCommandEvent& WXUNUSED(event)) newCheat.name = WxStrToStr(EditCheatName->GetValue()); newCheat.ops = decryptedLines; newCheat.active = true; + newCheat.user_defined = true; arCodes->push_back(newCheat); } diff --git a/Source/Core/DolphinWX/Cheats/CheatSearchTab.cpp b/Source/Core/DolphinWX/Cheats/CheatSearchTab.cpp index 469209febd..57a5db53c5 100644 --- a/Source/Core/DolphinWX/Cheats/CheatSearchTab.cpp +++ b/Source/Core/DolphinWX/Cheats/CheatSearchTab.cpp @@ -174,7 +174,6 @@ void CheatSearchTab::OnCreateARCodeClicked(wxCommandEvent&) const u32 address = m_search_results[idx].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(); } diff --git a/Source/Core/DolphinWX/Cheats/CheatsWindow.cpp b/Source/Core/DolphinWX/Cheats/CheatsWindow.cpp index 83089b58c8..8d040a23e6 100644 --- a/Source/Core/DolphinWX/Cheats/CheatsWindow.cpp +++ b/Source/Core/DolphinWX/Cheats/CheatsWindow.cpp @@ -6,9 +6,10 @@ #include #include #include -#include #include +#include #include +#include #include #include #include @@ -32,6 +33,7 @@ #include "Core/GeckoCode.h" #include "Core/GeckoCodeConfig.h" #include "DolphinWX/Frame.h" +#include "DolphinWX/Globals.h" #include "DolphinWX/Main.h" #include "DolphinWX/WxUtils.h" #include "DolphinWX/Cheats/CheatSearchTab.h" @@ -39,10 +41,12 @@ #include "DolphinWX/Cheats/CreateCodeDialog.h" #include "DolphinWX/Cheats/GeckoCodeDiag.h" -namespace +wxDEFINE_EVENT(DOLPHIN_EVT_ADD_NEW_ACTION_REPLAY_CODE, wxCommandEvent); + +struct wxCheatsWindow::CodeData : public wxClientData { -wxDEFINE_EVENT(DOLPHIN_EVT_UPDATE_CHEAT_LIST, wxThreadEvent); -} + ActionReplay::ARCode code; +}; wxCheatsWindow::wxCheatsWindow(wxWindow* const parent) : wxDialog(parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX | wxMINIMIZE_BOX | wxDIALOG_NO_PARENT) @@ -50,10 +54,9 @@ wxCheatsWindow::wxCheatsWindow(wxWindow* const parent) // Create the GUI controls Init_ChildControls(); - m_ar_callback_token = ActionReplay::RegisterCodeChangeCallback(std::bind(&wxCheatsWindow::OnActionReplayModified, this)); - // load codes UpdateGUI(); + wxTheApp->Bind(DOLPHIN_EVT_LOCAL_INI_CHANGED, &wxCheatsWindow::OnEvent_CheatsList_Update, this); SetSize(wxSize(-1, 600)); Center(); @@ -62,7 +65,6 @@ wxCheatsWindow::wxCheatsWindow(wxWindow* const parent) wxCheatsWindow::~wxCheatsWindow() { - ActionReplay::UnregisterCodeChangeCallback(m_ar_callback_token); main_frame->g_CheatsWindow = nullptr; } @@ -75,18 +77,17 @@ void wxCheatsWindow::Init_ChildControls() // Cheats List Tab m_tab_cheats = new wxPanel(m_notebook_main, wxID_ANY); - m_checklistbox_cheats_list = new wxCheckListBox(m_tab_cheats, wxID_ANY, wxDefaultPosition, wxSize(300, 0), m_cheat_string_list, wxLB_HSCROLL, wxDefaultValidator); + m_checklistbox_cheats_list = new wxCheckListBox(m_tab_cheats, wxID_ANY, wxDefaultPosition, wxSize(300, 0), 0, nullptr, wxLB_HSCROLL); m_checklistbox_cheats_list->Bind(wxEVT_LISTBOX, &wxCheatsWindow::OnEvent_CheatsList_ItemSelected, this); - m_checklistbox_cheats_list->Bind(wxEVT_CHECKLISTBOX, &wxCheatsWindow::OnEvent_CheatsList_ItemToggled, this); - m_label_code_name = new wxStaticText(m_tab_cheats, wxID_ANY, _("Name: ")); + m_label_code_name = new wxStaticText(m_tab_cheats, wxID_ANY, _("Name: "), wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE); m_groupbox_info = new wxStaticBox(m_tab_cheats, wxID_ANY, _("Code Info")); m_label_num_codes = new wxStaticText(m_tab_cheats, wxID_ANY, _("Number Of Codes: ")); m_listbox_codes_list = new wxListBox(m_tab_cheats, wxID_ANY, wxDefaultPosition, wxSize(120, 150), 0, nullptr, wxLB_HSCROLL); wxStaticBoxSizer* sGroupBoxInfo = new wxStaticBoxSizer(m_groupbox_info, wxVERTICAL); - sGroupBoxInfo->Add(m_label_code_name, 0, wxALL, 5); + sGroupBoxInfo->Add(m_label_code_name, 0, wxEXPAND | wxALL, 5); sGroupBoxInfo->Add(m_label_num_codes, 0, wxALL, 5); sGroupBoxInfo->Add(m_listbox_codes_list, 1, wxALL, 5); @@ -104,6 +105,8 @@ void wxCheatsWindow::Init_ChildControls() wxButton* const button_updatelog = new wxButton(m_tab_log, wxID_ANY, _("Update")); button_updatelog->Bind(wxEVT_BUTTON, &wxCheatsWindow::OnEvent_ButtonUpdateLog_Press, this); + wxButton* const button_clearlog = new wxButton(m_tab_log, wxID_ANY, _("Clear")); + button_clearlog->Bind(wxEVT_BUTTON, &wxCheatsWindow::OnClearActionReplayLog, this); m_checkbox_log_ar = new wxCheckBox(m_tab_log, wxID_ANY, _("Enable AR Logging")); m_checkbox_log_ar->Bind(wxEVT_CHECKBOX, &wxCheatsWindow::OnEvent_CheckBoxEnableLogging_StateChange, this); @@ -114,6 +117,7 @@ void wxCheatsWindow::Init_ChildControls() wxBoxSizer *HStrip1 = new wxBoxSizer(wxHORIZONTAL); HStrip1->Add(m_checkbox_log_ar, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); HStrip1->Add(button_updatelog, 0, wxALL, 5); + HStrip1->Add(button_clearlog, 0, wxALL, 5); wxBoxSizer *sTabLog = new wxBoxSizer(wxVERTICAL); sTabLog->Add(HStrip1, 0, wxALL, 5); @@ -128,19 +132,15 @@ void wxCheatsWindow::Init_ChildControls() m_notebook_main->AddPage(tab_cheat_search, _("Cheat Search")); m_notebook_main->AddPage(m_tab_log, _("Logging")); - // Button Strip - m_button_apply = new wxButton(this, wxID_APPLY, _("Apply")); - m_button_apply->Bind(wxEVT_BUTTON, &wxCheatsWindow::OnEvent_ApplyChanges_Press, this); - wxButton* const button_cancel = new wxButton(this, wxID_CANCEL, _("Cancel")); - button_cancel->Bind(wxEVT_BUTTON, &wxCheatsWindow::OnEvent_ButtonClose_Press, this); - + Bind(wxEVT_BUTTON, &wxCheatsWindow::OnEvent_ApplyChanges_Press, this, wxID_APPLY); + Bind(wxEVT_BUTTON, &wxCheatsWindow::OnEvent_ButtonClose_Press, this, wxID_CANCEL); Bind(wxEVT_CLOSE_WINDOW, &wxCheatsWindow::OnEvent_Close, this); - Bind(DOLPHIN_EVT_UPDATE_CHEAT_LIST, &wxCheatsWindow::OnEvent_CheatsList_Update, this); + Bind(DOLPHIN_EVT_ADD_NEW_ACTION_REPLAY_CODE, &wxCheatsWindow::OnNewARCodeCreated, this); - wxStdDialogButtonSizer* const sButtons = new wxStdDialogButtonSizer(); - sButtons->AddButton(m_button_apply); - sButtons->AddButton(button_cancel); - sButtons->Realize(); + wxStdDialogButtonSizer* const sButtons = CreateStdDialogButtonSizer(wxAPPLY | wxCANCEL); + m_button_apply = sButtons->GetApplyButton(); + SetEscapeId(wxID_CANCEL); + SetAffirmativeId(wxID_CANCEL); wxBoxSizer* const sMain = new wxBoxSizer(wxVERTICAL); sMain->Add(m_notebook_main, 1, wxEXPAND | wxALL, 5); @@ -165,7 +165,9 @@ void wxCheatsWindow::UpdateGUI() const SConfig& parameters = SConfig::GetInstance(); m_gameini_default = parameters.LoadDefaultGameIni(); m_gameini_local = parameters.LoadLocalGameIni(); - m_gameini_local_path = File::GetUserPath(D_GAMESETTINGS_IDX) + parameters.GetUniqueID() + ".ini"; + m_game_id = parameters.GetUniqueID(); + m_game_revision = parameters.m_revision; + m_gameini_local_path = File::GetUserPath(D_GAMESETTINGS_IDX) + m_game_id + ".ini"; Load_ARCodes(); Load_GeckoCodes(); @@ -176,32 +178,28 @@ void wxCheatsWindow::UpdateGUI() // write the ISO name in the title if (Core::IsRunning()) - SetTitle(title + ": " + parameters.GetUniqueID() + " - " + parameters.m_strName); + SetTitle(title + StrToWxStr(": " + m_game_id + " - " + parameters.m_strName)); else SetTitle(title); } void wxCheatsWindow::Load_ARCodes() { - using namespace ActionReplay; - m_checklistbox_cheats_list->Clear(); if (!Core::IsRunning()) return; - m_index_list.clear(); - size_t size = GetCodeListSize(); - for (size_t i = 0; i < size; i++) + m_checklistbox_cheats_list->Freeze(); + for (auto& code : ActionReplay::LoadCodes(m_gameini_default, m_gameini_local)) { - ARCode code = GetARCode(i); - ARCodeIndex ind; - u32 index = m_checklistbox_cheats_list->Append(StrToWxStr(code.name)); - m_checklistbox_cheats_list->Check(index, code.active); - ind.index = i; - ind.uiIndex = index; - m_index_list.push_back(ind); + CodeData* cd = new CodeData(); + cd->code = std::move(code); + int index = m_checklistbox_cheats_list->Append(wxCheckListBox::EscapeMnemonics(StrToWxStr(cd->code.name)), + cd); + m_checklistbox_cheats_list->Check(index, cd->code.active); } + m_checklistbox_cheats_list->Thaw(); } void wxCheatsWindow::Load_GeckoCodes() @@ -209,70 +207,81 @@ void wxCheatsWindow::Load_GeckoCodes() m_geckocode_panel->LoadCodes(m_gameini_default, m_gameini_local, SConfig::GetInstance().GetUniqueID(), true); } -void wxCheatsWindow::OnEvent_CheatsList_ItemSelected(wxCommandEvent& WXUNUSED(event)) +void wxCheatsWindow::OnNewARCodeCreated(wxCommandEvent& ev) { - using namespace ActionReplay; + auto code = static_cast(ev.GetClientData()); + ActionReplay::AddCode(*code); - int index = m_checklistbox_cheats_list->GetSelection(); - for (size_t i = 0; i < m_index_list.size(); i++) - { - if ((int)m_index_list[i].uiIndex == index) - { - ARCode code = GetARCode(i); - m_label_code_name->SetLabel(_("Name: ") + StrToWxStr(code.name)); - - std::string numcodes = StringFromFormat("Number of Codes: %zu", code.ops.size()); - m_label_num_codes->SetLabel(StrToWxStr(numcodes)); - m_listbox_codes_list->Clear(); - - for (const AREntry& entry : code.ops) - { - std::string ops = StringFromFormat("%08x %08x", entry.cmd_addr, entry.value); - m_listbox_codes_list->Append(StrToWxStr(ops)); - } - } - } + CodeData* cd = new CodeData(); + cd->code = *code; + int idx = m_checklistbox_cheats_list->Append(wxCheckListBox::EscapeMnemonics(StrToWxStr(code->name)), + cd); + m_checklistbox_cheats_list->Check(idx, code->active); } -void wxCheatsWindow::OnEvent_CheatsList_ItemToggled(wxCommandEvent& WXUNUSED(event)) +void wxCheatsWindow::OnEvent_CheatsList_ItemSelected(wxCommandEvent& event) { - int index = m_checklistbox_cheats_list->GetSelection(); - for (const ARCodeIndex& code_index : m_index_list) + CodeData* cd = static_cast(event.GetClientObject()); + + m_label_code_name->SetLabelText(_("Name: ") + StrToWxStr(cd->code.name)); + m_label_code_name->Wrap(m_label_code_name->GetSize().GetWidth()); + m_label_code_name->InvalidateBestSize(); + m_label_num_codes->SetLabelText(wxString::Format("%s%zu", _("Number Of Codes: "), cd->code.ops.size())); + + m_listbox_codes_list->Freeze(); + m_listbox_codes_list->Clear(); + for (const ActionReplay::AREntry& entry : cd->code.ops) { - if ((int)code_index.uiIndex == index) - { - m_ar_ignore_callback = true; - ActionReplay::SetARCode_IsActive(m_checklistbox_cheats_list->IsChecked(index), code_index.index); - } + m_listbox_codes_list->Append(wxString::Format("%08x %08x", entry.cmd_addr, entry.value)); } + m_listbox_codes_list->Thaw(); + + m_tab_cheats->Layout(); } -void wxCheatsWindow::OnEvent_CheatsList_Update(wxThreadEvent&) +void wxCheatsWindow::OnEvent_CheatsList_Update(wxCommandEvent& ev) { - if (m_ar_ignore_callback) + ev.Skip(); + if (WxStrToStr(ev.GetString()) != m_game_id) + return; + if (m_ignore_ini_callback) { - m_ar_ignore_callback = false; + m_ignore_ini_callback = false; return; } - Load_ARCodes(); -} - -void wxCheatsWindow::OnActionReplayModified() -{ - // NOTE: This is an arbitrary thread context - GetEventHandler()->QueueEvent(new wxThreadEvent(DOLPHIN_EVT_UPDATE_CHEAT_LIST)); + UpdateGUI(); } void wxCheatsWindow::OnEvent_ApplyChanges_Press(wxCommandEvent& ev) { + // Convert embedded metadata back into ARCode vector and update active states + std::vector code_vec; + code_vec.reserve(m_checklistbox_cheats_list->GetCount()); + for (unsigned int i = 0; i < m_checklistbox_cheats_list->GetCount(); ++i) + { + CodeData* cd = static_cast(m_checklistbox_cheats_list->GetClientObject(i)); + cd->code.active = m_checklistbox_cheats_list->IsChecked(i); + code_vec.push_back(cd->code); + } + + // Apply Action Replay code changes + ActionReplay::ApplyCodes(code_vec); + // Apply Gecko Code changes Gecko::SetActiveCodes(m_geckocode_panel->GetCodes()); - // Save gameini, with changed gecko codes + // Save gameini, with changed codes if (m_gameini_local_path.size()) { + ActionReplay::SaveCodes(&m_gameini_local, code_vec); Gecko::SaveCodes(m_gameini_local, m_geckocode_panel->GetCodes()); m_gameini_local.Save(m_gameini_local_path); + + wxCommandEvent ini_changed(DOLPHIN_EVT_LOCAL_INI_CHANGED); + ini_changed.SetString(StrToWxStr(m_game_id)); + ini_changed.SetInt(m_game_revision); + m_ignore_ini_callback = true; + wxTheApp->ProcessEvent(ini_changed); } ev.Skip(); @@ -291,6 +300,12 @@ void wxCheatsWindow::OnEvent_ButtonUpdateLog_Press(wxCommandEvent& WXUNUSED(even wxEndBusyCursor(); } +void wxCheatsWindow::OnClearActionReplayLog(wxCommandEvent& event) +{ + ActionReplay::ClearSelfLog(); + OnEvent_ButtonUpdateLog_Press(event); +} + void wxCheatsWindow::OnEvent_CheckBoxEnableLogging_StateChange(wxCommandEvent& WXUNUSED(event)) { ActionReplay::EnableSelfLogging(m_checkbox_log_ar->IsChecked()); diff --git a/Source/Core/DolphinWX/Cheats/CheatsWindow.h b/Source/Core/DolphinWX/Cheats/CheatsWindow.h index 34e66bb7ff..a3f3d8ef06 100644 --- a/Source/Core/DolphinWX/Cheats/CheatsWindow.h +++ b/Source/Core/DolphinWX/Cheats/CheatsWindow.h @@ -29,6 +29,8 @@ namespace Gecko class CodeConfigPanel; } +wxDECLARE_EVENT(DOLPHIN_EVT_ADD_NEW_ACTION_REPLAY_CODE, wxCommandEvent); + class wxCheatsWindow final : public wxDialog { public: @@ -37,11 +39,7 @@ public: void UpdateGUI(); private: - struct ARCodeIndex - { - u32 uiIndex; - size_t index; - }; + struct CodeData; // --- GUI Controls --- wxButton* m_button_apply; @@ -63,18 +61,14 @@ private: wxStaticBox* m_groupbox_info; - wxArrayString m_cheat_string_list; - - std::vector m_index_list; - Gecko::CodeConfigPanel* m_geckocode_panel; IniFile m_gameini_default; IniFile m_gameini_local; std::string m_gameini_local_path; + std::string m_game_id; + u32 m_game_revision; - // ActionReplay::UnregisterCodeChangeCallback handle - void* m_ar_callback_token = nullptr; - bool m_ar_ignore_callback = false; + bool m_ignore_ini_callback = false; void Init_ChildControls(); @@ -82,6 +76,8 @@ private: void Load_GeckoCodes(); // --- Wx Events Handlers --- + // Cheat Search + void OnNewARCodeCreated(wxCommandEvent& ev); // Close Button void OnEvent_ButtonClose_Press(wxCommandEvent& event); @@ -89,15 +85,14 @@ private: // Cheats List void OnEvent_CheatsList_ItemSelected(wxCommandEvent& event); - void OnEvent_CheatsList_ItemToggled(wxCommandEvent& event); - void OnEvent_CheatsList_Update(wxThreadEvent& event); - void OnActionReplayModified(); + void OnEvent_CheatsList_Update(wxCommandEvent& event); // Apply Changes Button void OnEvent_ApplyChanges_Press(wxCommandEvent& event); // Update Log Button void OnEvent_ButtonUpdateLog_Press(wxCommandEvent& event); + void OnClearActionReplayLog(wxCommandEvent& event); // Enable Logging Checkbox void OnEvent_CheckBoxEnableLogging_StateChange(wxCommandEvent& event); diff --git a/Source/Core/DolphinWX/Cheats/CreateCodeDialog.cpp b/Source/Core/DolphinWX/Cheats/CreateCodeDialog.cpp index d53136987a..34b6baac82 100644 --- a/Source/Core/DolphinWX/Cheats/CreateCodeDialog.cpp +++ b/Source/Core/DolphinWX/Cheats/CreateCodeDialog.cpp @@ -12,6 +12,7 @@ #include "Core/ConfigManager.h" #include "DolphinWX/ISOProperties.h" #include "DolphinWX/WxUtils.h" +#include "DolphinWX/Cheats/CheatsWindow.h" #include "DolphinWX/Cheats/CreateCodeDialog.h" CreateCodeDialog::CreateCodeDialog(wxWindow* const parent, const u32 address) @@ -70,27 +71,16 @@ void CreateCodeDialog::PressOK(wxCommandEvent& ev) return; } - //wxString full_code = textctrl_code->GetValue(); - //full_code += ' '; - //full_code += wxString::Format("0x%08x", code_value); - // create the new code ActionReplay::ARCode new_cheat; new_cheat.active = false; new_cheat.user_defined = true; new_cheat.name = WxStrToStr(code_name); new_cheat.ops.emplace_back(ActionReplay::AREntry(m_code_address, code_value)); - ActionReplay::AddCode(new_cheat); - // 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, {}), this); - // add the code to the isoproperties arcode list - isoprops.AddARCode(new_cheat); - // save the gameini - isoprops.SaveGameConfig(); - } + wxCommandEvent add_event(DOLPHIN_EVT_ADD_NEW_ACTION_REPLAY_CODE, GetId()); + add_event.SetClientData(&new_cheat); + GetParent()->GetEventHandler()->ProcessEvent(add_event); Close(); } diff --git a/Source/Core/DolphinWX/Frame.cpp b/Source/Core/DolphinWX/Frame.cpp index 60142db49c..4056ed4f90 100644 --- a/Source/Core/DolphinWX/Frame.cpp +++ b/Source/Core/DolphinWX/Frame.cpp @@ -223,6 +223,7 @@ bool CRenderFrame::ShowFullScreen(bool show, long style) // help button. wxDEFINE_EVENT(wxEVT_HOST_COMMAND, wxCommandEvent); +wxDEFINE_EVENT(DOLPHIN_EVT_LOCAL_INI_CHANGED, wxCommandEvent); BEGIN_EVENT_TABLE(CFrame, CRenderFrame) diff --git a/Source/Core/DolphinWX/Globals.h b/Source/Core/DolphinWX/Globals.h index 4499491a8c..ef41b61e79 100644 --- a/Source/Core/DolphinWX/Globals.h +++ b/Source/Core/DolphinWX/Globals.h @@ -331,10 +331,11 @@ enum // custom message macro #define EVT_HOST_COMMAND(id, fn) \ - DECLARE_EVENT_TABLE_ENTRY(\ - wxEVT_HOST_COMMAND, id, wxID_ANY, \ - (wxObjectEventFunction)(wxEventFunction) wxStaticCastEvent(wxCommandEventFunction, &fn), \ - (wxObject*) nullptr \ - ), + EVT_COMMAND(id, wxEVT_HOST_COMMAND, fn) wxDECLARE_EVENT(wxEVT_HOST_COMMAND, wxCommandEvent); + +// Sent to wxTheApp +// GetString() == Game's Unique ID +// GetInt() == Game's Revision +wxDECLARE_EVENT(DOLPHIN_EVT_LOCAL_INI_CHANGED, wxCommandEvent); diff --git a/Source/Core/DolphinWX/ISOProperties.cpp b/Source/Core/DolphinWX/ISOProperties.cpp index 77c64cd150..9071f70bd3 100644 --- a/Source/Core/DolphinWX/ISOProperties.cpp +++ b/Source/Core/DolphinWX/ISOProperties.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -63,7 +64,7 @@ #include "DiscIO/VolumeCreator.h" #include "DolphinWX/ARCodeAddEdit.h" #include "DolphinWX/GameListCtrl.h" -//#include "DolphinWX/Frame.h" +#include "DolphinWX/Globals.h" #include "DolphinWX/ISOFile.h" #include "DolphinWX/ISOProperties.h" #include "DolphinWX/PatchAddEdit.h" @@ -230,6 +231,8 @@ CISOProperties::CISOProperties(const GameListItem& game_list_item, wxWindow* par m_Treectrl->Expand(RootId); } + + wxTheApp->Bind(DOLPHIN_EVT_LOCAL_INI_CHANGED, &CISOProperties::OnLocalIniModified, this); } CISOProperties::~CISOProperties() @@ -1164,6 +1167,9 @@ bool CISOProperties::SaveGameConfig() if (success && File::GetSize(GameIniFileLocal) == 0) File::Delete(GameIniFileLocal); + if (success) + GenerateLocalIniModified(); + return success; } @@ -1211,6 +1217,24 @@ void CISOProperties::LaunchExternalEditor(const std::string& filename, bool wait #endif } +void CISOProperties::GenerateLocalIniModified() +{ + wxCommandEvent event_update(DOLPHIN_EVT_LOCAL_INI_CHANGED); + event_update.SetString(StrToWxStr(game_id)); + event_update.SetInt(OpenGameListItem.GetRevision()); + wxTheApp->ProcessEvent(event_update); +} + +void CISOProperties::OnLocalIniModified(wxCommandEvent& ev) +{ + ev.Skip(); + if (WxStrToStr(ev.GetString()) != game_id) + return; + + GameIniLocal.Load(GameIniFileLocal); + LoadGameConfig(); +} + void CISOProperties::OnEditConfig(wxCommandEvent& WXUNUSED (event)) { SaveGameConfig(); @@ -1221,8 +1245,7 @@ void CISOProperties::OnEditConfig(wxCommandEvent& WXUNUSED (event)) blankFile.close(); } LaunchExternalEditor(GameIniFileLocal, true); - GameIniLocal.Load(GameIniFileLocal); - LoadGameConfig(); + GenerateLocalIniModified(); } void CISOProperties::OnComputeMD5Sum(wxCommandEvent& WXUNUSED (event)) @@ -1301,7 +1324,7 @@ void CISOProperties::ListSelectionChanged(wxCommandEvent& event) break; case ID_CHEATS_LIST: if (Cheats->GetSelection() == wxNOT_FOUND || - DefaultCheats.find(Cheats->GetString(Cheats->GetSelection()).ToStdString()) != DefaultCheats.end()) + DefaultCheats.find(Cheats->RemoveMnemonics(Cheats->GetString(Cheats->GetSelection())).ToStdString()) != DefaultCheats.end()) { EditCheat->Disable(); RemoveCheat->Disable(); @@ -1405,44 +1428,24 @@ void CISOProperties::PatchButtonClicked(wxCommandEvent& event) void CISOProperties::ActionReplayList_Load() { - Cheats->Clear(); arCodes = ActionReplay::LoadCodes(GameIniDefault, GameIniLocal); + DefaultCheats.clear(); - u32 index = 0; + Cheats->Freeze(); + Cheats->Clear(); for (const ActionReplay::ARCode& arCode : arCodes) { - Cheats->Append(StrToWxStr(arCode.name)); - Cheats->Check(index, arCode.active); + int idx = Cheats->Append(Cheats->EscapeMnemonics(StrToWxStr(arCode.name))); + Cheats->Check(idx, arCode.active); if (!arCode.user_defined) DefaultCheats.insert(arCode.name); - ++index; } + Cheats->Thaw(); } void CISOProperties::ActionReplayList_Save() { - std::vector lines; - std::vector enabledLines; - u32 index = 0; - u32 cheats_chkbox_count = Cheats->GetCount(); - for (const ActionReplay::ARCode& code : arCodes) - { - if (code.active) - enabledLines.push_back("$" + code.name); - - // Do not save default cheats. - if (DefaultCheats.find(code.name) == DefaultCheats.end()) - { - lines.push_back("$" + code.name); - for (const ActionReplay::AREntry& op : code.ops) - { - lines.push_back(WxStrToStr(wxString::Format("%08X %08X", op.cmd_addr, op.value))); - } - } - ++index; - } - GameIniLocal.SetLines("ActionReplay_Enabled", enabledLines); - GameIniLocal.SetLines("ActionReplay", lines); + ActionReplay::SaveCodes(&GameIniLocal, arCodes); } void CISOProperties::ActionReplayButtonClicked(wxCommandEvent& event) @@ -1484,11 +1487,6 @@ void CISOProperties::ActionReplayButtonClicked(wxCommandEvent& event) RemoveCheat->Disable(); } -void CISOProperties::AddARCode(const ActionReplay::ARCode& code) -{ - arCodes.emplace_back(code); -} - void CISOProperties::OnChangeBannerLang(wxCommandEvent& event) { ChangeBannerDetails(OpenGameListItem.GetLanguages()[event.GetSelection()]); diff --git a/Source/Core/DolphinWX/ISOProperties.h b/Source/Core/DolphinWX/ISOProperties.h index 9ef253d8ea..4743d7299b 100644 --- a/Source/Core/DolphinWX/ISOProperties.h +++ b/Source/Core/DolphinWX/ISOProperties.h @@ -68,15 +68,6 @@ public: long style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); virtual ~CISOProperties(); - bool bRefreshList; - - // These are only public because of the ugly hack in CreateCodeDialog.cpp - void ActionReplayList_Load(); - bool SaveGameConfig(); - - // This only exists because of the ugly hack in CreateCodeDialog.cpp - void AddARCode(const ActionReplay::ARCode& code); - private: DECLARE_EVENT_TABLE(); @@ -242,9 +233,15 @@ private: std::set DefaultPatches; std::set DefaultCheats; + bool bRefreshList; + void LoadGameConfig(); + bool SaveGameConfig(); + void OnLocalIniModified(wxCommandEvent& ev); + void GenerateLocalIniModified(); void PatchList_Load(); void PatchList_Save(); + void ActionReplayList_Load(); void ActionReplayList_Save(); void ChangeBannerDetails(DiscIO::IVolume::ELanguage language); From 309d0e59f4697fdfc2763c7d3ee0075c514abe9b Mon Sep 17 00:00:00 2001 From: EmptyChaos Date: Sun, 29 May 2016 14:49:11 +0000 Subject: [PATCH 3/3] ISOProperties/GameListCtrl: Use Global INI Change event. ISOProperties no longer needs its hack to refresh the game list, the new INI Modified event can be used instead. --- Source/Core/DolphinWX/GameListCtrl.cpp | 139 +++++++++++++++--------- Source/Core/DolphinWX/GameListCtrl.h | 1 + Source/Core/DolphinWX/ISOFile.cpp | 46 +++++--- Source/Core/DolphinWX/ISOFile.h | 6 +- Source/Core/DolphinWX/ISOProperties.cpp | 18 +-- Source/Core/DolphinWX/ISOProperties.h | 4 +- 6 files changed, 128 insertions(+), 86 deletions(-) diff --git a/Source/Core/DolphinWX/GameListCtrl.cpp b/Source/Core/DolphinWX/GameListCtrl.cpp index 371d25887f..ef2ca15d18 100644 --- a/Source/Core/DolphinWX/GameListCtrl.cpp +++ b/Source/Core/DolphinWX/GameListCtrl.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -167,51 +168,50 @@ CGameListCtrl::CGameListCtrl(wxWindow* parent, const wxWindowID id, const Bind(wxEVT_MENU, &CGameListCtrl::OnMultiDecompressISO, this, IDM_MULTI_DECOMPRESS_ISO); Bind(wxEVT_MENU, &CGameListCtrl::OnDeleteISO, this, IDM_DELETE_ISO); Bind(wxEVT_MENU, &CGameListCtrl::OnChangeDisc, this, IDM_LIST_CHANGE_DISC); + + wxTheApp->Bind(DOLPHIN_EVT_LOCAL_INI_CHANGED, &CGameListCtrl::OnLocalIniModified, this); } CGameListCtrl::~CGameListCtrl() { - if (m_imageListSmall) - delete m_imageListSmall; - ClearIsoFiles(); } void CGameListCtrl::InitBitmaps() { wxSize size(96, 32); - m_imageListSmall = new wxImageList(96, 32); - SetImageList(m_imageListSmall, wxIMAGE_LIST_SMALL); + wxImageList* img_list = new wxImageList(96, 32); + AssignImageList(img_list, wxIMAGE_LIST_SMALL); m_FlagImageIndex.resize(DiscIO::IVolume::NUMBER_OF_COUNTRIES); - m_FlagImageIndex[DiscIO::IVolume::COUNTRY_JAPAN] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_Japan", size)); - m_FlagImageIndex[DiscIO::IVolume::COUNTRY_EUROPE] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_Europe", size)); - m_FlagImageIndex[DiscIO::IVolume::COUNTRY_USA] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_USA", size)); - m_FlagImageIndex[DiscIO::IVolume::COUNTRY_AUSTRALIA] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_Australia", size)); - m_FlagImageIndex[DiscIO::IVolume::COUNTRY_FRANCE] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_France", size)); - m_FlagImageIndex[DiscIO::IVolume::COUNTRY_GERMANY] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_Germany", size)); - m_FlagImageIndex[DiscIO::IVolume::COUNTRY_ITALY] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_Italy", size)); - m_FlagImageIndex[DiscIO::IVolume::COUNTRY_KOREA] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_Korea", size)); - m_FlagImageIndex[DiscIO::IVolume::COUNTRY_NETHERLANDS] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_Netherlands", size)); - m_FlagImageIndex[DiscIO::IVolume::COUNTRY_RUSSIA] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_Russia", size)); - m_FlagImageIndex[DiscIO::IVolume::COUNTRY_SPAIN] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_Spain", size)); - m_FlagImageIndex[DiscIO::IVolume::COUNTRY_TAIWAN] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_Taiwan", size)); - m_FlagImageIndex[DiscIO::IVolume::COUNTRY_WORLD] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_International", size)); - m_FlagImageIndex[DiscIO::IVolume::COUNTRY_UNKNOWN] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_Unknown", size)); + m_FlagImageIndex[DiscIO::IVolume::COUNTRY_JAPAN] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_Japan", size)); + m_FlagImageIndex[DiscIO::IVolume::COUNTRY_EUROPE] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_Europe", size)); + m_FlagImageIndex[DiscIO::IVolume::COUNTRY_USA] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_USA", size)); + m_FlagImageIndex[DiscIO::IVolume::COUNTRY_AUSTRALIA] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_Australia", size)); + m_FlagImageIndex[DiscIO::IVolume::COUNTRY_FRANCE] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_France", size)); + m_FlagImageIndex[DiscIO::IVolume::COUNTRY_GERMANY] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_Germany", size)); + m_FlagImageIndex[DiscIO::IVolume::COUNTRY_ITALY] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_Italy", size)); + m_FlagImageIndex[DiscIO::IVolume::COUNTRY_KOREA] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_Korea", size)); + m_FlagImageIndex[DiscIO::IVolume::COUNTRY_NETHERLANDS] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_Netherlands", size)); + m_FlagImageIndex[DiscIO::IVolume::COUNTRY_RUSSIA] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_Russia", size)); + m_FlagImageIndex[DiscIO::IVolume::COUNTRY_SPAIN] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_Spain", size)); + m_FlagImageIndex[DiscIO::IVolume::COUNTRY_TAIWAN] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_Taiwan", size)); + m_FlagImageIndex[DiscIO::IVolume::COUNTRY_WORLD] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_International", size)); + m_FlagImageIndex[DiscIO::IVolume::COUNTRY_UNKNOWN] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_Unknown", size)); m_PlatformImageIndex.resize(4); - m_PlatformImageIndex[DiscIO::IVolume::GAMECUBE_DISC] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Platform_Gamecube", size)); - m_PlatformImageIndex[DiscIO::IVolume::WII_DISC] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Platform_Wii", size)); - m_PlatformImageIndex[DiscIO::IVolume::WII_WAD] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Platform_Wad", size)); - m_PlatformImageIndex[DiscIO::IVolume::ELF_DOL] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Platform_File", size)); + m_PlatformImageIndex[DiscIO::IVolume::GAMECUBE_DISC] = img_list->Add(WxUtils::LoadResourceBitmap("Platform_Gamecube", size)); + m_PlatformImageIndex[DiscIO::IVolume::WII_DISC] = img_list->Add(WxUtils::LoadResourceBitmap("Platform_Wii", size)); + m_PlatformImageIndex[DiscIO::IVolume::WII_WAD] = img_list->Add(WxUtils::LoadResourceBitmap("Platform_Wad", size)); + m_PlatformImageIndex[DiscIO::IVolume::ELF_DOL] = img_list->Add(WxUtils::LoadResourceBitmap("Platform_File", size)); m_EmuStateImageIndex.resize(6); - m_EmuStateImageIndex[0] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("rating0", size)); - m_EmuStateImageIndex[1] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("rating1", size)); - m_EmuStateImageIndex[2] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("rating2", size)); - m_EmuStateImageIndex[3] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("rating3", size)); - m_EmuStateImageIndex[4] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("rating4", size)); - m_EmuStateImageIndex[5] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("rating5", size)); + m_EmuStateImageIndex[0] = img_list->Add(WxUtils::LoadResourceBitmap("rating0", size)); + m_EmuStateImageIndex[1] = img_list->Add(WxUtils::LoadResourceBitmap("rating1", size)); + m_EmuStateImageIndex[2] = img_list->Add(WxUtils::LoadResourceBitmap("rating2", size)); + m_EmuStateImageIndex[3] = img_list->Add(WxUtils::LoadResourceBitmap("rating3", size)); + m_EmuStateImageIndex[4] = img_list->Add(WxUtils::LoadResourceBitmap("rating4", size)); + m_EmuStateImageIndex[5] = img_list->Add(WxUtils::LoadResourceBitmap("rating5", size)); } void CGameListCtrl::BrowseForDirectory() @@ -247,16 +247,9 @@ void CGameListCtrl::Update() if (Core::GetState() != Core::CORE_UNINITIALIZED) return; - if (m_imageListSmall) - { - delete m_imageListSmall; - m_imageListSmall = nullptr; - } - - Hide(); - ScanForISOs(); + Freeze(); ClearAll(); if (m_ISOFiles.size() != 0) @@ -320,6 +313,12 @@ void CGameListCtrl::Update() } else { + // Remove existing image list and replace it with the smallest possible one. + // The list needs an image list because it reserves screen pixels for the + // image even if we aren't going to use one. It uses the dimensions of the + // last non-null list so assigning nullptr doesn't work. + AssignImageList(new wxImageList(1, 1), wxIMAGE_LIST_SMALL); + wxString errorString; // We just check for one hide setting to be enabled, as we may only // have GC games for example, and hide them, so we should show the @@ -339,7 +338,9 @@ void CGameListCtrl::Update() } if (GetSelectedISO() == nullptr) main_frame->UpdateGUI(); - Show(); + // Thaw before calling AutomaticColumnWidth so that GetClientSize will + // correctly account for the width of scrollbars if they appear. + Thaw(); AutomaticColumnWidth(); ScrollLines(scrollPos); @@ -367,7 +368,7 @@ static wxString NiceSizeFormat(u64 size) // Update the column content of the item at _Index void CGameListCtrl::UpdateItemAtColumn(long _Index, int column) { - GameListItem& rISOFile = *m_ISOFiles[_Index]; + GameListItem& rISOFile = *m_ISOFiles[GetItemData(_Index)]; switch(column) { @@ -382,7 +383,7 @@ void CGameListCtrl::UpdateItemAtColumn(long _Index, int column) int ImageIndex = -1; if (rISOFile.GetBitmap().IsOk()) - ImageIndex = m_imageListSmall->Add(rISOFile.GetBitmap()); + ImageIndex = GetImageList(wxIMAGE_LIST_SMALL)->Add(rISOFile.GetBitmap()); SetItemColumnImage(_Index, COLUMN_BANNER, ImageIndex); break; @@ -426,28 +427,32 @@ void CGameListCtrl::UpdateItemAtColumn(long _Index, int column) } } -void CGameListCtrl::InsertItemInReportView(long _Index) +void CGameListCtrl::InsertItemInReportView(long index) { // When using wxListCtrl, there is no hope of per-column text colors. // But for reference, here are the old colors that were used: (BGR) // title: 0xFF0000 // company: 0x007030 - // Insert a first row with nothing in it, that will be used as the Index - long ItemIndex = InsertItem(_Index, wxEmptyString); + // Insert a first column with nothing in it, that will be used as the Index + long item_index; + { + wxListItem li; + li.SetId(index); + li.SetData(index); + li.SetMask(wxLIST_MASK_DATA); + item_index = InsertItem(li); + } // Iterate over all columns and fill them with content if they are visible for (int i = 1; i < NUMBER_OF_COLUMN; i++) { if (GetColumnWidth(i) != 0) - UpdateItemAtColumn(_Index, i); + UpdateItemAtColumn(item_index, i); } // Background color SetBackgroundColor(); - - // Item data - SetItemData(_Index, ItemIndex); } static wxColour blend50(const wxColour& c1, const wxColour& c2) @@ -653,6 +658,41 @@ void CGameListCtrl::ScanForISOs() std::sort(m_ISOFiles.begin(), m_ISOFiles.end()); } +void CGameListCtrl::OnLocalIniModified(wxCommandEvent& ev) +{ + ev.Skip(); + std::string game_id = WxStrToStr(ev.GetString()); + // NOTE: The same game may occur multiple times if there are multiple + // physical copies in the search paths. + for (std::size_t i = 0; i < m_ISOFiles.size(); ++i) + { + if (m_ISOFiles[i]->GetUniqueID() != game_id) + continue; + m_ISOFiles[i]->ReloadINI(); + + // The indexes in m_ISOFiles and the list do not line up. + // We need to find the corresponding item in the list (if it exists) + long item_id = 0; + for (; item_id < GetItemCount(); ++item_id) + { + if (i == static_cast(GetItemData(item_id))) + break; + } + // If the item is not currently being displayed then we're done. + if (item_id == GetItemCount()) + continue; + + // Update all the columns + for (int j = 1; j < NUMBER_OF_COLUMN; ++j) + { + // NOTE: Banner is not modified by the INI and updating it will + // duplicate it in memory which is not wanted. + if (j != COLUMN_BANNER && GetColumnWidth(j) != 0) + UpdateItemAtColumn(item_id, j); + } + } +} + void CGameListCtrl::OnColBeginDrag(wxListEvent& event) { const int column_id = event.GetColumn(); @@ -1310,19 +1350,19 @@ void CGameListCtrl::OnChangeDisc(wxCommandEvent& WXUNUSED(event)) void CGameListCtrl::OnSize(wxSizeEvent& event) { + event.Skip(); if (lastpos == event.GetSize()) return; lastpos = event.GetSize(); AutomaticColumnWidth(); - - event.Skip(); } void CGameListCtrl::AutomaticColumnWidth() { wxRect rc(GetClientRect()); + Freeze(); if (GetColumnCount() == 1) { SetColumnWidth(0, rc.GetWidth()); @@ -1359,6 +1399,7 @@ void CGameListCtrl::AutomaticColumnWidth() SetColumnWidth(COLUMN_TITLE, resizable); } } + Thaw(); } void CGameListCtrl::UnselectAll() diff --git a/Source/Core/DolphinWX/GameListCtrl.h b/Source/Core/DolphinWX/GameListCtrl.h index 328b69747c..6914d23fb6 100644 --- a/Source/Core/DolphinWX/GameListCtrl.h +++ b/Source/Core/DolphinWX/GameListCtrl.h @@ -102,6 +102,7 @@ private: void OnMultiCompressISO(wxCommandEvent& event); void OnMultiDecompressISO(wxCommandEvent& event); void OnChangeDisc(wxCommandEvent& event); + void OnLocalIniModified(wxCommandEvent& event); void CompressSelection(bool _compress); void AutomaticColumnWidth(); diff --git a/Source/Core/DolphinWX/ISOFile.cpp b/Source/Core/DolphinWX/ISOFile.cpp index 3312394dda..886837af52 100644 --- a/Source/Core/DolphinWX/ISOFile.cpp +++ b/Source/Core/DolphinWX/ISOFile.cpp @@ -124,27 +124,19 @@ GameListItem::GameListItem(const std::string& _rFileName, const std::unordered_m if (IsValid()) { - IniFile ini = SConfig::LoadGameIni(m_UniqueID, m_Revision); - ini.GetIfExists("EmuState", "EmulationStateId", &m_emu_state); - ini.GetIfExists("EmuState", "EmulationIssues", &m_issues); - m_has_custom_name = ini.GetIfExists("EmuState", "Title", &m_custom_name); + std::string game_id = m_UniqueID; - if (!m_has_custom_name) + // Ignore publisher ID for WAD files + if (m_Platform == DiscIO::IVolume::WII_WAD && game_id.size() > 4) + game_id.erase(4); + + auto it = custom_titles.find(game_id); + if (it != custom_titles.end()) { - std::string game_id = m_UniqueID; - - // Ignore publisher ID for WAD files - if (m_Platform == DiscIO::IVolume::WII_WAD && game_id.size() > 4) - game_id.erase(4); - - auto end = custom_titles.end(); - auto it = custom_titles.find(game_id); - if (it != end) - { - m_custom_name = it->second; - m_has_custom_name = true; - } + m_custom_name_titles_txt = it->second; } + + ReloadINI(); } if (!IsValid() && IsElfOrDol()) @@ -183,6 +175,24 @@ GameListItem::~GameListItem() { } +void GameListItem::ReloadINI() +{ + if (!IsValid()) + return; + + IniFile ini = SConfig::LoadGameIni(m_UniqueID, m_Revision); + ini.GetIfExists("EmuState", "EmulationStateId", &m_emu_state, 0); + ini.GetIfExists("EmuState", "EmulationIssues", &m_issues, std::string()); + + m_custom_name.clear(); + m_has_custom_name = ini.GetIfExists("EmuState", "Title", &m_custom_name); + if (!m_has_custom_name && !m_custom_name_titles_txt.empty()) + { + m_custom_name = m_custom_name_titles_txt; + m_has_custom_name = true; + } +} + bool GameListItem::LoadFromCache() { return CChunkFileReader::Load(CreateCacheFilename(), CACHE_REVISION, *this); diff --git a/Source/Core/DolphinWX/ISOFile.h b/Source/Core/DolphinWX/ISOFile.h index 448139fd43..a814820ec0 100644 --- a/Source/Core/DolphinWX/ISOFile.h +++ b/Source/Core/DolphinWX/ISOFile.h @@ -25,6 +25,9 @@ public: GameListItem(const std::string& _rFileName, const std::unordered_map& custom_titles); ~GameListItem(); + // Reload settings after INI changes + void ReloadINI(); + bool IsValid() const {return m_Valid;} const std::string& GetFileName() const {return m_FileName;} std::string GetName(DiscIO::IVolume::ELanguage language) const; @@ -86,7 +89,8 @@ private: int m_ImageWidth, m_ImageHeight; u8 m_disc_number; - std::string m_custom_name; + std::string m_custom_name_titles_txt; // Custom title from titles.txt + std::string m_custom_name; // Custom title from INI or titles.txt bool m_has_custom_name; bool LoadFromCache(); diff --git a/Source/Core/DolphinWX/ISOProperties.cpp b/Source/Core/DolphinWX/ISOProperties.cpp index 9071f70bd3..39b4e2c041 100644 --- a/Source/Core/DolphinWX/ISOProperties.cpp +++ b/Source/Core/DolphinWX/ISOProperties.cpp @@ -63,7 +63,6 @@ #include "DiscIO/Volume.h" #include "DiscIO/VolumeCreator.h" #include "DolphinWX/ARCodeAddEdit.h" -#include "DolphinWX/GameListCtrl.h" #include "DolphinWX/Globals.h" #include "DolphinWX/ISOFile.h" #include "DolphinWX/ISOProperties.h" @@ -77,8 +76,7 @@ BEGIN_EVENT_TABLE(CISOProperties, wxDialog) EVT_BUTTON(ID_EDITCONFIG, CISOProperties::OnEditConfig) EVT_BUTTON(ID_MD5SUMCOMPUTE, CISOProperties::OnComputeMD5Sum) EVT_BUTTON(ID_SHOWDEFAULTCONFIG, CISOProperties::OnShowDefaultConfig) - EVT_CHOICE(ID_EMUSTATE, CISOProperties::SetRefresh) - EVT_CHOICE(ID_EMU_ISSUES, CISOProperties::SetRefresh) + EVT_CHOICE(ID_EMUSTATE, CISOProperties::OnEmustateChanged) EVT_LISTBOX(ID_PATCHES_LIST, CISOProperties::ListSelectionChanged) EVT_BUTTON(ID_EDITPATCH, CISOProperties::PatchButtonClicked) EVT_BUTTON(ID_ADDPATCH, CISOProperties::PatchButtonClicked) @@ -114,8 +112,6 @@ CISOProperties::CISOProperties(const GameListItem& game_list_item, wxWindow* par GameIniLocal = SConfig::LoadLocalGameIni(game_id, m_open_iso->GetRevision()); // Setup GUI - bRefreshList = false; - CreateGUIControls(); LoadGameConfig(); @@ -647,8 +643,6 @@ void CISOProperties::OnClose(wxCloseEvent& WXUNUSED (event)) { if (!SaveGameConfig()) WxUtils::ShowErrorDialog(wxString::Format(_("Could not save %s."), GameIniFileLocal.c_str())); - if (bRefreshList) - ((CGameListCtrl*)GetParent())->Update(); Destroy(); } @@ -991,12 +985,9 @@ void CISOProperties::CheckPartitionIntegrity(wxCommandEvent& event) } } -void CISOProperties::SetRefresh(wxCommandEvent& event) +void CISOProperties::OnEmustateChanged(wxCommandEvent& event) { - bRefreshList = true; - - if (event.GetId() == ID_EMUSTATE) - EmuIssues->Enable(event.GetSelection() != 0); + EmuIssues->Enable(event.GetSelection() != 0); } void CISOProperties::SetCheckboxValueFromGameini(const char* section, const char* key, wxCheckBox* checkbox) @@ -1211,9 +1202,6 @@ void CISOProperties::LaunchExternalEditor(const std::string& filename, bool wait WxUtils::ShowErrorDialog(_("wxExecute returned -1 on application run!")); return; } - - if (wait_until_closed) - bRefreshList = true; // Just in case #endif } diff --git a/Source/Core/DolphinWX/ISOProperties.h b/Source/Core/DolphinWX/ISOProperties.h index 4743d7299b..a702d8e256 100644 --- a/Source/Core/DolphinWX/ISOProperties.h +++ b/Source/Core/DolphinWX/ISOProperties.h @@ -209,7 +209,7 @@ private: void OnExtractDir(wxCommandEvent& event); void OnExtractDataFromHeader(wxCommandEvent& event); void CheckPartitionIntegrity(wxCommandEvent& event); - void SetRefresh(wxCommandEvent& event); + void OnEmustateChanged(wxCommandEvent& event); void OnChangeBannerLang(wxCommandEvent& event); const GameListItem OpenGameListItem; @@ -233,8 +233,6 @@ private: std::set DefaultPatches; std::set DefaultCheats; - bool bRefreshList; - void LoadGameConfig(); bool SaveGameConfig(); void OnLocalIniModified(wxCommandEvent& ev);