mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	core/cheats: Add and change a few functions
Added a few interfaces for adding/deleting/replacing/saving cheats. The cheats list is guarded by a std::shared_mutex, and would only need a exclusive lock when it's being updated. I marked the `Execute` function as `const` to avoid accidentally changing the internal state of the cheat on execution, so that execution can be considered a "read" operation which only needs a shared lock. Whether a cheat is enabled or not is now saved by a special comment line `*citra_enabled`.
This commit is contained in:
		
							parent
							
								
									2731437a17
								
							
						
					
					
						commit
						573036b38e
					
				
					 5 changed files with 117 additions and 16 deletions
				
			
		|  | @ -14,7 +14,7 @@ namespace Cheats { | |||
| class CheatBase { | ||||
| public: | ||||
|     virtual ~CheatBase(); | ||||
|     virtual void Execute(Core::System& system) = 0; | ||||
|     virtual void Execute(Core::System& system) const = 0; | ||||
| 
 | ||||
|     virtual bool IsEnabled() const = 0; | ||||
|     virtual void SetEnabled(bool enabled) = 0; | ||||
|  | @ -22,6 +22,7 @@ public: | |||
|     virtual std::string GetComments() const = 0; | ||||
|     virtual std::string GetName() const = 0; | ||||
|     virtual std::string GetType() const = 0; | ||||
|     virtual std::string GetCode() const = 0; | ||||
| 
 | ||||
|     virtual std::string ToString() const = 0; | ||||
| }; | ||||
|  |  | |||
|  | @ -2,8 +2,10 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <fstream> | ||||
| #include <functional> | ||||
| #include <fmt/format.h> | ||||
| #include "common/file_util.h" | ||||
| #include "core/cheats/cheats.h" | ||||
| #include "core/cheats/gateway_cheat.h" | ||||
| #include "core/core.h" | ||||
|  | @ -26,10 +28,54 @@ CheatEngine::~CheatEngine() { | |||
|     system.CoreTiming().UnscheduleEvent(event, 0); | ||||
| } | ||||
| 
 | ||||
| const std::vector<std::unique_ptr<CheatBase>>& CheatEngine::GetCheats() const { | ||||
| std::vector<std::shared_ptr<CheatBase>> CheatEngine::GetCheats() const { | ||||
|     std::shared_lock<std::shared_mutex> lock(cheats_list_mutex); | ||||
|     return cheats_list; | ||||
| } | ||||
| 
 | ||||
| void CheatEngine::AddCheat(const std::shared_ptr<CheatBase>& cheat) { | ||||
|     std::unique_lock<std::shared_mutex> lock(cheats_list_mutex); | ||||
|     cheats_list.push_back(cheat); | ||||
| } | ||||
| 
 | ||||
| void CheatEngine::RemoveCheat(int index) { | ||||
|     std::unique_lock<std::shared_mutex> lock(cheats_list_mutex); | ||||
|     if (index < 0 || index >= cheats_list.size()) { | ||||
|         LOG_ERROR(Core_Cheats, "Invalid index {}", index); | ||||
|         return; | ||||
|     } | ||||
|     cheats_list.erase(cheats_list.begin() + index); | ||||
| } | ||||
| 
 | ||||
| void CheatEngine::UpdateCheat(int index, const std::shared_ptr<CheatBase>& new_cheat) { | ||||
|     std::unique_lock<std::shared_mutex> lock(cheats_list_mutex); | ||||
|     if (index < 0 || index >= cheats_list.size()) { | ||||
|         LOG_ERROR(Core_Cheats, "Invalid index {}", index); | ||||
|         return; | ||||
|     } | ||||
|     cheats_list[index] = new_cheat; | ||||
| } | ||||
| 
 | ||||
| void CheatEngine::SaveCheatFile() const { | ||||
|     const std::string cheat_dir = FileUtil::GetUserPath(FileUtil::UserPath::CheatsDir); | ||||
|     const std::string filepath = fmt::format( | ||||
|         "{}{:016X}.txt", cheat_dir, system.Kernel().GetCurrentProcess()->codeset->program_id); | ||||
| 
 | ||||
|     if (!FileUtil::IsDirectory(cheat_dir)) { | ||||
|         FileUtil::CreateDir(cheat_dir); | ||||
|     } | ||||
| 
 | ||||
|     std::ofstream file; | ||||
|     OpenFStream(file, filepath, std::ios_base::out); | ||||
| 
 | ||||
|     auto cheats = GetCheats(); | ||||
|     for (const auto& cheat : cheats) { | ||||
|         file << cheat->ToString(); | ||||
|     } | ||||
| 
 | ||||
|     file.flush(); | ||||
| } | ||||
| 
 | ||||
| void CheatEngine::LoadCheatFile() { | ||||
|     const std::string cheat_dir = FileUtil::GetUserPath(FileUtil::UserPath::CheatsDir); | ||||
|     const std::string filepath = fmt::format( | ||||
|  | @ -43,13 +89,19 @@ void CheatEngine::LoadCheatFile() { | |||
|         return; | ||||
| 
 | ||||
|     auto gateway_cheats = GatewayCheat::LoadFile(filepath); | ||||
|     std::move(gateway_cheats.begin(), gateway_cheats.end(), std::back_inserter(cheats_list)); | ||||
|     { | ||||
|         std::unique_lock<std::shared_mutex> lock(cheats_list_mutex); | ||||
|         std::move(gateway_cheats.begin(), gateway_cheats.end(), std::back_inserter(cheats_list)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void CheatEngine::RunCallback([[maybe_unused]] u64 userdata, int cycles_late) { | ||||
|     for (auto& cheat : cheats_list) { | ||||
|         if (cheat->IsEnabled()) { | ||||
|             cheat->Execute(system); | ||||
|     { | ||||
|         std::shared_lock<std::shared_mutex> lock(cheats_list_mutex); | ||||
|         for (auto& cheat : cheats_list) { | ||||
|             if (cheat->IsEnabled()) { | ||||
|                 cheat->Execute(system); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     system.CoreTiming().ScheduleEvent(run_interval_ticks - cycles_late, event); | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <shared_mutex> | ||||
| #include <vector> | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
|  | @ -25,12 +26,17 @@ class CheatEngine { | |||
| public: | ||||
|     explicit CheatEngine(Core::System& system); | ||||
|     ~CheatEngine(); | ||||
|     const std::vector<std::unique_ptr<CheatBase>>& GetCheats() const; | ||||
|     std::vector<std::shared_ptr<CheatBase>> GetCheats() const; | ||||
|     void AddCheat(const std::shared_ptr<CheatBase>& cheat); | ||||
|     void RemoveCheat(int index); | ||||
|     void UpdateCheat(int index, const std::shared_ptr<CheatBase>& new_cheat); | ||||
|     void SaveCheatFile() const; | ||||
| 
 | ||||
| private: | ||||
|     void LoadCheatFile(); | ||||
|     void RunCallback(u64 userdata, int cycles_late); | ||||
|     std::vector<std::unique_ptr<CheatBase>> cheats_list; | ||||
|     std::vector<std::shared_ptr<CheatBase>> cheats_list; | ||||
|     mutable std::shared_mutex cheats_list_mutex; | ||||
|     Core::TimingEventType* event; | ||||
|     Core::System& system; | ||||
| }; | ||||
|  |  | |||
|  | @ -176,6 +176,7 @@ GatewayCheat::CheatLine::CheatLine(const std::string& line) { | |||
|         type = CheatType::Null; | ||||
|         cheat_line = line; | ||||
|         LOG_ERROR(Core_Cheats, "Cheat contains invalid line: {}", line); | ||||
|         valid = false; | ||||
|         return; | ||||
|     } | ||||
|     try { | ||||
|  | @ -193,6 +194,7 @@ GatewayCheat::CheatLine::CheatLine(const std::string& line) { | |||
|         type = CheatType::Null; | ||||
|         cheat_line = line; | ||||
|         LOG_ERROR(Core_Cheats, "Cheat contains invalid line: {}", line); | ||||
|         valid = false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -201,9 +203,23 @@ GatewayCheat::GatewayCheat(std::string name_, std::vector<CheatLine> cheat_lines | |||
|     : name(std::move(name_)), cheat_lines(std::move(cheat_lines_)), comments(std::move(comments_)) { | ||||
| } | ||||
| 
 | ||||
| GatewayCheat::GatewayCheat(std::string name_, std::string code, std::string comments_) | ||||
|     : name(std::move(name_)), comments(std::move(comments_)) { | ||||
| 
 | ||||
|     std::vector<std::string> code_lines; | ||||
|     Common::SplitString(code, '\n', code_lines); | ||||
| 
 | ||||
|     std::vector<CheatLine> temp_cheat_lines; | ||||
|     for (std::size_t i = 0; i < code_lines.size(); ++i) { | ||||
|         if (!code_lines[i].empty()) | ||||
|             temp_cheat_lines.emplace_back(code_lines[i]); | ||||
|     } | ||||
|     cheat_lines = std::move(temp_cheat_lines); | ||||
| } | ||||
| 
 | ||||
| GatewayCheat::~GatewayCheat() = default; | ||||
| 
 | ||||
| void GatewayCheat::Execute(Core::System& system) { | ||||
| void GatewayCheat::Execute(Core::System& system) const { | ||||
|     State state; | ||||
| 
 | ||||
|     Memory::MemorySystem& memory = system.Memory(); | ||||
|  | @ -421,13 +437,28 @@ std::string GatewayCheat::GetType() const { | |||
|     return "Gateway"; | ||||
| } | ||||
| 
 | ||||
| std::string GatewayCheat::GetCode() const { | ||||
|     std::string result; | ||||
|     for (const auto& line : cheat_lines) | ||||
|         result += line.cheat_line + '\n'; | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| /// A special marker used to keep track of enabled cheats
 | ||||
| static constexpr char EnabledText[] = "*citra_enabled"; | ||||
| 
 | ||||
| std::string GatewayCheat::ToString() const { | ||||
|     std::string result; | ||||
|     result += '[' + name + "]\n"; | ||||
|     result += comments + '\n'; | ||||
|     for (const auto& line : cheat_lines) | ||||
|         result += line.cheat_line + '\n'; | ||||
|     result += '\n'; | ||||
|     if (enabled) { | ||||
|         result += EnabledText; | ||||
|         result += '\n'; | ||||
|     } | ||||
|     std::vector<std::string> comment_lines; | ||||
|     Common::SplitString(comments, '\n', comment_lines); | ||||
|     for (const auto& comment_line : comment_lines) | ||||
|         result += "*" + comment_line + '\n'; | ||||
|     result += GetCode() + '\n'; | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
|  | @ -443,6 +474,7 @@ std::vector<std::unique_ptr<CheatBase>> GatewayCheat::LoadFile(const std::string | |||
|     std::string comments; | ||||
|     std::vector<CheatLine> cheat_lines; | ||||
|     std::string name; | ||||
|     bool enabled = false; | ||||
| 
 | ||||
|     while (!file.eof()) { | ||||
|         std::string line; | ||||
|  | @ -452,18 +484,25 @@ std::vector<std::unique_ptr<CheatBase>> GatewayCheat::LoadFile(const std::string | |||
|         if (line.length() >= 2 && line.front() == '[') { | ||||
|             if (!cheat_lines.empty()) { | ||||
|                 cheats.push_back(std::make_unique<GatewayCheat>(name, cheat_lines, comments)); | ||||
|                 cheats.back()->SetEnabled(enabled); | ||||
|                 enabled = false; | ||||
|             } | ||||
|             name = line.substr(1, line.length() - 2); | ||||
|             cheat_lines.clear(); | ||||
|             comments.erase(); | ||||
|         } else if (!line.empty() && line.front() == '*') { | ||||
|             comments += line.substr(1, line.length() - 1) + '\n'; | ||||
|             if (line == EnabledText) { | ||||
|                 enabled = true; | ||||
|             } else { | ||||
|                 comments += line.substr(1, line.length() - 1) + '\n'; | ||||
|             } | ||||
|         } else if (!line.empty()) { | ||||
|             cheat_lines.emplace_back(std::move(line)); | ||||
|         } | ||||
|     } | ||||
|     if (!cheat_lines.empty()) { | ||||
|         cheats.push_back(std::make_unique<GatewayCheat>(name, cheat_lines, comments)); | ||||
|         cheats.back()->SetEnabled(enabled); | ||||
|     } | ||||
|     return cheats; | ||||
| } | ||||
|  |  | |||
|  | @ -50,12 +50,14 @@ public: | |||
|         u32 value; | ||||
|         u32 first; | ||||
|         std::string cheat_line; | ||||
|         bool valid = true; | ||||
|     }; | ||||
| 
 | ||||
|     GatewayCheat(std::string name, std::vector<CheatLine> cheat_lines, std::string comments); | ||||
|     GatewayCheat(std::string name, std::string code, std::string comments); | ||||
|     ~GatewayCheat(); | ||||
| 
 | ||||
|     void Execute(Core::System& system) override; | ||||
|     void Execute(Core::System& system) const override; | ||||
| 
 | ||||
|     bool IsEnabled() const override; | ||||
|     void SetEnabled(bool enabled) override; | ||||
|  | @ -63,6 +65,7 @@ public: | |||
|     std::string GetComments() const override; | ||||
|     std::string GetName() const override; | ||||
|     std::string GetType() const override; | ||||
|     std::string GetCode() const override; | ||||
|     std::string ToString() const override; | ||||
| 
 | ||||
|     /// Gateway cheats look like:
 | ||||
|  | @ -77,7 +80,7 @@ public: | |||
| private: | ||||
|     std::atomic<bool> enabled = false; | ||||
|     const std::string name; | ||||
|     const std::vector<CheatLine> cheat_lines; | ||||
|     std::vector<CheatLine> cheat_lines; | ||||
|     const std::string comments; | ||||
| }; | ||||
| } // namespace Cheats
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue