diff --git a/src/core/core.cpp b/src/core/core.cpp index bada8ef2c1..8ae97d8713 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -40,6 +40,7 @@ #include "core/hle/service/am/frontend/applets.h" #include "core/hle/service/am/process_creation.h" #include "core/hle/service/apm/apm_controller.h" +#include "core/hle/service/dmnt/cheat_process_manager.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/glue/glue_manager.h" #include "core/hle/service/glue/time/static.h" @@ -54,7 +55,6 @@ #include "core/internal_network/network.h" #include "core/loader/loader.h" #include "core/memory.h" -#include "core/memory/cheat_engine.h" #include "core/perf_stats.h" #include "core/reporter.h" #include "core/tools/freezer.h" @@ -277,8 +277,17 @@ struct System::Impl { audio_core.emplace(system); service_manager = std::make_shared(kernel); + + // Create cheat_manager BEFORE services, as DMNT::LoopProcess needs it + cheat_manager = std::make_unique(system); + services.emplace(service_manager, system, stop_event.get_token()); + // Apply any pending cheats that were registered before cheat_manager was initialized + if (pending_cheats.has_pending) { + ApplyPendingCheats(system); + } + is_powered_on = true; exit_locked = false; exit_requested = false; @@ -339,11 +348,6 @@ struct System::Impl { return init_result; } - // Initialize cheat engine - if (cheat_engine) { - cheat_engine->Initialize(); - } - // Register with applet manager // All threads are started, begin main process execution, now that we're in the clear applet_manager.CreateAndInsertByFrontendAppletParameters(std::move(process), params); @@ -404,7 +408,6 @@ struct System::Impl { services.reset(); service_manager.reset(); fs_controller.Reset(); - cheat_engine.reset(); core_timing.ClearPendingEvents(); app_loader.reset(); audio_core.reset(); @@ -465,7 +468,6 @@ struct System::Impl { Core::SpeedLimiter speed_limiter; ExecuteProgramCallback execute_program_callback; ExitCallback exit_callback; - std::optional services; std::optional debugger; std::optional general_channel_context; @@ -474,7 +476,6 @@ struct System::Impl { std::optional host1x_core; std::optional device_memory; std::optional audio_core; - std::optional cheat_engine; std::optional memory_freezer; std::optional renderdoc_api; @@ -494,6 +495,17 @@ struct System::Impl { std::unique_ptr gpu_core; std::stop_source stop_event; + /// Cheat Manager (DMNT) + std::unique_ptr cheat_manager; + /// Pending cheats to register after cheat_manager is initialized + struct PendingCheats { + std::vector list; + std::array build_id{}; + u64 main_region_begin{}; + u64 main_region_size{}; + bool has_pending{false}; + } pending_cheats; + mutable std::mutex suspend_guard; std::mutex general_channel_mutex; std::atomic_bool is_paused{}; @@ -511,6 +523,61 @@ struct System::Impl { general_channel_event.emplace(*general_channel_context); } } + + void ApplyPendingCheats(System& system) { + if (!pending_cheats.has_pending || !cheat_manager) { + return; + } + + LOG_DEBUG(Core, "Applying {} pending cheats", pending_cheats.list.size()); + + const auto result = cheat_manager->AttachToApplicationProcess( + pending_cheats.build_id, pending_cheats.main_region_begin, + pending_cheats.main_region_size); + + if (result.IsError()) { + LOG_WARNING(Core, "Failed to attach cheat process: result={}", result.raw); + pending_cheats = {}; + return; + } + + LOG_DEBUG(Core, "Cheat process attached successfully"); + + for (const auto& entry : pending_cheats.list) { + if (entry.cheat_id == 0 && entry.definition.num_opcodes != 0) { + LOG_DEBUG(Core, "Setting master cheat '{}' with {} opcodes", + entry.definition.readable_name.data(), entry.definition.num_opcodes); + const auto set_result = cheat_manager->SetMasterCheat(entry.definition); + if (set_result.IsError()) { + LOG_WARNING(Core, "Failed to set master cheat: result={}", set_result.raw); + } + break; + } + } + + // Add normal cheats (cheat_id != 0) + for (const auto& entry : pending_cheats.list) { + if (entry.cheat_id == 0 || entry.definition.num_opcodes == 0) { + continue; + } + + u32 assigned_id = 0; + LOG_DEBUG(Core, "Adding cheat '{}' (enabled={}, {} opcodes)", + entry.definition.readable_name.data(), entry.enabled, + entry.definition.num_opcodes); + const auto add_result = cheat_manager->AddCheat(assigned_id, entry.enabled, + entry.definition); + if (add_result.IsError()) { + LOG_WARNING(Core, + "Failed to add cheat (original_id={} enabled={} name='{}'): result={}", + entry.cheat_id, entry.enabled, + entry.definition.readable_name.data(), add_result.raw); + } + } + + // Clear pending cheats + pending_cheats = {}; + } }; System::System() : impl{std::make_unique(*this)} {} @@ -748,11 +815,61 @@ FileSys::VirtualFilesystem System::GetFilesystem() const { return impl->virtual_filesystem; } -void System::RegisterCheatList(const std::vector& list, +void System::RegisterCheatList(const std::vector& list, const std::array& build_id, u64 main_region_begin, u64 main_region_size) { - impl->cheat_engine.emplace(*this, list, build_id); - impl->cheat_engine->SetMainMemoryParameters(main_region_begin, main_region_size); + // If cheat_manager is not yet initialized, cache the cheats for later + if (!impl->cheat_manager) { + impl->pending_cheats.list = list; + impl->pending_cheats.build_id = build_id; + impl->pending_cheats.main_region_begin = main_region_begin; + impl->pending_cheats.main_region_size = main_region_size; + impl->pending_cheats.has_pending = true; + LOG_INFO(Core, "Cached {} cheats for later registration", list.size()); + return; + } + + // Attach cheat process to the current application process + const auto result = impl->cheat_manager->AttachToApplicationProcess(build_id, main_region_begin, + main_region_size); + if (result.IsError()) { + LOG_WARNING(Core, "Failed to attach cheat process: result={}", result.raw); + return; + } + + // Empty list: nothing more to do + if (list.empty()) { + return; + } + + // Set master cheat if present (cheat_id == 0) + for (const auto& entry : list) { + if (entry.cheat_id == 0 && entry.definition.num_opcodes != 0) { + const auto set_result = impl->cheat_manager->SetMasterCheat(entry.definition); + if (set_result.IsError()) { + LOG_WARNING(Core, "Failed to set master cheat: result={}", set_result.raw); + } + // Only one master cheat allowed + break; + } + } + + // Add normal cheats (cheat_id != 0) + for (const auto& entry : list) { + if (entry.cheat_id == 0 || entry.definition.num_opcodes == 0) { + continue; + } + + u32 assigned_id = 0; + const auto add_result = impl->cheat_manager->AddCheat(assigned_id, entry.enabled, + entry.definition); + if (add_result.IsError()) { + LOG_WARNING(Core, + "Failed to add cheat (original_id={} enabled={} name='{}'): result={}", + entry.cheat_id, entry.enabled, + entry.definition.readable_name.data(), add_result.raw); + } + } } void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set) { @@ -896,6 +1013,14 @@ Tools::RenderdocAPI& System::GetRenderdocAPI() { return *impl->renderdoc_api; } +Service::DMNT::CheatProcessManager& System::GetCheatManager() { + return *impl->cheat_manager; +} + +const Service::DMNT::CheatProcessManager& System::GetCheatManager() const { + return *impl->cheat_manager; +} + void System::RunServer(std::unique_ptr&& server_manager) { return impl->kernel.RunServer(std::move(server_manager)); } diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index adbb611c1c..ceea3437a3 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -90,15 +90,15 @@ std::optional> ReadCheatFileFromFolder( const auto file = base_path->GetFile(fmt::format("{}.txt", build_id)); if (file == nullptr) { - LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}", + LOG_DEBUG(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}", title_id, build_id); return std::nullopt; } std::vector data(file->GetSize()); if (file->Read(data.data(), data.size()) != data.size()) { - LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}", - title_id, build_id); + LOG_WARNING(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}", + title_id, build_id); return std::nullopt; } @@ -661,7 +661,8 @@ std::vector PatchManager::GetPatches(VirtualFile update_raw, const BuildI .parent_name = ""}); // Add individual cheats as sub-entries if we have a build_id - if (has_cheats && has_build_id && !mod_disabled) { + // Always show cheats even if mod folder is disabled, so users can enable individual cheats + if (has_cheats && has_build_id) { // Try to read cheat file (uppercase first, then lowercase) std::optional> cheat_entries; if (auto res = ReadCheatFileFromFolder(title_id, build_id, cheats_dir, true)) {