|
| 1 | +/* |
| 2 | + Copyright 2019-2024 Hydr8gon |
| 3 | +
|
| 4 | + This file is part of NooDS. |
| 5 | +
|
| 6 | + NooDS is free software: you can redistribute it and/or modify it |
| 7 | + under the terms of the GNU General Public License as published by |
| 8 | + the Free Software Foundation, either version 3 of the License, or |
| 9 | + (at your option) any later version. |
| 10 | +
|
| 11 | + NooDS is distributed in the hope that it will be useful, but |
| 12 | + WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 | + General Public License for more details. |
| 15 | +
|
| 16 | + You should have received a copy of the GNU General Public License |
| 17 | + along with NooDS. If not, see <https://www.gnu.org/licenses/>. |
| 18 | +*/ |
| 19 | + |
| 20 | +#include "cheat_dialog.h" |
| 21 | + |
| 22 | +enum CheatEvent |
| 23 | +{ |
| 24 | + ADD_CHEAT = 1, |
| 25 | + REMOVE_CHEAT |
| 26 | +}; |
| 27 | + |
| 28 | +wxBEGIN_EVENT_TABLE(CheatDialog, wxDialog) |
| 29 | +EVT_CHECKLISTBOX(wxID_ANY, CheatDialog::checkCheat) |
| 30 | +EVT_LISTBOX(wxID_ANY, CheatDialog::selectCheat) |
| 31 | +EVT_BUTTON(ADD_CHEAT, CheatDialog::addCheat) |
| 32 | +EVT_BUTTON(REMOVE_CHEAT, CheatDialog::removeCheat) |
| 33 | +EVT_BUTTON(wxID_CANCEL, CheatDialog::cancel) |
| 34 | +EVT_BUTTON(wxID_OK, CheatDialog::confirm) |
| 35 | +wxEND_EVENT_TABLE() |
| 36 | + |
| 37 | +CheatDialog::CheatDialog(Core *core): wxDialog(nullptr, wxID_ANY, "Action Replay Cheats"), core(core) |
| 38 | +{ |
| 39 | + // Use the height of a button as a unit to scale pixel values based on DPI/font |
| 40 | + wxButton *dummy = new wxButton(this, wxID_ANY, ""); |
| 41 | + int size = dummy->GetSize().y; |
| 42 | + delete dummy; |
| 43 | + |
| 44 | + // Set up the cheat name and code editors |
| 45 | + wxBoxSizer *editSizer = new wxBoxSizer(wxVERTICAL); |
| 46 | + nameEditor = new wxTextCtrl(); |
| 47 | + nameEditor->Create(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(size * 8, size)); |
| 48 | + editSizer->Add(nameEditor, 0, wxEXPAND | wxBOTTOM, size / 16); |
| 49 | + codeEditor = new wxTextCtrl(); |
| 50 | + codeEditor->Create(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE); |
| 51 | + editSizer->Add(codeEditor, 1, wxEXPAND | wxTOP, size / 16); |
| 52 | + |
| 53 | + // Set up the cheat list and combine it with the editors |
| 54 | + wxBoxSizer *cheatSizer = new wxBoxSizer(wxHORIZONTAL); |
| 55 | + cheatList = new wxCheckListBox(); |
| 56 | + cheatList->Create(this, wxID_ANY, wxDefaultPosition, wxSize(size * 8, size * 12)); |
| 57 | + cheatSizer->Add(cheatList, 1, wxEXPAND | wxRIGHT, size / 16); |
| 58 | + cheatSizer->Add(editSizer, 1, wxEXPAND | wxLEFT, size / 16); |
| 59 | + |
| 60 | + // Disable editors and populate the cheat list |
| 61 | + nameEditor->Disable(); |
| 62 | + codeEditor->Disable(); |
| 63 | + std::vector<ARCheat> &cheats = core->actionReplay.cheats; |
| 64 | + for (uint32_t i = 0; i < cheats.size(); i++) |
| 65 | + { |
| 66 | + cheatList->Append(cheats[i].name); |
| 67 | + cheatList->Check(i, cheats[i].enabled); |
| 68 | + } |
| 69 | + |
| 70 | + // Set up the add, remove, cancel, and confirm buttons |
| 71 | + wxBoxSizer *buttonSizer = new wxBoxSizer(wxHORIZONTAL); |
| 72 | + buttonSizer->Add(new wxButton(this, ADD_CHEAT, "Add"), 0, wxRIGHT, size / 16); |
| 73 | + buttonSizer->Add(new wxButton(this, REMOVE_CHEAT, "Remove"), 0, wxLEFT, size / 16); |
| 74 | + buttonSizer->Add(new wxStaticText(this, wxID_ANY, ""), 1); |
| 75 | + buttonSizer->Add(new wxButton(this, wxID_CANCEL, "Cancel"), 0, wxRIGHT, size / 16); |
| 76 | + buttonSizer->Add(new wxButton(this, wxID_OK, "Confirm"), 0, wxLEFT, size / 16); |
| 77 | + |
| 78 | + // Combine all of the contents |
| 79 | + wxBoxSizer *contents = new wxBoxSizer(wxVERTICAL); |
| 80 | + contents->Add(cheatSizer, 1, wxEXPAND | wxBOTTOM, size / 16); |
| 81 | + contents->Add(buttonSizer, 0, wxEXPAND | wxTOP, size / 16); |
| 82 | + |
| 83 | + // Add a final border around everything |
| 84 | + wxBoxSizer *sizer = new wxBoxSizer(wxHORIZONTAL); |
| 85 | + sizer->Add(contents, 1, wxEXPAND | wxALL, size / 8); |
| 86 | + SetSizer(sizer); |
| 87 | + |
| 88 | + // Size the window to fit the contents and prevent resizing |
| 89 | + sizer->Fit(this); |
| 90 | + SetMinSize(GetSize()); |
| 91 | + SetMaxSize(GetSize()); |
| 92 | +} |
| 93 | + |
| 94 | +void CheatDialog::updateCheat() |
| 95 | +{ |
| 96 | + // Get the current cheat and update its name |
| 97 | + ARCheat *cheat = &core->actionReplay.cheats[curCheat]; |
| 98 | + cheatList->SetString(curCheat, cheat->name = nameEditor->GetValue()); |
| 99 | + cheat->code.clear(); |
| 100 | + |
| 101 | + // Parse the cheat's code from the editor string |
| 102 | + std::string code = codeEditor->GetValue().ToStdString(); |
| 103 | + for (size_t i = 0, p; i < code.size(); i = p + 1) |
| 104 | + { |
| 105 | + p = code.find_first_of(" \n", i); |
| 106 | + if (p == std::string::npos) p = code.size(); |
| 107 | + cheat->code.push_back(strtol(code.substr(i, p).c_str(), nullptr, 16)); |
| 108 | + } |
| 109 | + |
| 110 | + // Ensure the code's word count is a multiple of 2 |
| 111 | + if (cheat->code.size() & 0x1) |
| 112 | + cheat->code.push_back(0); |
| 113 | +} |
| 114 | + |
| 115 | +void CheatDialog::checkCheat(wxCommandEvent &event) |
| 116 | +{ |
| 117 | + // Enable or disable a cheat |
| 118 | + ARCheat &cheat = core->actionReplay.cheats[event.GetInt()]; |
| 119 | + cheat.enabled = !cheat.enabled; |
| 120 | +} |
| 121 | + |
| 122 | +void CheatDialog::selectCheat(wxCommandEvent &event) |
| 123 | +{ |
| 124 | + // Select a new cheat and put its name in the editor |
| 125 | + if (curCheat >= 0) updateCheat(); |
| 126 | + ARCheat *cheat = &core->actionReplay.cheats[curCheat = event.GetInt()]; |
| 127 | + nameEditor->SetValue(cheat->name); |
| 128 | + codeEditor->Clear(); |
| 129 | + |
| 130 | + // Write the code to the editor as a string |
| 131 | + for (uint32_t i = 0; i < cheat->code.size(); i += 2) |
| 132 | + { |
| 133 | + char line[19]; |
| 134 | + sprintf(line, "%08X %08X\n", cheat->code[i], cheat->code[i + 1]); |
| 135 | + codeEditor->AppendText(line); |
| 136 | + } |
| 137 | + |
| 138 | + // Enable the cheat editors |
| 139 | + nameEditor->Enable(); |
| 140 | + codeEditor->Enable(); |
| 141 | +} |
| 142 | + |
| 143 | +void CheatDialog::addCheat(wxCommandEvent &event) |
| 144 | +{ |
| 145 | + // Create a new cheat |
| 146 | + ARCheat cheat; |
| 147 | + cheatList->Append(cheat.name = "New Cheat"); |
| 148 | + cheat.enabled = false; |
| 149 | + core->actionReplay.cheats.push_back(cheat); |
| 150 | +} |
| 151 | + |
| 152 | +void CheatDialog::removeCheat(wxCommandEvent &event) |
| 153 | +{ |
| 154 | + // Remove a cheat and reset the list |
| 155 | + if (curCheat < 0) return; |
| 156 | + std::vector<ARCheat> &cheats = core->actionReplay.cheats; |
| 157 | + cheats.erase(cheats.begin() + curCheat); |
| 158 | + cheatList->Clear(); |
| 159 | + |
| 160 | + // Repopulate the cheat list |
| 161 | + for (uint32_t i = 0; i < cheats.size(); i++) |
| 162 | + { |
| 163 | + cheatList->Append(cheats[i].name); |
| 164 | + cheatList->Check(i, cheats[i].enabled); |
| 165 | + } |
| 166 | + |
| 167 | + // Reset and disable the editors |
| 168 | + curCheat = -1; |
| 169 | + nameEditor->Clear(); |
| 170 | + codeEditor->Clear(); |
| 171 | + nameEditor->Disable(); |
| 172 | + codeEditor->Disable(); |
| 173 | +} |
| 174 | + |
| 175 | +void CheatDialog::cancel(wxCommandEvent &event) |
| 176 | +{ |
| 177 | + // Reload cheats to discard changes |
| 178 | + core->actionReplay.loadCheats(); |
| 179 | + event.Skip(true); |
| 180 | +} |
| 181 | + |
| 182 | +void CheatDialog::confirm(wxCommandEvent &event) |
| 183 | +{ |
| 184 | + // Update the current cheat and save changes |
| 185 | + if (curCheat >= 0) updateCheat(); |
| 186 | + core->actionReplay.saveCheats(); |
| 187 | + event.Skip(true); |
| 188 | +} |
0 commit comments