| | |
| | |
| | |
| |
|
| | #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" |
| | #include "core/core_timing.h" |
| |
|
| | namespace Cheats { |
| |
|
| | |
| | |
| | constexpr u64 run_interval_ticks = 50'000'000; |
| |
|
| | CheatEngine::CheatEngine(Core::System& system_) : system{system_} {} |
| |
|
| | CheatEngine::~CheatEngine() { |
| | if (system.IsPoweredOn()) { |
| | system.CoreTiming().UnscheduleEvent(event, 0); |
| | } |
| | } |
| |
|
| | void CheatEngine::Connect() { |
| | event = system.CoreTiming().RegisterEvent( |
| | "CheatCore::run_event", |
| | [this](u64 thread_id, s64 cycle_late) { RunCallback(thread_id, cycle_late); }); |
| | system.CoreTiming().ScheduleEvent(run_interval_ticks, event); |
| | } |
| |
|
| | std::span<const std::shared_ptr<CheatBase>> CheatEngine::GetCheats() const { |
| | std::shared_lock lock{cheats_list_mutex}; |
| | return cheats_list; |
| | } |
| |
|
| | void CheatEngine::AddCheat(std::shared_ptr<CheatBase>&& cheat) { |
| | std::unique_lock lock{cheats_list_mutex}; |
| | cheats_list.push_back(std::move(cheat)); |
| | } |
| |
|
| | void CheatEngine::RemoveCheat(std::size_t index) { |
| | std::unique_lock 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(std::size_t index, std::shared_ptr<CheatBase>&& new_cheat) { |
| | std::unique_lock lock{cheats_list_mutex}; |
| | if (index < 0 || index >= cheats_list.size()) { |
| | LOG_ERROR(Core_Cheats, "Invalid index {}", index); |
| | return; |
| | } |
| | cheats_list[index] = std::move(new_cheat); |
| | } |
| |
|
| | void CheatEngine::SaveCheatFile(u64 title_id) const { |
| | const std::string cheat_dir = FileUtil::GetUserPath(FileUtil::UserPath::CheatsDir); |
| | const std::string filepath = fmt::format("{}{:016X}.txt", cheat_dir, title_id); |
| |
|
| | if (!FileUtil::IsDirectory(cheat_dir)) { |
| | FileUtil::CreateDir(cheat_dir); |
| | } |
| | FileUtil::IOFile file(filepath, "w"); |
| |
|
| | auto cheats = GetCheats(); |
| | for (const auto& cheat : cheats) { |
| | file.WriteString(cheat->ToString()); |
| | } |
| | } |
| |
|
| | void CheatEngine::LoadCheatFile(u64 title_id) { |
| | { |
| | std::unique_lock lock{cheats_list_mutex}; |
| | if (loaded_title_id.has_value() && loaded_title_id == title_id) { |
| | return; |
| | } |
| | } |
| |
|
| | const std::string cheat_dir = FileUtil::GetUserPath(FileUtil::UserPath::CheatsDir); |
| | const std::string filepath = fmt::format("{}{:016X}.txt", cheat_dir, title_id); |
| |
|
| | if (!FileUtil::IsDirectory(cheat_dir)) { |
| | FileUtil::CreateDir(cheat_dir); |
| | } |
| |
|
| | if (!FileUtil::Exists(filepath)) { |
| | return; |
| | } |
| |
|
| | auto gateway_cheats = GatewayCheat::LoadFile(filepath); |
| | { |
| | std::unique_lock lock{cheats_list_mutex}; |
| | loaded_title_id = title_id; |
| | cheats_list = std::move(gateway_cheats); |
| | } |
| | } |
| |
|
| | void CheatEngine::RunCallback([[maybe_unused]] std::uintptr_t user_data, s64 cycles_late) { |
| | { |
| | std::shared_lock lock{cheats_list_mutex}; |
| | for (const auto& cheat : cheats_list) { |
| | if (cheat->IsEnabled()) { |
| | cheat->Execute(system); |
| | } |
| | } |
| | } |
| | system.CoreTiming().ScheduleEvent(run_interval_ticks - cycles_late, event); |
| | } |
| |
|
| | } |
| |
|