From 00a4404045d28beef129d027dc9eaae33ab44703 Mon Sep 17 00:00:00 2001 From: EmptyChaos Date: Sat, 23 Jul 2016 01:05:04 +0000 Subject: [PATCH 1/2] CheatManager/ISOProperties: Refactor ActionReplay tabs as a class Refactor Action Replay code into its own class like Gecko Codes. --- Source/Core/Core/ARDecrypt.cpp | 16 +- Source/Core/Core/ARDecrypt.h | 2 +- Source/Core/Core/ActionReplay.cpp | 4 +- Source/Core/Core/ActionReplay.h | 4 + Source/Core/DolphinWX/ARCodeAddEdit.cpp | 188 ----------- Source/Core/DolphinWX/ARCodeAddEdit.h | 38 --- Source/Core/DolphinWX/CMakeLists.txt | 3 +- .../Core/DolphinWX/Cheats/ARCodeAddEdit.cpp | 193 ++++++++++++ Source/Core/DolphinWX/Cheats/ARCodeAddEdit.h | 29 ++ .../Cheats/ActionReplayCodesPanel.cpp | 291 ++++++++++++++++++ .../DolphinWX/Cheats/ActionReplayCodesPanel.h | 89 ++++++ Source/Core/DolphinWX/Cheats/CheatsWindow.cpp | 120 +++----- Source/Core/DolphinWX/Cheats/CheatsWindow.h | 22 +- Source/Core/DolphinWX/DolphinWX.vcxproj | 8 +- .../Core/DolphinWX/DolphinWX.vcxproj.filters | 22 +- Source/Core/DolphinWX/ISOProperties.cpp | 144 ++------- Source/Core/DolphinWX/ISOProperties.h | 20 +- 17 files changed, 706 insertions(+), 487 deletions(-) delete mode 100644 Source/Core/DolphinWX/ARCodeAddEdit.cpp delete mode 100644 Source/Core/DolphinWX/ARCodeAddEdit.h create mode 100644 Source/Core/DolphinWX/Cheats/ARCodeAddEdit.cpp create mode 100644 Source/Core/DolphinWX/Cheats/ARCodeAddEdit.h create mode 100644 Source/Core/DolphinWX/Cheats/ActionReplayCodesPanel.cpp create mode 100644 Source/Core/DolphinWX/Cheats/ActionReplayCodesPanel.h diff --git a/Source/Core/Core/ARDecrypt.cpp b/Source/Core/Core/ARDecrypt.cpp index d336ddd276..cb6b393423 100644 --- a/Source/Core/Core/ARDecrypt.cpp +++ b/Source/Core/Core/ARDecrypt.cpp @@ -454,7 +454,7 @@ static int alphatobin(u32* dst, const std::vector& alpha, int size) return ret; } -void DecryptARCode(std::vector vCodes, std::vector& ops) +void DecryptARCode(std::vector vCodes, std::vector* ops) { // The almighty buildseeds() function!! without this, the crypto routines are useless buildseeds(); @@ -469,9 +469,9 @@ void DecryptARCode(std::vector vCodes, std::vector& ops) if ((ret = alphatobin(uCodes, vCodes, (int)vCodes.size()))) { + // Return value is index + 1, 0 being the success flag value. PanicAlertT("Action Replay Code Decryption Error:\nParity Check Failed\n\nCulprit Code:\n%s", - vCodes[ret].c_str()); - batchdecrypt(uCodes, (u16)vCodes.size() << 1); + vCodes[ret - 1].c_str()); } else if (!batchdecrypt(uCodes, (u16)vCodes.size() << 1)) { @@ -481,10 +481,7 @@ void DecryptARCode(std::vector vCodes, std::vector& ops) for (size_t i = 0; i < (vCodes.size() << 1); i += 2) { - AREntry op; - op.cmd_addr = uCodes[i]; - op.value = uCodes[i + 1]; - ops.push_back(op); + ops->emplace_back(uCodes[i], uCodes[i + 1]); // PanicAlert("Decrypted AR Code without verification code:\n%08X %08X", uCodes[i], // uCodes[i+1]); } @@ -494,10 +491,7 @@ void DecryptARCode(std::vector vCodes, std::vector& ops) // Skip passing the verification code back for (size_t i = 2; i < (vCodes.size() << 1); i += 2) { - AREntry op; - op.cmd_addr = uCodes[i]; - op.value = uCodes[i + 1]; - ops.push_back(op); + ops->emplace_back(uCodes[i], uCodes[i + 1]); // PanicAlert("Decrypted AR Code:\n%08X %08X", uCodes[i], uCodes[i+1]); } } diff --git a/Source/Core/Core/ARDecrypt.h b/Source/Core/Core/ARDecrypt.h index 897ecb19c8..399b5d0dc0 100644 --- a/Source/Core/Core/ARDecrypt.h +++ b/Source/Core/Core/ARDecrypt.h @@ -11,6 +11,6 @@ namespace ActionReplay { -void DecryptARCode(std::vector vCodes, std::vector& ops); +void DecryptARCode(std::vector vCodes, std::vector* ops); } // namespace diff --git a/Source/Core/Core/ActionReplay.cpp b/Source/Core/Core/ActionReplay.cpp index c0055d771b..0e9b8aff15 100644 --- a/Source/Core/Core/ActionReplay.cpp +++ b/Source/Core/Core/ActionReplay.cpp @@ -184,7 +184,7 @@ std::vector LoadCodes(const IniFile& global_ini, const IniFile& local_in } if (encrypted_lines.size()) { - DecryptARCode(encrypted_lines, current_code.ops); + DecryptARCode(encrypted_lines, ¤t_code.ops); codes.push_back(current_code); current_code.ops.clear(); encrypted_lines.clear(); @@ -242,7 +242,7 @@ std::vector LoadCodes(const IniFile& global_ini, const IniFile& local_in } if (encrypted_lines.size()) { - DecryptARCode(encrypted_lines, current_code.ops); + DecryptARCode(encrypted_lines, ¤t_code.ops); codes.push_back(current_code); } } diff --git a/Source/Core/Core/ActionReplay.h b/Source/Core/Core/ActionReplay.h index d6f617cd12..2a94018ee3 100644 --- a/Source/Core/Core/ActionReplay.h +++ b/Source/Core/Core/ActionReplay.h @@ -19,6 +19,10 @@ struct AREntry u32 cmd_addr; u32 value; }; +constexpr bool operator==(const AREntry& left, const AREntry& right) +{ + return left.cmd_addr == right.cmd_addr && left.value == right.value; +} struct ARCode { diff --git a/Source/Core/DolphinWX/ARCodeAddEdit.cpp b/Source/Core/DolphinWX/ARCodeAddEdit.cpp deleted file mode 100644 index c756b00d02..0000000000 --- a/Source/Core/DolphinWX/ARCodeAddEdit.cpp +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2008 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Common/CommonTypes.h" -#include "Common/Logging/Log.h" -#include "Common/MsgHandler.h" -#include "Common/StringUtil.h" -#include "Core/ARDecrypt.h" -#include "Core/ActionReplay.h" -#include "DolphinWX/ARCodeAddEdit.h" -#include "DolphinWX/WxUtils.h" - -CARCodeAddEdit::CARCodeAddEdit(int _selection, std::vector* _arCodes, - wxWindow* parent, wxWindowID id, const wxString& title, - const wxPoint& position, const wxSize& size, long style) - : wxDialog(parent, id, title, position, size, style), arCodes(_arCodes), selection(_selection) -{ - Bind(wxEVT_BUTTON, &CARCodeAddEdit::SaveCheatData, this, wxID_OK); - - ActionReplay::ARCode tempEntries; - wxString currentName; - - if (selection == wxNOT_FOUND) - { - tempEntries.name = ""; - } - else - { - currentName = StrToWxStr(arCodes->at(selection).name); - tempEntries = arCodes->at(selection); - } - - wxBoxSizer* sEditCheat = new wxBoxSizer(wxVERTICAL); - wxStaticBoxSizer* sbEntry = new wxStaticBoxSizer(wxVERTICAL, this, _("Cheat Code")); - wxGridBagSizer* sgEntry = new wxGridBagSizer(0, 0); - - wxStaticText* EditCheatNameText = new wxStaticText(this, wxID_ANY, _("Name:")); - wxStaticText* EditCheatCodeText = new wxStaticText(this, wxID_ANY, _("Code:")); - - EditCheatName = new wxTextCtrl(this, wxID_ANY, wxEmptyString); - EditCheatName->SetValue(currentName); - - EntrySelection = new wxSpinButton(this); - EntrySelection->SetRange(1, std::max((int)arCodes->size(), 1)); - EntrySelection->SetValue((int)(arCodes->size() - selection)); - EntrySelection->Bind(wxEVT_SPIN, &CARCodeAddEdit::ChangeEntry, this); - - EditCheatCode = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(300, 100), - wxTE_MULTILINE); - - UpdateTextCtrl(tempEntries); - - sgEntry->Add(EditCheatNameText, wxGBPosition(0, 0), wxGBSpan(1, 1), wxALIGN_CENTER | wxALL, 5); - sgEntry->Add(EditCheatCodeText, wxGBPosition(1, 0), wxGBSpan(1, 1), wxALIGN_CENTER | wxALL, 5); - sgEntry->Add(EditCheatName, wxGBPosition(0, 1), wxGBSpan(1, 1), wxEXPAND | wxALL, 5); - sgEntry->Add(EntrySelection, wxGBPosition(0, 2), wxGBSpan(2, 1), wxEXPAND | wxALL, 5); - sgEntry->Add(EditCheatCode, wxGBPosition(1, 1), wxGBSpan(1, 1), wxEXPAND | wxALL, 5); - sgEntry->AddGrowableCol(1); - sgEntry->AddGrowableRow(1); - sbEntry->Add(sgEntry, 1, wxEXPAND | wxALL); - - sEditCheat->Add(sbEntry, 1, wxEXPAND | wxALL, 5); - sEditCheat->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND | wxALL, 5); - - SetSizerAndFit(sEditCheat); - SetFocus(); -} - -void CARCodeAddEdit::ChangeEntry(wxSpinEvent& event) -{ - ActionReplay::ARCode currentCode = arCodes->at((int)arCodes->size() - event.GetPosition()); - EditCheatName->SetValue(StrToWxStr(currentCode.name)); - UpdateTextCtrl(currentCode); -} - -void CARCodeAddEdit::SaveCheatData(wxCommandEvent& WXUNUSED(event)) -{ - std::vector decryptedLines; - std::vector encryptedLines; - - // Split the entered cheat into lines. - std::vector userInputLines; - SplitString(WxStrToStr(EditCheatCode->GetValue()), '\n', userInputLines); - - for (size_t i = 0; i < userInputLines.size(); i++) - { - // Make sure to ignore unneeded whitespace characters. - std::string line_str = StripSpaces(userInputLines[i]); - - if (line_str == "") - continue; - - // Let's parse the current line. Is it in encrypted or decrypted form? - std::vector pieces; - SplitString(line_str, ' ', pieces); - - if (pieces.size() == 2 && pieces[0].size() == 8 && pieces[1].size() == 8) - { - // Decrypted code line. - u32 addr = std::stoul(pieces[0], nullptr, 16); - u32 value = std::stoul(pieces[1], nullptr, 16); - - decryptedLines.emplace_back(addr, value); - continue; - } - else if (pieces.size() == 1) - { - SplitString(line_str, '-', pieces); - - if (pieces.size() == 3 && pieces[0].size() == 4 && pieces[1].size() == 4 && - pieces[2].size() == 5) - { - // Encrypted code line. We'll have to decode it later. - encryptedLines.push_back(pieces[0] + pieces[1] + pieces[2]); - continue; - } - } - - // If the above-mentioned conditions weren't met, then something went wrong. - if (!PanicYesNoT("Unable to parse line %u of the entered AR code as a valid " - "encrypted or decrypted code. Make sure you typed it correctly.\n" - "Would you like to ignore this line and continue parsing?", - (unsigned)(i + 1))) - { - return; - } - } - - // If the entered code was in encrypted form, we decode it here. - if (encryptedLines.size()) - { - // TODO: what if both decrypted AND encrypted lines are entered into a single AR code? - ActionReplay::DecryptARCode(encryptedLines, decryptedLines); - } - - // Codes with no lines appear to be deleted/hidden from the list. Let's prevent that. - if (!decryptedLines.size()) - { - WxUtils::ShowErrorDialog(_("The resulting decrypted AR code doesn't contain any lines.")); - return; - } - - if (selection == wxNOT_FOUND) - { - // Add a new AR cheat code. - ActionReplay::ARCode newCheat; - - newCheat.name = WxStrToStr(EditCheatName->GetValue()); - newCheat.ops = decryptedLines; - newCheat.active = true; - newCheat.user_defined = true; - - arCodes->push_back(newCheat); - } - else - { - // Update the currently-selected AR cheat code. - arCodes->at(selection).name = WxStrToStr(EditCheatName->GetValue()); - arCodes->at(selection).ops = decryptedLines; - } - - AcceptAndClose(); -} - -void CARCodeAddEdit::UpdateTextCtrl(ActionReplay::ARCode arCode) -{ - EditCheatCode->Clear(); - - if (arCode.name != "") - { - for (auto& op : arCode.ops) - EditCheatCode->AppendText(wxString::Format("%08X %08X\n", op.cmd_addr, op.value)); - } -} diff --git a/Source/Core/DolphinWX/ARCodeAddEdit.h b/Source/Core/DolphinWX/ARCodeAddEdit.h deleted file mode 100644 index 5377276f63..0000000000 --- a/Source/Core/DolphinWX/ARCodeAddEdit.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2008 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#pragma once - -#include - -class wxSpinButton; -class wxSpinEvent; -class wxTextCtrl; - -namespace ActionReplay -{ -struct ARCode; -} - -class CARCodeAddEdit : public wxDialog -{ -public: - CARCodeAddEdit(int _selection, std::vector* _arCodes, wxWindow* parent, - wxWindowID id = wxID_ANY, const wxString& title = _("Edit ActionReplay Code"), - const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, - long style = wxDEFAULT_DIALOG_STYLE); - -private: - wxTextCtrl* EditCheatName; - wxSpinButton* EntrySelection; - wxTextCtrl* EditCheatCode; - - std::vector* arCodes; - - void SaveCheatData(wxCommandEvent& event); - void ChangeEntry(wxSpinEvent& event); - void UpdateTextCtrl(ActionReplay::ARCode arCode); - - int selection; -}; diff --git a/Source/Core/DolphinWX/CMakeLists.txt b/Source/Core/DolphinWX/CMakeLists.txt index 9e89e73987..fc6a786a7f 100644 --- a/Source/Core/DolphinWX/CMakeLists.txt +++ b/Source/Core/DolphinWX/CMakeLists.txt @@ -1,7 +1,8 @@ set(GUI_SRCS - ARCodeAddEdit.cpp AboutDolphin.cpp ControllerConfigDiag.cpp + Cheats/ActionReplayCodesPanel.cpp + Cheats/ARCodeAddEdit.cpp Cheats/CheatSearchTab.cpp Cheats/CheatsWindow.cpp Cheats/CreateCodeDialog.cpp diff --git a/Source/Core/DolphinWX/Cheats/ARCodeAddEdit.cpp b/Source/Core/DolphinWX/Cheats/ARCodeAddEdit.cpp new file mode 100644 index 0000000000..3162e52211 --- /dev/null +++ b/Source/Core/DolphinWX/Cheats/ARCodeAddEdit.cpp @@ -0,0 +1,193 @@ +// Copyright 2008 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Common/CommonTypes.h" +#include "Common/StringUtil.h" +#include "Core/ARDecrypt.h" +#include "Core/ActionReplay.h" +#include "DolphinWX/Cheats/ARCodeAddEdit.h" +#include "DolphinWX/WxUtils.h" + +ARCodeAddEdit::ARCodeAddEdit(ActionReplay::ARCode code, wxWindow* parent, wxWindowID id, + const wxString& title, const wxPoint& position, const wxSize& size, + long style) + : wxDialog(parent, id, title, position, size, style), m_code(std::move(code)) +{ + CreateGUI(); +} + +void ARCodeAddEdit::CreateGUI() +{ + const int space10 = FromDIP(10); + const int space5 = FromDIP(5); + + wxBoxSizer* sEditCheat = new wxBoxSizer(wxVERTICAL); + wxStaticBoxSizer* sbEntry = new wxStaticBoxSizer(wxVERTICAL, this, _("Cheat Code")); + wxGridBagSizer* sgEntry = new wxGridBagSizer(space10, space10); + + wxStaticText* lbl_cheat_name = new wxStaticText(sbEntry->GetStaticBox(), wxID_ANY, _("Name:")); + wxStaticText* lbl_cheat_codes = new wxStaticText(sbEntry->GetStaticBox(), wxID_ANY, _("Code:")); + + m_txt_cheat_name = new wxTextCtrl(sbEntry->GetStaticBox(), wxID_ANY, wxEmptyString); + m_txt_cheat_name->SetValue(StrToWxStr(m_code.name)); + + m_cheat_codes = + new wxTextCtrl(sbEntry->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, + wxDLG_UNIT(this, wxSize(240, 128)), wxTE_MULTILINE); + for (const auto& op : m_code.ops) + m_cheat_codes->AppendText(wxString::Format("%08X %08X\n", op.cmd_addr, op.value)); + + { + wxFont font{m_cheat_codes->GetFont()}; + font.SetFamily(wxFONTFAMILY_TELETYPE); +#ifdef _WIN32 + // Windows uses Courier New for monospace even though there are better fonts. + font.SetFaceName("Consolas"); +#endif + m_cheat_codes->SetFont(font); + } + + sgEntry->Add(lbl_cheat_name, wxGBPosition(0, 0), wxGBSpan(1, 1), wxALIGN_CENTER); + sgEntry->Add(lbl_cheat_codes, wxGBPosition(1, 0), wxGBSpan(1, 1), wxALIGN_CENTER); + sgEntry->Add(m_txt_cheat_name, wxGBPosition(0, 1), wxGBSpan(1, 1), wxEXPAND); + sgEntry->Add(m_cheat_codes, wxGBPosition(1, 1), wxGBSpan(1, 1), wxEXPAND); + sgEntry->AddGrowableCol(1); + sgEntry->AddGrowableRow(1); + sbEntry->Add(sgEntry, 1, wxEXPAND | wxALL, space5); + + // OS X UX: ID_NO becomes "Don't Save" when paired with wxID_SAVE + wxStdDialogButtonSizer* buttons = new wxStdDialogButtonSizer(); + buttons->AddButton(new wxButton(this, wxID_SAVE)); + buttons->AddButton(new wxButton(this, wxID_NO, wxGetStockLabel(wxID_CANCEL))); + buttons->Realize(); + + sEditCheat->AddSpacer(space5); + sEditCheat->Add(sbEntry, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + sEditCheat->AddSpacer(space10); + sEditCheat->Add(buttons, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + sEditCheat->AddSpacer(space5); + + Bind(wxEVT_BUTTON, &ARCodeAddEdit::SaveCheatData, this, wxID_SAVE); + + SetEscapeId(wxID_NO); + SetAffirmativeId(wxID_SAVE); + SetSizerAndFit(sEditCheat); +} + +void ARCodeAddEdit::SaveCheatData(wxCommandEvent& WXUNUSED(event)) +{ + std::vector decrypted_lines; + std::vector encrypted_lines; + + // Split the entered cheat into lines. + std::vector input_lines; + SplitString(WxStrToStr(m_cheat_codes->GetValue()), '\n', input_lines); + + for (size_t i = 0; i < input_lines.size(); i++) + { + // Make sure to ignore unneeded whitespace characters. + std::string line_str = StripSpaces(input_lines[i]); + + if (line_str.empty()) + continue; + + // Let's parse the current line. Is it in encrypted or decrypted form? + std::vector pieces; + SplitString(line_str, ' ', pieces); + + if (pieces.size() == 2 && pieces[0].size() == 8 && pieces[1].size() == 8) + { + // Decrypted code line. + u32 addr = std::stoul(pieces[0], nullptr, 16); + u32 value = std::stoul(pieces[1], nullptr, 16); + + decrypted_lines.emplace_back(addr, value); + continue; + } + else if (pieces.size() == 1) + { + SplitString(line_str, '-', pieces); + + if (pieces.size() == 3 && pieces[0].size() == 4 && pieces[1].size() == 4 && + pieces[2].size() == 5) + { + // Encrypted code line. We'll have to decode it later. + encrypted_lines.emplace_back(pieces[0] + pieces[1] + pieces[2]); + continue; + } + } + + // If the above-mentioned conditions weren't met, then something went wrong. + if (wxMessageBox( + wxString::Format(_("Unable to parse line %u of the entered AR code as a valid " + "encrypted or decrypted code. Make sure you typed it correctly.\n\n" + "Would you like to ignore this line and continue parsing?"), + (unsigned)(i + 1)), + _("Parsing Error"), wxYES_NO | wxICON_ERROR, this) == wxNO) + { + return; + } + } + + // If the entered code was in encrypted form, we decode it here. + if (!encrypted_lines.empty()) + { + // If the code contains a mixture of encrypted and unencrypted lines then we can't process it. + int mode = wxYES; + if (!decrypted_lines.empty()) + { + mode = + wxMessageBox(_("This Action Replay code contains both encrypted and unencrypted lines; " + "you should check that you have entered it correctly.\n\n" + "Do you want to discard all unencrypted lines?"), + _("Invalid Mixed Code"), wxYES_NO | wxCANCEL | wxICON_ERROR, this); + // YES = Discard the unencrypted lines then decrypt the encrypted ones instead. + // NO = Discard the encrypted lines, keep the unencrypted ones + // CANCEL = Stop and let the user go back to editing + if (mode == wxCANCEL) + return; + if (mode == wxYES) + decrypted_lines.clear(); + } + + if (mode == wxYES) + ActionReplay::DecryptARCode(encrypted_lines, &decrypted_lines); + } + + // There's no point creating a code with no content. + if (decrypted_lines.empty()) + { + WxUtils::ShowErrorDialog(_("The resulting decrypted AR code doesn't contain any lines.")); + return; + } + + ActionReplay::ARCode new_code; + new_code.name = WxStrToStr(m_txt_cheat_name->GetValue()); + new_code.ops = std::move(decrypted_lines); + new_code.active = true; + new_code.user_defined = true; + + if (new_code.name != m_code.name || new_code.ops != m_code.ops) + { + m_code = std::move(new_code); + AcceptAndClose(); + } + else + { + EndDialog(GetEscapeId()); + } +} diff --git a/Source/Core/DolphinWX/Cheats/ARCodeAddEdit.h b/Source/Core/DolphinWX/Cheats/ARCodeAddEdit.h new file mode 100644 index 0000000000..670da1ae5f --- /dev/null +++ b/Source/Core/DolphinWX/Cheats/ARCodeAddEdit.h @@ -0,0 +1,29 @@ +// Copyright 2008 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "Core/ActionReplay.h" + +class wxTextCtrl; + +class ARCodeAddEdit final : public wxDialog +{ +public: + ARCodeAddEdit(ActionReplay::ARCode code, wxWindow* parent, wxWindowID id = wxID_ANY, + const wxString& title = _("Edit ActionReplay Code"), + const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, + long style = wxDEFAULT_DIALOG_STYLE); + + const ActionReplay::ARCode& GetCode() const { return m_code; } +private: + void CreateGUI(); + void SaveCheatData(wxCommandEvent& event); + + ActionReplay::ARCode m_code; + wxTextCtrl* m_txt_cheat_name; + wxTextCtrl* m_cheat_codes; +}; diff --git a/Source/Core/DolphinWX/Cheats/ActionReplayCodesPanel.cpp b/Source/Core/DolphinWX/Cheats/ActionReplayCodesPanel.cpp new file mode 100644 index 0000000000..7d03372ab1 --- /dev/null +++ b/Source/Core/DolphinWX/Cheats/ActionReplayCodesPanel.cpp @@ -0,0 +1,291 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include +#include + +#include "DolphinWX/Cheats/ARCodeAddEdit.h" +#include "DolphinWX/Cheats/ActionReplayCodesPanel.h" +#include "DolphinWX/WxUtils.h" + +wxDEFINE_EVENT(DOLPHIN_EVT_ARCODE_TOGGLED, wxCommandEvent); + +ActionReplayCodesPanel::ActionReplayCodesPanel(wxWindow* parent, Style styles) : wxPanel(parent) +{ + SetExtraStyle(GetExtraStyle() | wxWS_EX_BLOCK_EVENTS); + CreateGUI(); + SetCodePanelStyle(styles); +} + +ActionReplayCodesPanel::~ActionReplayCodesPanel() +{ +} + +void ActionReplayCodesPanel::LoadCodes(const IniFile& global_ini, const IniFile& local_ini) +{ + m_codes = ActionReplay::LoadCodes(global_ini, local_ini); + m_was_modified = false; + Repopulate(); +} + +void ActionReplayCodesPanel::SaveCodes(IniFile* local_ini) +{ + ActionReplay::SaveCodes(local_ini, m_codes); + m_was_modified = false; +} + +void ActionReplayCodesPanel::AppendNewCode(const ActionReplay::ARCode& code) +{ + m_codes.push_back(code); + int idx = m_checklist_cheats->Append(m_checklist_cheats->EscapeMnemonics(StrToWxStr(code.name))); + if (code.active) + m_checklist_cheats->Check(idx); + m_was_modified = true; + GenerateToggleEvent(code); +} + +void ActionReplayCodesPanel::Clear() +{ + m_was_modified = false; + m_codes.clear(); + m_codes.shrink_to_fit(); + Repopulate(); +} + +void ActionReplayCodesPanel::SetCodePanelStyle(Style styles) +{ + m_styles = styles; + m_side_panel->GetStaticBox()->Show(!!(styles & STYLE_SIDE_PANEL)); + m_modify_buttons->Show(!!(styles & STYLE_MODIFY_BUTTONS)); + UpdateSidePanel(); + UpdateModifyButtons(); + Layout(); +} + +void ActionReplayCodesPanel::CreateGUI() +{ + // STYLE_LIST + m_checklist_cheats = new wxCheckListBox(this, wxID_ANY); + + // STYLE_SIDE_PANEL + m_side_panel = new wxStaticBoxSizer(wxVERTICAL, this, _("Code Info")); + m_label_code_name = new wxStaticText(m_side_panel->GetStaticBox(), wxID_ANY, _("Name: "), + wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE); + m_label_num_codes = + new wxStaticText(m_side_panel->GetStaticBox(), wxID_ANY, _("Number of Codes: ") + '0'); + + m_list_codes = new wxListBox(m_side_panel->GetStaticBox(), wxID_ANY); + { + wxFont monospace{m_list_codes->GetFont()}; + monospace.SetFamily(wxFONTFAMILY_TELETYPE); +#ifdef _WIN32 + monospace.SetFaceName("Consolas"); // Windows always uses Courier New +#endif + m_list_codes->SetFont(monospace); + } + + const int space5 = FromDIP(5); + + m_side_panel->AddSpacer(space5); + m_side_panel->Add(m_label_code_name, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + m_side_panel->AddSpacer(space5); + m_side_panel->Add(m_label_num_codes, 0, wxEXPAND | wxLEFT | wxRIGHT, space5); + m_side_panel->AddSpacer(space5); + m_side_panel->Add(m_list_codes, 1, wxEXPAND | wxLEFT | wxRIGHT, space5); + m_side_panel->AddSpacer(space5); + m_side_panel->SetMinSize(FromDIP(wxSize(180, -1))); + + // STYLE_MODIFY_BUTTONS + m_modify_buttons = new wxPanel(this); + wxButton* btn_add_code = new wxButton(m_modify_buttons, wxID_ANY, _("&Add New Code...")); + m_btn_edit_code = new wxButton(m_modify_buttons, wxID_ANY, _("&Edit Code...")); + m_btn_remove_code = new wxButton(m_modify_buttons, wxID_ANY, _("&Remove Code")); + + wxBoxSizer* button_layout = new wxBoxSizer(wxHORIZONTAL); + button_layout->Add(btn_add_code); + button_layout->AddStretchSpacer(); + button_layout->Add(m_btn_edit_code); + button_layout->Add(m_btn_remove_code); + m_modify_buttons->SetSizer(button_layout); + + // Top level layouts + wxBoxSizer* panel_layout = new wxBoxSizer(wxHORIZONTAL); + panel_layout->Add(m_checklist_cheats, 1, wxEXPAND); + panel_layout->Add(m_side_panel, 0, wxEXPAND | wxLEFT, space5); + + wxBoxSizer* main_layout = new wxBoxSizer(wxVERTICAL); + main_layout->Add(panel_layout, 1, wxEXPAND); + main_layout->Add(m_modify_buttons, 0, wxEXPAND | wxTOP, space5); + + m_checklist_cheats->Bind(wxEVT_LISTBOX, &ActionReplayCodesPanel::OnCodeSelectionChanged, this); + m_checklist_cheats->Bind(wxEVT_LISTBOX_DCLICK, &ActionReplayCodesPanel::OnCodeDoubleClick, this); + m_checklist_cheats->Bind(wxEVT_CHECKLISTBOX, &ActionReplayCodesPanel::OnCodeChecked, this); + btn_add_code->Bind(wxEVT_BUTTON, &ActionReplayCodesPanel::OnAddNewCodeClick, this); + m_btn_edit_code->Bind(wxEVT_BUTTON, &ActionReplayCodesPanel::OnEditCodeClick, this); + m_btn_remove_code->Bind(wxEVT_BUTTON, &ActionReplayCodesPanel::OnRemoveCodeClick, this); + + SetSizer(main_layout); +} + +void ActionReplayCodesPanel::Repopulate() +{ + // If the code editor is open then it's invalidated now. + // (whatever it was doing is no longer relevant) + if (m_editor) + m_editor->EndModal(wxID_NO); + + m_checklist_cheats->Freeze(); + m_checklist_cheats->Clear(); + + for (const auto& code : m_codes) + { + int idx = + m_checklist_cheats->Append(m_checklist_cheats->EscapeMnemonics(StrToWxStr(code.name))); + if (code.active) + m_checklist_cheats->Check(idx); + } + m_checklist_cheats->Thaw(); + + // Clear side panel contents since selection is invalidated + UpdateSidePanel(); + UpdateModifyButtons(); +} + +void ActionReplayCodesPanel::UpdateSidePanel() +{ + if (!(m_styles & STYLE_SIDE_PANEL)) + return; + + wxString name; + std::size_t code_count = 0; + if (m_checklist_cheats->GetSelection() != wxNOT_FOUND) + { + auto& code = m_codes.at(m_checklist_cheats->GetSelection()); + name = StrToWxStr(code.name); + code_count = code.ops.size(); + + m_list_codes->Freeze(); + m_list_codes->Clear(); + for (const auto& entry : code.ops) + { + m_list_codes->Append(wxString::Format("%08X %08X", entry.cmd_addr, entry.value)); + } + m_list_codes->Thaw(); + } + else + { + m_list_codes->Clear(); + } + + m_label_code_name->SetLabelText(_("Name: ") + 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: "), code_count)); + Layout(); +} + +void ActionReplayCodesPanel::UpdateModifyButtons() +{ + if (!(m_styles & STYLE_MODIFY_BUTTONS)) + return; + + bool is_user_defined = true; + bool enable_buttons = false; + if (m_checklist_cheats->GetSelection() != wxNOT_FOUND) + { + is_user_defined = m_codes.at(m_checklist_cheats->GetSelection()).user_defined; + enable_buttons = true; + } + + m_btn_edit_code->SetLabel(is_user_defined ? _("&Edit Code...") : _("Clone and &Edit Code...")); + m_btn_edit_code->Enable(enable_buttons); + m_btn_remove_code->Enable(enable_buttons && is_user_defined); + Layout(); +} + +void ActionReplayCodesPanel::GenerateToggleEvent(const ActionReplay::ARCode& code) +{ + wxCommandEvent toggle_event{DOLPHIN_EVT_ARCODE_TOGGLED, GetId()}; + toggle_event.SetClientData(const_cast(&code)); + if (!GetEventHandler()->ProcessEvent(toggle_event)) + { + // Because wxWS_EX_BLOCK_EVENTS affects all events, propagation needs to be done manually. + GetParent()->GetEventHandler()->ProcessEvent(toggle_event); + } +} + +void ActionReplayCodesPanel::OnCodeChecked(wxCommandEvent& ev) +{ + auto& code = m_codes.at(ev.GetSelection()); + code.active = m_checklist_cheats->IsChecked(ev.GetSelection()); + m_was_modified = true; + GenerateToggleEvent(code); +} + +void ActionReplayCodesPanel::OnCodeSelectionChanged(wxCommandEvent&) +{ + UpdateSidePanel(); + UpdateModifyButtons(); +} + +void ActionReplayCodesPanel::OnCodeDoubleClick(wxCommandEvent& ev) +{ + if (!(m_styles & STYLE_MODIFY_BUTTONS)) + return; + + OnEditCodeClick(ev); +} + +void ActionReplayCodesPanel::OnAddNewCodeClick(wxCommandEvent&) +{ + ARCodeAddEdit editor{{}, this, wxID_ANY, _("Add ActionReplay Code")}; + m_editor = &editor; + if (editor.ShowModal() == wxID_SAVE) + AppendNewCode(editor.GetCode()); + m_editor = nullptr; +} + +void ActionReplayCodesPanel::OnEditCodeClick(wxCommandEvent&) +{ + int idx = m_checklist_cheats->GetSelection(); + wxASSERT(idx != wxNOT_FOUND); + auto& code = m_codes.at(idx); + // If the code is from the global INI then we'll have to clone it. + if (!code.user_defined) + { + ARCodeAddEdit editor{code, this, wxID_ANY, _("Duplicate Bundled ActionReplay Code")}; + m_editor = &editor; + if (editor.ShowModal() == wxID_SAVE) + AppendNewCode(editor.GetCode()); + m_editor = nullptr; + return; + } + + ARCodeAddEdit editor{code, this}; + m_editor = &editor; + if (editor.ShowModal() == wxID_SAVE) + { + code = editor.GetCode(); + m_checklist_cheats->SetString(idx, m_checklist_cheats->EscapeMnemonics(StrToWxStr(code.name))); + m_checklist_cheats->Check(idx, code.active); + m_was_modified = true; + UpdateSidePanel(); + GenerateToggleEvent(code); + } + m_editor = nullptr; +} + +void ActionReplayCodesPanel::OnRemoveCodeClick(wxCommandEvent&) +{ + int idx = m_checklist_cheats->GetSelection(); + wxASSERT(idx != wxNOT_FOUND); + m_codes.erase(m_codes.begin() + idx); + m_checklist_cheats->Delete(idx); + m_was_modified = true; +} diff --git a/Source/Core/DolphinWX/Cheats/ActionReplayCodesPanel.h b/Source/Core/DolphinWX/Cheats/ActionReplayCodesPanel.h new file mode 100644 index 0000000000..bdb5370142 --- /dev/null +++ b/Source/Core/DolphinWX/Cheats/ActionReplayCodesPanel.h @@ -0,0 +1,89 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include + +#include "Core/ActionReplay.h" + +class wxButton; +class wxCheckListBox; +class wxListBox; +class wxStaticBoxSizer; +class wxStaticText; + +class ARCodeAddEdit; +class IniFile; + +// GetClientData() -> ActionReplay::ARCode* [immutable] +wxDECLARE_EVENT(DOLPHIN_EVT_ARCODE_TOGGLED, wxCommandEvent); + +class ActionReplayCodesPanel final : public wxPanel +{ +public: + enum Style + { + STYLE_LIST = 0, // Show checklist + STYLE_SIDE_PANEL = 1, // Show side panel displaying code content + STYLE_MODIFY_BUTTONS = 2 // Show buttons for adding/editing/removing codes + }; + + explicit ActionReplayCodesPanel(wxWindow* parent, Style styles = STYLE_LIST); + ~ActionReplayCodesPanel() override; + + void LoadCodes(const IniFile& global_ini, const IniFile& local_ini); + const std::vector& GetCodes() { return m_codes; } + void SaveCodes(IniFile* local_ini); + void AppendNewCode(const ActionReplay::ARCode& code); + void Clear(); + + bool IsModified() const { return m_was_modified; } + void ClearModified() { m_was_modified = false; } + Style GetCodePanelStyle() const { return m_styles; } + void SetCodePanelStyle(Style style); + +private: + void CreateGUI(); + void Repopulate(); + void UpdateSidePanel(); + void UpdateModifyButtons(); + void GenerateToggleEvent(const ActionReplay::ARCode& code); + void OnCodeSelectionChanged(wxCommandEvent&); + void OnCodeChecked(wxCommandEvent&); + void OnCodeDoubleClick(wxCommandEvent&); + void OnAddNewCodeClick(wxCommandEvent&); + void OnEditCodeClick(wxCommandEvent&); + void OnRemoveCodeClick(wxCommandEvent&); + + std::vector m_codes; + + wxStaticText* m_label_code_name = nullptr; + wxStaticText* m_label_num_codes = nullptr; + wxCheckListBox* m_checklist_cheats = nullptr; + wxListBox* m_list_codes = nullptr; + wxPanel* m_modify_buttons = nullptr; + wxButton* m_btn_edit_code = nullptr; + wxButton* m_btn_remove_code = nullptr; + wxStaticBoxSizer* m_side_panel = nullptr; + + ARCodeAddEdit* m_editor = nullptr; + + Style m_styles = STYLE_LIST; + bool m_was_modified = false; +}; + +constexpr ActionReplayCodesPanel::Style operator|(ActionReplayCodesPanel::Style a, + ActionReplayCodesPanel::Style b) +{ + return static_cast(static_cast(a) | b); +} + +constexpr ActionReplayCodesPanel::Style operator&(ActionReplayCodesPanel::Style a, + ActionReplayCodesPanel::Style b) +{ + return static_cast(static_cast(a) & b); +} diff --git a/Source/Core/DolphinWX/Cheats/CheatsWindow.cpp b/Source/Core/DolphinWX/Cheats/CheatsWindow.cpp index ef96aaa601..a6c6bad730 100644 --- a/Source/Core/DolphinWX/Cheats/CheatsWindow.cpp +++ b/Source/Core/DolphinWX/Cheats/CheatsWindow.cpp @@ -32,6 +32,7 @@ #include "Core/Core.h" #include "Core/GeckoCode.h" #include "Core/GeckoCodeConfig.h" +#include "DolphinWX/Cheats/ActionReplayCodesPanel.h" #include "DolphinWX/Cheats/CheatSearchTab.h" #include "DolphinWX/Cheats/CheatsWindow.h" #include "DolphinWX/Cheats/CreateCodeDialog.h" @@ -58,7 +59,7 @@ wxCheatsWindow::wxCheatsWindow(wxWindow* const parent) // load codes UpdateGUI(); - wxTheApp->Bind(DOLPHIN_EVT_LOCAL_INI_CHANGED, &wxCheatsWindow::OnEvent_CheatsList_Update, this); + wxTheApp->Bind(DOLPHIN_EVT_LOCAL_INI_CHANGED, &wxCheatsWindow::OnLocalGameIniModified, this); SetSize(wxSize(-1, 600)); Center(); @@ -77,31 +78,16 @@ void wxCheatsWindow::Init_ChildControls() // --- Tabs --- // Cheats List Tab - m_tab_cheats = new wxPanel(m_notebook_main, wxID_ANY); + wxPanel* tab_cheats = new wxPanel(m_notebook_main, wxID_ANY); - 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_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, wxEXPAND | wxALL, 5); - sGroupBoxInfo->Add(m_label_num_codes, 0, wxALL, 5); - sGroupBoxInfo->Add(m_listbox_codes_list, 1, wxALL, 5); + m_ar_codes_panel = + new ActionReplayCodesPanel(tab_cheats, ActionReplayCodesPanel::STYLE_SIDE_PANEL | + ActionReplayCodesPanel::STYLE_MODIFY_BUTTONS); wxBoxSizer* sizer_tab_cheats = new wxBoxSizer(wxHORIZONTAL); - sizer_tab_cheats->Add(m_checklistbox_cheats_list, 1, wxEXPAND | wxTOP | wxBOTTOM | wxLEFT, 10); - sizer_tab_cheats->Add(sGroupBoxInfo, 0, wxALIGN_LEFT | wxEXPAND | wxALL, 5); + sizer_tab_cheats->Add(m_ar_codes_panel, 1, wxEXPAND | wxALL, 5); - m_tab_cheats->SetSizerAndFit(sizer_tab_cheats); + tab_cheats->SetSizerAndFit(sizer_tab_cheats); // Cheat Search Tab wxPanel* const tab_cheat_search = new CheatSearchTab(m_notebook_main); @@ -134,7 +120,7 @@ void wxCheatsWindow::Init_ChildControls() m_tab_log->SetSizerAndFit(sTabLog); // Add Tabs to Notebook - m_notebook_main->AddPage(m_tab_cheats, _("AR Codes")); + m_notebook_main->AddPage(tab_cheats, _("AR Codes")); m_geckocode_panel = new Gecko::CodeConfigPanel(m_notebook_main); m_notebook_main->AddPage(m_geckocode_panel, _("Gecko Codes")); m_notebook_main->AddPage(tab_cheat_search, _("Cheat Search")); @@ -193,21 +179,17 @@ void wxCheatsWindow::UpdateGUI() void wxCheatsWindow::Load_ARCodes() { - m_checklistbox_cheats_list->Clear(); - if (!Core::IsRunning()) - return; - - m_checklistbox_cheats_list->Freeze(); - for (auto& code : ActionReplay::LoadCodes(m_gameini_default, m_gameini_local)) { - 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_ar_codes_panel->Clear(); + m_ar_codes_panel->Disable(); + return; } - m_checklistbox_cheats_list->Thaw(); + else if (!m_ar_codes_panel->IsEnabled()) + { + m_ar_codes_panel->Enable(); + } + m_ar_codes_panel->LoadCodes(m_gameini_default, m_gameini_local); } void wxCheatsWindow::Load_GeckoCodes() @@ -220,36 +202,10 @@ void wxCheatsWindow::OnNewARCodeCreated(wxCommandEvent& ev) { auto code = static_cast(ev.GetClientData()); ActionReplay::AddCode(*code); - - 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); + m_ar_codes_panel->AppendNewCode(*code); } -void wxCheatsWindow::OnEvent_CheatsList_ItemSelected(wxCommandEvent& event) -{ - 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) - { - 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(wxCommandEvent& ev) +void wxCheatsWindow::OnLocalGameIniModified(wxCommandEvent& ev) { ev.Skip(); if (WxStrToStr(ev.GetString()) != m_game_id) @@ -264,18 +220,8 @@ void wxCheatsWindow::OnEvent_CheatsList_Update(wxCommandEvent& ev) 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); + ActionReplay::ApplyCodes(m_ar_codes_panel->GetCodes()); // Apply Gecko Code changes Gecko::SetActiveCodes(m_geckocode_panel->GetCodes()); @@ -283,7 +229,7 @@ void wxCheatsWindow::OnEvent_ApplyChanges_Press(wxCommandEvent& ev) // Save gameini, with changed codes if (m_gameini_local_path.size()) { - ActionReplay::SaveCodes(&m_gameini_local, code_vec); + m_ar_codes_panel->SaveCodes(&m_gameini_local); Gecko::SaveCodes(m_gameini_local, m_geckocode_panel->GetCodes()); m_gameini_local.Save(m_gameini_local_path); @@ -302,9 +248,31 @@ void wxCheatsWindow::OnEvent_ButtonUpdateLog_Press(wxCommandEvent& WXUNUSED(even wxBeginBusyCursor(); m_textctrl_log->Freeze(); m_textctrl_log->Clear(); + // This horrible mess is because the Windows Textbox Widget suffers from + // a Shlemiel The Painter problem where it keeps allocating new memory each + // time some text is appended then memcpys to the new buffer. This happens + // for every single line resulting in the operation taking minutes instead of + // seconds. + // Why not just append all of the text all at once? Microsoft decided that it + // would be clever to accept as much text as will fit in the internal buffer + // then silently discard the rest. We have to iteratively append the text over + // and over until the internal buffer becomes big enough to hold all of it. + // (wxWidgets should have hidden this platform detail but it sucks) + wxString super_string; + super_string.reserve(1024 * 1024); for (const std::string& text : ActionReplay::GetSelfLog()) { - m_textctrl_log->AppendText(StrToWxStr(text)); + super_string.append(StrToWxStr(text)); + } + while (!super_string.empty()) + { + // Read "GetLastPosition" as "Size", there's no size function. + wxTextPos start = m_textctrl_log->GetLastPosition(); + m_textctrl_log->AppendText(super_string); + wxTextPos end = m_textctrl_log->GetLastPosition(); + if (start == end) + break; + super_string.erase(0, end - start); } m_textctrl_log->Thaw(); wxEndBusyCursor(); diff --git a/Source/Core/DolphinWX/Cheats/CheatsWindow.h b/Source/Core/DolphinWX/Cheats/CheatsWindow.h index 252b0c890b..4302cb8af0 100644 --- a/Source/Core/DolphinWX/Cheats/CheatsWindow.h +++ b/Source/Core/DolphinWX/Cheats/CheatsWindow.h @@ -16,18 +16,14 @@ class wxButton; class wxCheckBox; -class wxCheckListBox; -class wxCloseEvent; -class wxListBox; class wxNotebook; -class wxStaticBox; -class wxStaticText; class wxTextCtrl; namespace Gecko { class CodeConfigPanel; } +class ActionReplayCodesPanel; wxDECLARE_EVENT(DOLPHIN_EVT_ADD_NEW_ACTION_REPLAY_CODE, wxCommandEvent); @@ -45,22 +41,13 @@ private: wxButton* m_button_apply; wxNotebook* m_notebook_main; - wxPanel* m_tab_cheats; wxPanel* m_tab_log; wxCheckBox* m_checkbox_log_ar; - wxStaticText* m_label_code_name; - wxStaticText* m_label_num_codes; - - wxCheckListBox* m_checklistbox_cheats_list; - wxTextCtrl* m_textctrl_log; - wxListBox* m_listbox_codes_list; - - wxStaticBox* m_groupbox_info; - + ActionReplayCodesPanel* m_ar_codes_panel; Gecko::CodeConfigPanel* m_geckocode_panel; IniFile m_gameini_default; IniFile m_gameini_local; @@ -83,9 +70,8 @@ private: void OnEvent_ButtonClose_Press(wxCommandEvent& event); void OnEvent_Close(wxCloseEvent& ev); - // Cheats List - void OnEvent_CheatsList_ItemSelected(wxCommandEvent& event); - void OnEvent_CheatsList_Update(wxCommandEvent& event); + // Changes to the INI (affects cheat listings) + void OnLocalGameIniModified(wxCommandEvent& event); // Apply Changes Button void OnEvent_ApplyChanges_Press(wxCommandEvent& event); diff --git a/Source/Core/DolphinWX/DolphinWX.vcxproj b/Source/Core/DolphinWX/DolphinWX.vcxproj index 56b5ed630a..0738037a43 100644 --- a/Source/Core/DolphinWX/DolphinWX.vcxproj +++ b/Source/Core/DolphinWX/DolphinWX.vcxproj @@ -56,7 +56,8 @@ - + + @@ -119,6 +120,8 @@ + + @@ -133,7 +136,6 @@ - @@ -265,4 +267,4 @@ - + \ No newline at end of file diff --git a/Source/Core/DolphinWX/DolphinWX.vcxproj.filters b/Source/Core/DolphinWX/DolphinWX.vcxproj.filters index 0480ca0de8..b060d540c0 100644 --- a/Source/Core/DolphinWX/DolphinWX.vcxproj.filters +++ b/Source/Core/DolphinWX/DolphinWX.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -121,9 +121,6 @@ GUI - - GUI - GUI @@ -202,6 +199,12 @@ GUI\NetPlay + + GUI\Cheats + + + GUI\Cheats + @@ -291,9 +294,6 @@ GUI - - GUI - GUI @@ -369,6 +369,12 @@ GUI\NetPlay + + GUI\Cheats + + + GUI\Cheats + @@ -381,4 +387,4 @@ - + \ No newline at end of file diff --git a/Source/Core/DolphinWX/ISOProperties.cpp b/Source/Core/DolphinWX/ISOProperties.cpp index 74352fd5ca..e8b30dddfe 100644 --- a/Source/Core/DolphinWX/ISOProperties.cpp +++ b/Source/Core/DolphinWX/ISOProperties.cpp @@ -54,7 +54,6 @@ #include "Common/MD5.h" #include "Common/StringUtil.h" #include "Common/SysConf.h" -#include "Core/ActionReplay.h" #include "Core/Boot/Boot.h" #include "Core/ConfigManager.h" #include "Core/GeckoCodeConfig.h" @@ -64,7 +63,7 @@ #include "DiscIO/Filesystem.h" #include "DiscIO/Volume.h" #include "DiscIO/VolumeCreator.h" -#include "DolphinWX/ARCodeAddEdit.h" +#include "DolphinWX/Cheats/ActionReplayCodesPanel.h" #include "DolphinWX/Cheats/GeckoCodeDiag.h" #include "DolphinWX/Globals.h" #include "DolphinWX/ISOFile.h" @@ -79,14 +78,10 @@ EVT_BUTTON(ID_EDITCONFIG, CISOProperties::OnEditConfig) EVT_BUTTON(ID_MD5SUMCOMPUTE, CISOProperties::OnComputeMD5Sum) EVT_BUTTON(ID_SHOWDEFAULTCONFIG, CISOProperties::OnShowDefaultConfig) EVT_CHOICE(ID_EMUSTATE, CISOProperties::OnEmustateChanged) -EVT_LISTBOX(ID_PATCHES_LIST, CISOProperties::ListSelectionChanged) +EVT_LISTBOX(ID_PATCHES_LIST, CISOProperties::PatchListSelectionChanged) EVT_BUTTON(ID_EDITPATCH, CISOProperties::PatchButtonClicked) EVT_BUTTON(ID_ADDPATCH, CISOProperties::PatchButtonClicked) EVT_BUTTON(ID_REMOVEPATCH, CISOProperties::PatchButtonClicked) -EVT_LISTBOX(ID_CHEATS_LIST, CISOProperties::ListSelectionChanged) -EVT_BUTTON(ID_EDITCHEAT, CISOProperties::ActionReplayButtonClicked) -EVT_BUTTON(ID_ADDCHEAT, CISOProperties::ActionReplayButtonClicked) -EVT_BUTTON(ID_REMOVECHEAT, CISOProperties::ActionReplayButtonClicked) EVT_MENU(IDM_BNRSAVEAS, CISOProperties::OnBannerImageSave) EVT_TREE_ITEM_RIGHT_CLICK(ID_TREECTRL, CISOProperties::OnRightClickOnTree) EVT_MENU(IDM_EXTRACTFILE, CISOProperties::OnExtractFile) @@ -96,7 +91,6 @@ EVT_MENU(IDM_EXTRACTAPPLOADER, CISOProperties::OnExtractDataFromHeader) 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, @@ -476,24 +470,12 @@ void CISOProperties::CreateGUIControls() m_PatchPage->SetSizer(sPatchPage); // Action Replay Cheats - wxBoxSizer* const sCheats = new wxBoxSizer(wxVERTICAL); - Cheats = new wxCheckListBox(m_CheatPage, ID_CHEATS_LIST, wxDefaultPosition, wxDefaultSize, 0, - nullptr, wxLB_HSCROLL); - wxBoxSizer* const sCheatButtons = new wxBoxSizer(wxHORIZONTAL); - EditCheat = new wxButton(m_CheatPage, ID_EDITCHEAT, _("Edit...")); - wxButton* const AddCheat = new wxButton(m_CheatPage, ID_ADDCHEAT, _("Add...")); - RemoveCheat = new wxButton(m_CheatPage, ID_REMOVECHEAT, _("Remove")); - EditCheat->Disable(); - RemoveCheat->Disable(); + m_ar_code_panel = + new ActionReplayCodesPanel(m_CheatPage, ActionReplayCodesPanel::STYLE_MODIFY_BUTTONS); - wxBoxSizer* sCheatPage = new wxBoxSizer(wxVERTICAL); - sCheats->Add(Cheats, 1, wxEXPAND | wxALL, 0); - sCheatButtons->Add(EditCheat, 0, wxEXPAND | wxALL, 0); - sCheatButtons->AddStretchSpacer(); - sCheatButtons->Add(AddCheat, 0, wxEXPAND | wxALL, 0); - sCheatButtons->Add(RemoveCheat, 0, wxEXPAND | wxALL, 0); - sCheats->Add(sCheatButtons, 0, wxEXPAND | wxALL, 0); - sCheatPage->Add(sCheats, 1, wxEXPAND | wxALL, 5); + wxBoxSizer* const sCheatPage = new wxBoxSizer(wxVERTICAL); + // TODO: Cheat disabled warning. + sCheatPage->Add(m_ar_code_panel, 1, wxEXPAND | wxALL, 5); m_CheatPage->SetSizer(sCheatPage); wxStaticText* const m_InternalNameText = @@ -1131,7 +1113,7 @@ void CISOProperties::LoadGameConfig() Convergence->SetValue(iTemp); PatchList_Load(); - ActionReplayList_Load(); + m_ar_code_panel->LoadCodes(GameIniDefault, GameIniLocal); m_geckocode_panel->LoadCodes(GameIniDefault, GameIniLocal, m_open_iso->GetUniqueID()); } @@ -1217,7 +1199,7 @@ bool CISOProperties::SaveGameConfig() SAVE_IF_NOT_DEFAULT("Video_Stereoscopy", "StereoConvergence", Convergence->GetValue(), 0); PatchList_Save(); - ActionReplayList_Save(); + m_ar_code_panel->SaveCodes(&GameIniLocal); Gecko::SaveCodes(GameIniLocal, m_geckocode_panel->GetCodes()); bool success = GameIniLocal.Save(GameIniFileLocal); @@ -1327,45 +1309,20 @@ void CISOProperties::OnShowDefaultConfig(wxCommandEvent& WXUNUSED(event)) } } -void CISOProperties::ListSelectionChanged(wxCommandEvent& event) +void CISOProperties::PatchListSelectionChanged(wxCommandEvent& event) { - switch (event.GetId()) + if (Patches->GetSelection() == wxNOT_FOUND || + DefaultPatches.find(Patches->GetString(Patches->GetSelection()).ToStdString()) != + DefaultPatches.end()) { - case ID_PATCHES_LIST: - if (Patches->GetSelection() == wxNOT_FOUND || - DefaultPatches.find(Patches->GetString(Patches->GetSelection()).ToStdString()) != - DefaultPatches.end()) - { - EditPatch->Disable(); - RemovePatch->Disable(); - } - else - { - EditPatch->Enable(); - RemovePatch->Enable(); - } - break; - case ID_CHEATS_LIST: - if (Cheats->GetSelection() == wxNOT_FOUND || - DefaultCheats.find( - Cheats->RemoveMnemonics(Cheats->GetString(Cheats->GetSelection())).ToStdString()) != - DefaultCheats.end()) - { - EditCheat->Disable(); - RemoveCheat->Disable(); - } - else - { - EditCheat->Enable(); - RemoveCheat->Enable(); - } - break; + EditPatch->Disable(); + RemovePatch->Disable(); + } + else + { + EditPatch->Enable(); + RemovePatch->Enable(); } -} - -void CISOProperties::OnActionReplayCodeChecked(wxCommandEvent& event) -{ - arCodes[event.GetSelection()].active = Cheats->IsChecked(event.GetSelection()); } void CISOProperties::PatchList_Load() @@ -1452,67 +1409,6 @@ void CISOProperties::PatchButtonClicked(wxCommandEvent& event) RemovePatch->Disable(); } -void CISOProperties::ActionReplayList_Load() -{ - arCodes = ActionReplay::LoadCodes(GameIniDefault, GameIniLocal); - DefaultCheats.clear(); - - Cheats->Freeze(); - Cheats->Clear(); - for (const ActionReplay::ARCode& arCode : arCodes) - { - int idx = Cheats->Append(Cheats->EscapeMnemonics(StrToWxStr(arCode.name))); - Cheats->Check(idx, arCode.active); - if (!arCode.user_defined) - DefaultCheats.insert(arCode.name); - } - Cheats->Thaw(); -} - -void CISOProperties::ActionReplayList_Save() -{ - ActionReplay::SaveCodes(&GameIniLocal, arCodes); -} - -void CISOProperties::ActionReplayButtonClicked(wxCommandEvent& event) -{ - int selection = Cheats->GetSelection(); - - switch (event.GetId()) - { - case ID_EDITCHEAT: - { - CARCodeAddEdit dlg(selection, &arCodes, this); - dlg.ShowModal(); - Raise(); - } - break; - case ID_ADDCHEAT: - { - CARCodeAddEdit dlg(-1, &arCodes, this, 1, _("Add ActionReplay Code")); - int res = dlg.ShowModal(); - Raise(); - if (res == wxID_OK) - { - Cheats->Append(StrToWxStr(arCodes.back().name)); - Cheats->Check((unsigned int)(arCodes.size() - 1), arCodes.back().active); - } - } - break; - case ID_REMOVECHEAT: - arCodes.erase(arCodes.begin() + Cheats->GetSelection()); - Cheats->Delete(Cheats->GetSelection()); - break; - } - - ActionReplayList_Save(); - Cheats->Clear(); - ActionReplayList_Load(); - - EditCheat->Disable(); - RemoveCheat->Disable(); -} - 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 d3671dc48a..125ff29967 100644 --- a/Source/Core/DolphinWX/ISOProperties.h +++ b/Source/Core/DolphinWX/ISOProperties.h @@ -14,10 +14,8 @@ #include #include "Common/IniFile.h" -#include "Core/ActionReplay.h" #include "DiscIO/Filesystem.h" #include "DiscIO/Volume.h" -#include "DolphinWX/ARCodeAddEdit.h" #include "DolphinWX/ISOFile.h" #include "DolphinWX/PatchAddEdit.h" @@ -40,6 +38,7 @@ namespace Gecko { class CodeConfigPanel; } +class ActionReplayCodesPanel; class WiiPartition final : public wxTreeItemData { @@ -78,7 +77,6 @@ private: std::unique_ptr m_filesystem; std::vector onFrame; - std::vector arCodes; PHackData m_PHack_Data; // Core @@ -103,10 +101,6 @@ private: wxButton* EditPatch; wxButton* RemovePatch; - wxCheckListBox* Cheats; - wxButton* EditCheat; - wxButton* RemoveCheat; - wxTextCtrl* m_InternalName; wxTextCtrl* m_GameID; wxTextCtrl* m_Country; @@ -126,6 +120,7 @@ private: wxTreeCtrl* m_Treectrl; wxTreeItemId RootId; + ActionReplayCodesPanel* m_ar_code_panel; Gecko::CodeConfigPanel* m_geckocode_panel; enum @@ -159,10 +154,6 @@ private: ID_EDITPATCH, ID_ADDPATCH, ID_REMOVEPATCH, - ID_CHEATS_LIST, - ID_EDITCHEAT, - ID_ADDCHEAT, - ID_REMOVECHEAT, ID_GPUDETERMINISM, ID_DEPTHPERCENTAGE, ID_CONVERGENCE, @@ -201,10 +192,8 @@ private: void OnEditConfig(wxCommandEvent& event); void OnComputeMD5Sum(wxCommandEvent& event); void OnShowDefaultConfig(wxCommandEvent& event); - void ListSelectionChanged(wxCommandEvent& event); - void OnActionReplayCodeChecked(wxCommandEvent& event); + void PatchListSelectionChanged(wxCommandEvent& event); void PatchButtonClicked(wxCommandEvent& event); - void ActionReplayButtonClicked(wxCommandEvent& event); void RightClickOnBanner(wxMouseEvent& event); void OnBannerImageSave(wxCommandEvent& event); void OnRightClickOnTree(wxTreeEvent& event); @@ -231,7 +220,6 @@ private: std::string game_id; std::set DefaultPatches; - std::set DefaultCheats; void LoadGameConfig(); bool SaveGameConfig(); @@ -239,8 +227,6 @@ private: void GenerateLocalIniModified(); void PatchList_Load(); void PatchList_Save(); - void ActionReplayList_Load(); - void ActionReplayList_Save(); void ChangeBannerDetails(DiscIO::Language language); long GetElementStyle(const char* section, const char* key); From cf0b4c9387bb613b20d170e5e034776653a87ce5 Mon Sep 17 00:00:00 2001 From: EmptyChaos Date: Sat, 23 Jul 2016 01:05:22 +0000 Subject: [PATCH 2/2] ISOProperties: Add notification when Cheats are disabled. If the user enables a cheat while cheats are globally disabled then display a notice about it. Issue 9690. --- .../Core/DolphinWX/Cheats/GeckoCodeDiag.cpp | 8 + Source/Core/DolphinWX/Cheats/GeckoCodeDiag.h | 3 + Source/Core/DolphinWX/Frame.h | 1 + Source/Core/DolphinWX/FrameTools.cpp | 28 ++-- Source/Core/DolphinWX/ISOProperties.cpp | 143 +++++++++++++++++- Source/Core/DolphinWX/ISOProperties.h | 7 +- 6 files changed, 174 insertions(+), 16 deletions(-) diff --git a/Source/Core/DolphinWX/Cheats/GeckoCodeDiag.cpp b/Source/Core/DolphinWX/Cheats/GeckoCodeDiag.cpp index 9ef181354a..4fb77fdb65 100644 --- a/Source/Core/DolphinWX/Cheats/GeckoCodeDiag.cpp +++ b/Source/Core/DolphinWX/Cheats/GeckoCodeDiag.cpp @@ -23,6 +23,8 @@ #include "DolphinWX/Cheats/GeckoCodeDiag.h" #include "DolphinWX/WxUtils.h" +wxDEFINE_EVENT(DOLPHIN_EVT_GECKOCODE_TOGGLED, wxCommandEvent); + namespace Gecko { static const wxString wxstr_name(wxTRANSLATE("Name: ")), wxstr_notes(wxTRANSLATE("Notes: ")), @@ -108,7 +110,13 @@ void CodeConfigPanel::ToggleCode(wxCommandEvent& evt) { const int sel = evt.GetInt(); // this right? if (sel > -1) + { m_gcodes[sel].enabled = m_listbox_gcodes->IsChecked(sel); + + wxCommandEvent toggle_event(DOLPHIN_EVT_GECKOCODE_TOGGLED, GetId()); + toggle_event.SetClientData(&m_gcodes[sel]); + GetEventHandler()->ProcessEvent(toggle_event); + } } void CodeConfigPanel::UpdateInfoBox(wxCommandEvent&) diff --git a/Source/Core/DolphinWX/Cheats/GeckoCodeDiag.h b/Source/Core/DolphinWX/Cheats/GeckoCodeDiag.h index 317f84f205..724dc76939 100644 --- a/Source/Core/DolphinWX/Cheats/GeckoCodeDiag.h +++ b/Source/Core/DolphinWX/Cheats/GeckoCodeDiag.h @@ -17,6 +17,9 @@ class wxListBox; class wxStaticText; class wxTextCtrl; +// GetClientData() -> GeckoCode* [immutable] +wxDECLARE_EVENT(DOLPHIN_EVT_GECKOCODE_TOGGLED, wxCommandEvent); + namespace Gecko { class CodeConfigPanel : public wxPanel diff --git a/Source/Core/DolphinWX/Frame.h b/Source/Core/DolphinWX/Frame.h index cbcee5e374..01285c3a37 100644 --- a/Source/Core/DolphinWX/Frame.h +++ b/Source/Core/DolphinWX/Frame.h @@ -109,6 +109,7 @@ public: void PopulateSavedPerspectives(); static void ConnectWiimote(int wm_idx, bool connect); void UpdateTitle(const std::string& str); + void OpenGeneralConfiguration(int tab = -1); const CGameListCtrl* GetGameListCtrl() const; wxMenuBar* GetMenuBar() const override; diff --git a/Source/Core/DolphinWX/FrameTools.cpp b/Source/Core/DolphinWX/FrameTools.cpp index 4dc8789796..2fdf38a02f 100644 --- a/Source/Core/DolphinWX/FrameTools.cpp +++ b/Source/Core/DolphinWX/FrameTools.cpp @@ -600,6 +600,20 @@ void CFrame::InitBitmaps() RecreateToolbar(); } +void CFrame::OpenGeneralConfiguration(int tab) +{ + CConfigMain config_main(this); + if (tab > -1) + config_main.SetSelectedTab(tab); + + HotkeyManagerEmu::Enable(false); + if (config_main.ShowModal() == wxID_OK) + m_GameListCtrl->Update(); + HotkeyManagerEmu::Enable(true); + + UpdateGUI(); +} + // Menu items // Start the game or change the disc. @@ -1300,12 +1314,7 @@ void CFrame::OnReset(wxCommandEvent& WXUNUSED(event)) void CFrame::OnConfigMain(wxCommandEvent& WXUNUSED(event)) { - CConfigMain ConfigMain(this); - HotkeyManagerEmu::Enable(false); - if (ConfigMain.ShowModal() == wxID_OK) - m_GameListCtrl->Update(); - HotkeyManagerEmu::Enable(true); - UpdateGUI(); + OpenGeneralConfiguration(); } void CFrame::OnConfigGFX(wxCommandEvent& WXUNUSED(event)) @@ -1318,12 +1327,7 @@ void CFrame::OnConfigGFX(wxCommandEvent& WXUNUSED(event)) void CFrame::OnConfigAudio(wxCommandEvent& WXUNUSED(event)) { - CConfigMain ConfigMain(this); - ConfigMain.SetSelectedTab(CConfigMain::ID_AUDIOPAGE); - HotkeyManagerEmu::Enable(false); - if (ConfigMain.ShowModal() == wxID_OK) - m_GameListCtrl->Update(); - HotkeyManagerEmu::Enable(true); + OpenGeneralConfiguration(CConfigMain::ID_AUDIOPAGE); } void CFrame::OnConfigControllers(wxCommandEvent& WXUNUSED(event)) diff --git a/Source/Core/DolphinWX/ISOProperties.cpp b/Source/Core/DolphinWX/ISOProperties.cpp index e8b30dddfe..339ff592bf 100644 --- a/Source/Core/DolphinWX/ISOProperties.cpp +++ b/Source/Core/DolphinWX/ISOProperties.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +57,7 @@ #include "Common/SysConf.h" #include "Core/Boot/Boot.h" #include "Core/ConfigManager.h" +#include "Core/Core.h" #include "Core/GeckoCodeConfig.h" #include "Core/PatchEngine.h" #include "DiscIO/Blob.h" @@ -65,12 +67,126 @@ #include "DiscIO/VolumeCreator.h" #include "DolphinWX/Cheats/ActionReplayCodesPanel.h" #include "DolphinWX/Cheats/GeckoCodeDiag.h" +#include "DolphinWX/Frame.h" #include "DolphinWX/Globals.h" #include "DolphinWX/ISOFile.h" #include "DolphinWX/ISOProperties.h" +#include "DolphinWX/Main.h" #include "DolphinWX/PatchAddEdit.h" #include "DolphinWX/WxUtils.h" +// A warning message displayed on the ARCodes and GeckoCodes pages when cheats are +// disabled globally to explain why turning cheats on does not work. +// Also displays a different warning when the game is currently running to explain +// that toggling codes has no effect while the game is already running. +class CheatWarningMessage final : public wxPanel +{ +public: + CheatWarningMessage(wxWindow* parent, std::string game_id) + : wxPanel(parent), m_game_id(std::move(game_id)) + { + SetExtraStyle(GetExtraStyle() | wxWS_EX_BLOCK_EVENTS); + CreateGUI(); + wxTheApp->Bind(wxEVT_IDLE, &CheatWarningMessage::OnAppIdle, this); + Hide(); + } + + void UpdateState() + { + // If cheats are disabled then show the notification about that. + // If cheats are enabled and the game is currently running then display that warning. + State new_state = State::Hidden; + if (!SConfig::GetInstance().bEnableCheats) + new_state = State::DisabledCheats; + else if (Core::IsRunning() && SConfig::GetInstance().GetUniqueID() == m_game_id) + new_state = State::GameRunning; + ApplyState(new_state); + } + +private: + enum class State + { + Inactive, + Hidden, + DisabledCheats, + GameRunning + }; + + void CreateGUI() + { + wxStaticBitmap* icon = + new wxStaticBitmap(this, wxID_ANY, wxArtProvider::GetMessageBoxIcon(wxICON_WARNING)); + m_message = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, + wxST_NO_AUTORESIZE); + m_btn_configure = new wxButton(this, wxID_ANY, _("Configure Dolphin")); + + m_btn_configure->Bind(wxEVT_BUTTON, &CheatWarningMessage::OnConfigureClicked, this); + + wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(icon, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 15); + sizer->Add(m_message, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, 15); + sizer->Add(m_btn_configure, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 10); + sizer->AddSpacer(10); + + SetSizer(sizer); + } + + void OnConfigureClicked(wxCommandEvent&) + { + main_frame->OpenGeneralConfiguration(); + UpdateState(); + } + + void OnAppIdle(wxIdleEvent& ev) + { + ev.Skip(); + // Only respond to setting changes if we've been triggered once already. + if (m_state != State::Inactive) + UpdateState(); + } + + void ApplyState(State new_state) + { + // The purpose of this function is to prevent unnecessary UI updates which cause flickering. + if (new_state == m_state || (m_state == State::Inactive && new_state == State::Hidden)) + return; + + bool visible = true; + switch (new_state) + { + case State::Inactive: + case State::Hidden: + visible = false; + break; + + case State::DisabledCheats: + m_btn_configure->Show(); + m_message->SetLabelText(_("Dolphin's cheat system is currently disabled.")); + break; + + case State::GameRunning: + m_btn_configure->Hide(); + m_message->SetLabelText( + _("Changing cheats will only take effect when the game is restarted.")); + break; + } + m_state = new_state; + Show(visible); + GetParent()->Layout(); + if (visible) + { + m_message->Wrap(m_message->GetSize().GetWidth()); + m_message->InvalidateBestSize(); + GetParent()->Layout(); + } + } + + std::string m_game_id; + wxStaticText* m_message = nullptr; + wxButton* m_btn_configure = nullptr; + State m_state = State::Inactive; +}; + BEGIN_EVENT_TABLE(CISOProperties, wxDialog) EVT_CLOSE(CISOProperties::OnClose) EVT_BUTTON(wxID_OK, CISOProperties::OnCloseClick) @@ -313,8 +429,8 @@ void CISOProperties::CreateGUIControls() m_Notebook->AddPage(m_PatchPage, _("Patches")); wxPanel* const m_CheatPage = new wxPanel(m_Notebook, ID_ARCODE_PAGE); m_Notebook->AddPage(m_CheatPage, _("AR Codes")); - m_geckocode_panel = new Gecko::CodeConfigPanel(m_Notebook); - m_Notebook->AddPage(m_geckocode_panel, _("Gecko Codes")); + wxPanel* const gecko_cheat_page = new wxPanel(m_Notebook); + m_Notebook->AddPage(gecko_cheat_page, _("Gecko Codes")); wxPanel* const m_Information = new wxPanel(m_Notebook, ID_INFORMATION); m_Notebook->AddPage(m_Information, _("Info")); @@ -472,12 +588,27 @@ void CISOProperties::CreateGUIControls() // Action Replay Cheats m_ar_code_panel = new ActionReplayCodesPanel(m_CheatPage, ActionReplayCodesPanel::STYLE_MODIFY_BUTTONS); + m_cheats_disabled_ar = new CheatWarningMessage(m_CheatPage, game_id); + + m_ar_code_panel->Bind(DOLPHIN_EVT_ARCODE_TOGGLED, &CISOProperties::OnCheatCodeToggled, this); wxBoxSizer* const sCheatPage = new wxBoxSizer(wxVERTICAL); - // TODO: Cheat disabled warning. + sCheatPage->Add(m_cheats_disabled_ar, 0, wxEXPAND | wxTOP, 5); sCheatPage->Add(m_ar_code_panel, 1, wxEXPAND | wxALL, 5); m_CheatPage->SetSizer(sCheatPage); + // Gecko Cheats + m_geckocode_panel = new Gecko::CodeConfigPanel(gecko_cheat_page); + m_cheats_disabled_gecko = new CheatWarningMessage(gecko_cheat_page, game_id); + + m_geckocode_panel->Bind(DOLPHIN_EVT_GECKOCODE_TOGGLED, &CISOProperties::OnCheatCodeToggled, this); + + wxBoxSizer* gecko_layout = new wxBoxSizer(wxVERTICAL); + gecko_layout->Add(m_cheats_disabled_gecko, 0, wxEXPAND | wxTOP, 5); + gecko_layout->Add(m_geckocode_panel, 1, wxEXPAND); + gecko_cheat_page->SetSizer(gecko_layout); + + // Info Page wxStaticText* const m_InternalNameText = new wxStaticText(m_Information, wxID_ANY, _("Internal Name:")); m_InternalName = new wxTextCtrl(m_Information, ID_NAME, wxEmptyString, wxDefaultPosition, @@ -1285,6 +1416,12 @@ void CISOProperties::OnEditConfig(wxCommandEvent& WXUNUSED(event)) GenerateLocalIniModified(); } +void CISOProperties::OnCheatCodeToggled(wxCommandEvent&) +{ + m_cheats_disabled_ar->UpdateState(); + m_cheats_disabled_gecko->UpdateState(); +} + void CISOProperties::OnComputeMD5Sum(wxCommandEvent& WXUNUSED(event)) { wxProgressDialog progressDialog(_("Computing MD5 checksum"), _("Working..."), 100, this, diff --git a/Source/Core/DolphinWX/ISOProperties.h b/Source/Core/DolphinWX/ISOProperties.h index 125ff29967..c3e70e62dd 100644 --- a/Source/Core/DolphinWX/ISOProperties.h +++ b/Source/Core/DolphinWX/ISOProperties.h @@ -19,7 +19,6 @@ #include "DolphinWX/ISOFile.h" #include "DolphinWX/PatchAddEdit.h" -class GameListItem; class wxButton; class wxCheckBox; class wxCheckListBox; @@ -39,6 +38,8 @@ namespace Gecko class CodeConfigPanel; } class ActionReplayCodesPanel; +class CheatWarningMessage; +class GameListItem; class WiiPartition final : public wxTreeItemData { @@ -123,6 +124,9 @@ private: ActionReplayCodesPanel* m_ar_code_panel; Gecko::CodeConfigPanel* m_geckocode_panel; + CheatWarningMessage* m_cheats_disabled_ar; + CheatWarningMessage* m_cheats_disabled_gecko; + enum { ID_TREECTRL = 1000, @@ -203,6 +207,7 @@ private: void CheckPartitionIntegrity(wxCommandEvent& event); void OnEmustateChanged(wxCommandEvent& event); void OnChangeBannerLang(wxCommandEvent& event); + void OnCheatCodeToggled(wxCommandEvent& event); const GameListItem OpenGameListItem;