mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +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 { | class CheatBase { | ||||||
| public: | public: | ||||||
|     virtual ~CheatBase(); |     virtual ~CheatBase(); | ||||||
|     virtual void Execute(Core::System& system) = 0; |     virtual void Execute(Core::System& system) const = 0; | ||||||
| 
 | 
 | ||||||
|     virtual bool IsEnabled() const = 0; |     virtual bool IsEnabled() const = 0; | ||||||
|     virtual void SetEnabled(bool enabled) = 0; |     virtual void SetEnabled(bool enabled) = 0; | ||||||
|  | @ -22,6 +22,7 @@ public: | ||||||
|     virtual std::string GetComments() const = 0; |     virtual std::string GetComments() const = 0; | ||||||
|     virtual std::string GetName() const = 0; |     virtual std::string GetName() const = 0; | ||||||
|     virtual std::string GetType() const = 0; |     virtual std::string GetType() const = 0; | ||||||
|  |     virtual std::string GetCode() const = 0; | ||||||
| 
 | 
 | ||||||
|     virtual std::string ToString() const = 0; |     virtual std::string ToString() const = 0; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -2,8 +2,10 @@ | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
|  | #include <fstream> | ||||||
| #include <functional> | #include <functional> | ||||||
| #include <fmt/format.h> | #include <fmt/format.h> | ||||||
|  | #include "common/file_util.h" | ||||||
| #include "core/cheats/cheats.h" | #include "core/cheats/cheats.h" | ||||||
| #include "core/cheats/gateway_cheat.h" | #include "core/cheats/gateway_cheat.h" | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
|  | @ -26,10 +28,54 @@ CheatEngine::~CheatEngine() { | ||||||
|     system.CoreTiming().UnscheduleEvent(event, 0); |     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; |     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() { | void CheatEngine::LoadCheatFile() { | ||||||
|     const std::string cheat_dir = FileUtil::GetUserPath(FileUtil::UserPath::CheatsDir); |     const std::string cheat_dir = FileUtil::GetUserPath(FileUtil::UserPath::CheatsDir); | ||||||
|     const std::string filepath = fmt::format( |     const std::string filepath = fmt::format( | ||||||
|  | @ -43,13 +89,19 @@ void CheatEngine::LoadCheatFile() { | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
|     auto gateway_cheats = GatewayCheat::LoadFile(filepath); |     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) { | void CheatEngine::RunCallback([[maybe_unused]] u64 userdata, int cycles_late) { | ||||||
|     for (auto& cheat : cheats_list) { |     { | ||||||
|         if (cheat->IsEnabled()) { |         std::shared_lock<std::shared_mutex> lock(cheats_list_mutex); | ||||||
|             cheat->Execute(system); |         for (auto& cheat : cheats_list) { | ||||||
|  |             if (cheat->IsEnabled()) { | ||||||
|  |                 cheat->Execute(system); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     system.CoreTiming().ScheduleEvent(run_interval_ticks - cycles_late, event); |     system.CoreTiming().ScheduleEvent(run_interval_ticks - cycles_late, event); | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <memory> | #include <memory> | ||||||
|  | #include <shared_mutex> | ||||||
| #include <vector> | #include <vector> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 | 
 | ||||||
|  | @ -25,12 +26,17 @@ class CheatEngine { | ||||||
| public: | public: | ||||||
|     explicit CheatEngine(Core::System& system); |     explicit CheatEngine(Core::System& system); | ||||||
|     ~CheatEngine(); |     ~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: | private: | ||||||
|     void LoadCheatFile(); |     void LoadCheatFile(); | ||||||
|     void RunCallback(u64 userdata, int cycles_late); |     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::TimingEventType* event; | ||||||
|     Core::System& system; |     Core::System& system; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -176,6 +176,7 @@ GatewayCheat::CheatLine::CheatLine(const std::string& line) { | ||||||
|         type = CheatType::Null; |         type = CheatType::Null; | ||||||
|         cheat_line = line; |         cheat_line = line; | ||||||
|         LOG_ERROR(Core_Cheats, "Cheat contains invalid line: {}", line); |         LOG_ERROR(Core_Cheats, "Cheat contains invalid line: {}", line); | ||||||
|  |         valid = false; | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     try { |     try { | ||||||
|  | @ -193,6 +194,7 @@ GatewayCheat::CheatLine::CheatLine(const std::string& line) { | ||||||
|         type = CheatType::Null; |         type = CheatType::Null; | ||||||
|         cheat_line = line; |         cheat_line = line; | ||||||
|         LOG_ERROR(Core_Cheats, "Cheat contains invalid 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_)) { |     : 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; | GatewayCheat::~GatewayCheat() = default; | ||||||
| 
 | 
 | ||||||
| void GatewayCheat::Execute(Core::System& system) { | void GatewayCheat::Execute(Core::System& system) const { | ||||||
|     State state; |     State state; | ||||||
| 
 | 
 | ||||||
|     Memory::MemorySystem& memory = system.Memory(); |     Memory::MemorySystem& memory = system.Memory(); | ||||||
|  | @ -421,13 +437,28 @@ std::string GatewayCheat::GetType() const { | ||||||
|     return "Gateway"; |     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 GatewayCheat::ToString() const { | ||||||
|     std::string result; |     std::string result; | ||||||
|     result += '[' + name + "]\n"; |     result += '[' + name + "]\n"; | ||||||
|     result += comments + '\n'; |     if (enabled) { | ||||||
|     for (const auto& line : cheat_lines) |         result += EnabledText; | ||||||
|         result += line.cheat_line + '\n'; |         result += '\n'; | ||||||
|     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; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -443,6 +474,7 @@ std::vector<std::unique_ptr<CheatBase>> GatewayCheat::LoadFile(const std::string | ||||||
|     std::string comments; |     std::string comments; | ||||||
|     std::vector<CheatLine> cheat_lines; |     std::vector<CheatLine> cheat_lines; | ||||||
|     std::string name; |     std::string name; | ||||||
|  |     bool enabled = false; | ||||||
| 
 | 
 | ||||||
|     while (!file.eof()) { |     while (!file.eof()) { | ||||||
|         std::string line; |         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 (line.length() >= 2 && line.front() == '[') { | ||||||
|             if (!cheat_lines.empty()) { |             if (!cheat_lines.empty()) { | ||||||
|                 cheats.push_back(std::make_unique<GatewayCheat>(name, cheat_lines, comments)); |                 cheats.push_back(std::make_unique<GatewayCheat>(name, cheat_lines, comments)); | ||||||
|  |                 cheats.back()->SetEnabled(enabled); | ||||||
|  |                 enabled = false; | ||||||
|             } |             } | ||||||
|             name = line.substr(1, line.length() - 2); |             name = line.substr(1, line.length() - 2); | ||||||
|             cheat_lines.clear(); |             cheat_lines.clear(); | ||||||
|             comments.erase(); |             comments.erase(); | ||||||
|         } else if (!line.empty() && line.front() == '*') { |         } 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()) { |         } else if (!line.empty()) { | ||||||
|             cheat_lines.emplace_back(std::move(line)); |             cheat_lines.emplace_back(std::move(line)); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     if (!cheat_lines.empty()) { |     if (!cheat_lines.empty()) { | ||||||
|         cheats.push_back(std::make_unique<GatewayCheat>(name, cheat_lines, comments)); |         cheats.push_back(std::make_unique<GatewayCheat>(name, cheat_lines, comments)); | ||||||
|  |         cheats.back()->SetEnabled(enabled); | ||||||
|     } |     } | ||||||
|     return cheats; |     return cheats; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -50,12 +50,14 @@ public: | ||||||
|         u32 value; |         u32 value; | ||||||
|         u32 first; |         u32 first; | ||||||
|         std::string cheat_line; |         std::string cheat_line; | ||||||
|  |         bool valid = true; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     GatewayCheat(std::string name, std::vector<CheatLine> cheat_lines, std::string comments); |     GatewayCheat(std::string name, std::vector<CheatLine> cheat_lines, std::string comments); | ||||||
|  |     GatewayCheat(std::string name, std::string code, std::string comments); | ||||||
|     ~GatewayCheat(); |     ~GatewayCheat(); | ||||||
| 
 | 
 | ||||||
|     void Execute(Core::System& system) override; |     void Execute(Core::System& system) const override; | ||||||
| 
 | 
 | ||||||
|     bool IsEnabled() const override; |     bool IsEnabled() const override; | ||||||
|     void SetEnabled(bool enabled) override; |     void SetEnabled(bool enabled) override; | ||||||
|  | @ -63,6 +65,7 @@ public: | ||||||
|     std::string GetComments() const override; |     std::string GetComments() const override; | ||||||
|     std::string GetName() const override; |     std::string GetName() const override; | ||||||
|     std::string GetType() const override; |     std::string GetType() const override; | ||||||
|  |     std::string GetCode() const override; | ||||||
|     std::string ToString() const override; |     std::string ToString() const override; | ||||||
| 
 | 
 | ||||||
|     /// Gateway cheats look like:
 |     /// Gateway cheats look like:
 | ||||||
|  | @ -77,7 +80,7 @@ public: | ||||||
| private: | private: | ||||||
|     std::atomic<bool> enabled = false; |     std::atomic<bool> enabled = false; | ||||||
|     const std::string name; |     const std::string name; | ||||||
|     const std::vector<CheatLine> cheat_lines; |     std::vector<CheatLine> cheat_lines; | ||||||
|     const std::string comments; |     const std::string comments; | ||||||
| }; | }; | ||||||
| } // namespace Cheats
 | } // namespace Cheats
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue