Compare commits
11 Commits
cheat_mana
...
home_menu5
Author | SHA1 | Date | |
---|---|---|---|
7d9470c2d5 | |||
f0df1e76e0 | |||
95d7f2e57e | |||
1d979d1cbb | |||
6e9faaa21d | |||
136ba33e6a | |||
763f358b41 | |||
4be4bc371b | |||
14618c5403 | |||
408b656e09 | |||
7e0e535406 |
@ -401,14 +401,16 @@ add_library(core STATIC
|
||||
hle/service/am/am_types.h
|
||||
hle/service/am/applet.cpp
|
||||
hle/service/am/applet.h
|
||||
hle/service/am/applet_manager.cpp
|
||||
hle/service/am/applet_data_broker.cpp
|
||||
hle/service/am/applet_data_broker.h
|
||||
hle/service/am/applet_manager.cpp
|
||||
hle/service/am/applet_manager.h
|
||||
hle/service/am/applet_message_queue.cpp
|
||||
hle/service/am/applet_message_queue.h
|
||||
hle/service/am/button_poller.cpp
|
||||
hle/service/am/button_poller.h
|
||||
hle/service/am/display_layer_manager.cpp
|
||||
hle/service/am/display_layer_manager.h
|
||||
hle/service/am/event_observer.cpp
|
||||
hle/service/am/event_observer.h
|
||||
hle/service/am/frontend/applet_cabinet.cpp
|
||||
hle/service/am/frontend/applet_cabinet.h
|
||||
hle/service/am/frontend/applet_controller.cpp
|
||||
@ -434,8 +436,12 @@ add_library(core STATIC
|
||||
hle/service/am/hid_registration.h
|
||||
hle/service/am/library_applet_storage.cpp
|
||||
hle/service/am/library_applet_storage.h
|
||||
hle/service/am/process.cpp
|
||||
hle/service/am/process.h
|
||||
hle/service/am/lifecycle_manager.cpp
|
||||
hle/service/am/lifecycle_manager.h
|
||||
hle/service/am/process_creation.cpp
|
||||
hle/service/am/process_creation.h
|
||||
hle/service/am/process_holder.cpp
|
||||
hle/service/am/process_holder.h
|
||||
hle/service/am/service/all_system_applet_proxies_service.cpp
|
||||
hle/service/am/service/all_system_applet_proxies_service.h
|
||||
hle/service/am/service/applet_common_functions.cpp
|
||||
@ -486,10 +492,10 @@ add_library(core STATIC
|
||||
hle/service/am/service/system_applet_proxy.h
|
||||
hle/service/am/service/window_controller.cpp
|
||||
hle/service/am/service/window_controller.h
|
||||
hle/service/aoc/addon_content_manager.cpp
|
||||
hle/service/aoc/addon_content_manager.h
|
||||
hle/service/aoc/purchase_event_manager.cpp
|
||||
hle/service/aoc/purchase_event_manager.h
|
||||
hle/service/am/window_system.cpp
|
||||
hle/service/am/window_system.h
|
||||
hle/service/aoc/aoc_u.cpp
|
||||
hle/service/aoc/aoc_u.h
|
||||
hle/service/apm/apm.cpp
|
||||
hle/service/apm/apm.h
|
||||
hle/service/apm/apm_controller.cpp
|
||||
@ -590,18 +596,6 @@ add_library(core STATIC
|
||||
hle/service/caps/caps_u.h
|
||||
hle/service/cmif_serialization.h
|
||||
hle/service/cmif_types.h
|
||||
hle/service/dmnt/cheat_interface.cpp
|
||||
hle/service/dmnt/cheat_interface.h
|
||||
hle/service/dmnt/cheat_parser.cpp
|
||||
hle/service/dmnt/cheat_parser.h
|
||||
hle/service/dmnt/cheat_process_manager.cpp
|
||||
hle/service/dmnt/cheat_process_manager.h
|
||||
hle/service/dmnt/cheat_virtual_machine.cpp
|
||||
hle/service/dmnt/cheat_virtual_machine.h
|
||||
hle/service/dmnt/dmnt.cpp
|
||||
hle/service/dmnt/dmnt.h
|
||||
hle/service/dmnt/dmnt_results.h
|
||||
hle/service/dmnt/dmnt_types.h
|
||||
hle/service/erpt/erpt.cpp
|
||||
hle/service/erpt/erpt.h
|
||||
hle/service/es/es.cpp
|
||||
@ -926,6 +920,8 @@ add_library(core STATIC
|
||||
hle/service/os/multi_wait_utils.h
|
||||
hle/service/os/mutex.cpp
|
||||
hle/service/os/mutex.h
|
||||
hle/service/os/process.cpp
|
||||
hle/service/os/process.h
|
||||
hle/service/pcie/pcie.cpp
|
||||
hle/service/pcie/pcie.h
|
||||
hle/service/pctl/parental_control_service_factory.cpp
|
||||
@ -1121,6 +1117,11 @@ add_library(core STATIC
|
||||
loader/xci.h
|
||||
memory.cpp
|
||||
memory.h
|
||||
memory/cheat_engine.cpp
|
||||
memory/cheat_engine.h
|
||||
memory/dmnt_cheat_types.h
|
||||
memory/dmnt_cheat_vm.cpp
|
||||
memory/dmnt_cheat_vm.h
|
||||
perf_stats.cpp
|
||||
perf_stats.h
|
||||
precompiled_headers.h
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
@ -20,7 +19,6 @@
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/debugger/debugger.h"
|
||||
#include "core/device_memory.h"
|
||||
#include "core/file_sys/bis_factory.h"
|
||||
#include "core/file_sys/fs_filesystem.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
@ -38,8 +36,8 @@
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
#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,6 +52,7 @@
|
||||
#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/telemetry_session.h"
|
||||
@ -72,30 +71,6 @@ MICROPROFILE_DEFINE(ARM_CPU3, "ARM", "CPU 3", MP_RGB(255, 64, 64));
|
||||
|
||||
namespace Core {
|
||||
|
||||
namespace {
|
||||
|
||||
FileSys::StorageId GetStorageIdForFrontendSlot(
|
||||
std::optional<FileSys::ContentProviderUnionSlot> slot) {
|
||||
if (!slot.has_value()) {
|
||||
return FileSys::StorageId::None;
|
||||
}
|
||||
|
||||
switch (*slot) {
|
||||
case FileSys::ContentProviderUnionSlot::UserNAND:
|
||||
return FileSys::StorageId::NandUser;
|
||||
case FileSys::ContentProviderUnionSlot::SysNAND:
|
||||
return FileSys::StorageId::NandSystem;
|
||||
case FileSys::ContentProviderUnionSlot::SDMC:
|
||||
return FileSys::StorageId::SdCard;
|
||||
case FileSys::ContentProviderUnionSlot::FrontendManual:
|
||||
return FileSys::StorageId::Host;
|
||||
default:
|
||||
return FileSys::StorageId::None;
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
const std::string& path) {
|
||||
// To account for split 00+01+etc files.
|
||||
@ -297,9 +272,6 @@ struct System::Impl {
|
||||
}
|
||||
|
||||
SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) {
|
||||
/// Reset all glue registrations
|
||||
arp_manager.ResetAll();
|
||||
|
||||
telemetry_session = std::make_unique<Core::TelemetrySession>();
|
||||
|
||||
host1x_core = std::make_unique<Tegra::Host1x::Host1x>(system);
|
||||
@ -335,38 +307,17 @@ struct System::Impl {
|
||||
SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
|
||||
const std::string& filepath,
|
||||
Service::AM::FrontendAppletParameters& params) {
|
||||
app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath),
|
||||
params.program_id, params.program_index);
|
||||
|
||||
if (!app_loader) {
|
||||
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
|
||||
return SystemResultStatus::ErrorGetLoader;
|
||||
}
|
||||
|
||||
if (app_loader->ReadProgramId(params.program_id) != Loader::ResultStatus::Success) {
|
||||
LOG_ERROR(Core, "Failed to find title id for ROM!");
|
||||
}
|
||||
|
||||
std::string name = "Unknown program";
|
||||
if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) {
|
||||
LOG_ERROR(Core, "Failed to read title for ROM!");
|
||||
}
|
||||
|
||||
LOG_INFO(Core, "Loading {} ({})", name, params.program_id);
|
||||
|
||||
InitializeKernel(system);
|
||||
|
||||
// Create the application process.
|
||||
auto main_process = Kernel::KProcess::Create(system.Kernel());
|
||||
Kernel::KProcess::Register(system.Kernel(), main_process);
|
||||
kernel.AppendNewProcess(main_process);
|
||||
kernel.MakeApplicationProcess(main_process);
|
||||
const auto file = GetGameFileFromPath(virtual_filesystem, filepath);
|
||||
|
||||
if (!cheat_manager) {
|
||||
cheat_manager = std::make_unique<Service::DMNT::CheatProcessManager>(system);
|
||||
}
|
||||
// Create the application process
|
||||
Loader::ResultStatus load_result{};
|
||||
std::vector<u8> control;
|
||||
auto process =
|
||||
Service::AM::CreateApplicationProcess(control, app_loader, load_result, system, file,
|
||||
params.program_id, params.program_index);
|
||||
|
||||
const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
|
||||
if (load_result != Loader::ResultStatus::Success) {
|
||||
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", load_result);
|
||||
ShutdownMainProcess();
|
||||
@ -375,6 +326,25 @@ struct System::Impl {
|
||||
static_cast<u32>(SystemResultStatus::ErrorLoader) + static_cast<u32>(load_result));
|
||||
}
|
||||
|
||||
if (!app_loader) {
|
||||
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
|
||||
return SystemResultStatus::ErrorGetLoader;
|
||||
}
|
||||
|
||||
if (app_loader->ReadProgramId(params.program_id) != Loader::ResultStatus::Success) {
|
||||
LOG_ERROR(Core, "Failed to find program id for ROM!");
|
||||
}
|
||||
|
||||
std::string name = "Unknown program";
|
||||
if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) {
|
||||
LOG_ERROR(Core, "Failed to read title for ROM!");
|
||||
}
|
||||
|
||||
LOG_INFO(Core, "Loading {} ({:016X}) ...", name, params.program_id);
|
||||
|
||||
// Make the process created be the application
|
||||
kernel.MakeApplicationProcess(process->GetHandle());
|
||||
|
||||
// Set up the rest of the system.
|
||||
SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)};
|
||||
if (init_result != SystemResultStatus::Success) {
|
||||
@ -384,17 +354,15 @@ struct System::Impl {
|
||||
return init_result;
|
||||
}
|
||||
|
||||
AddGlueRegistrationForProcess(*app_loader, *main_process);
|
||||
telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
|
||||
|
||||
// Register with applet manager.
|
||||
applet_manager.CreateAndInsertByFrontendAppletParameters(main_process->GetProcessId(),
|
||||
params);
|
||||
// Initialize cheat engine
|
||||
if (cheat_engine) {
|
||||
cheat_engine->Initialize();
|
||||
}
|
||||
|
||||
// All threads are started, begin main process execution, now that we're in the clear.
|
||||
main_process->Run(load_parameters->main_thread_priority,
|
||||
load_parameters->main_thread_stack_size);
|
||||
main_process->Close();
|
||||
// Register with applet manager.
|
||||
applet_manager.CreateAndInsertByFrontendAppletParameters(std::move(process), params);
|
||||
|
||||
if (Settings::values.gamecard_inserted) {
|
||||
if (Settings::values.gamecard_current_game) {
|
||||
@ -466,11 +434,10 @@ struct System::Impl {
|
||||
kernel.SuspendEmulation(true);
|
||||
kernel.CloseServices();
|
||||
kernel.ShutdownCores();
|
||||
applet_manager.Reset();
|
||||
services.reset();
|
||||
service_manager.reset();
|
||||
fs_controller.Reset();
|
||||
cheat_manager.reset();
|
||||
cheat_engine.reset();
|
||||
telemetry_session.reset();
|
||||
core_timing.ClearPendingEvents();
|
||||
app_loader.reset();
|
||||
@ -492,6 +459,9 @@ struct System::Impl {
|
||||
// Workarounds
|
||||
Settings::values.renderer_amdvlk_depth_bias_workaround = false;
|
||||
|
||||
// Reset all glue registrations
|
||||
arp_manager.ResetAll();
|
||||
|
||||
LOG_DEBUG(Core, "Shutdown OK");
|
||||
}
|
||||
|
||||
@ -509,31 +479,6 @@ struct System::Impl {
|
||||
return app_loader->ReadTitle(out);
|
||||
}
|
||||
|
||||
void AddGlueRegistrationForProcess(Loader::AppLoader& loader, Kernel::KProcess& process) {
|
||||
std::vector<u8> nacp_data;
|
||||
FileSys::NACP nacp;
|
||||
if (loader.ReadControlData(nacp) == Loader::ResultStatus::Success) {
|
||||
nacp_data = nacp.GetRawBytes();
|
||||
} else {
|
||||
nacp_data.resize(sizeof(FileSys::RawNACP));
|
||||
}
|
||||
|
||||
Service::Glue::ApplicationLaunchProperty launch{};
|
||||
launch.title_id = process.GetProgramId();
|
||||
|
||||
FileSys::PatchManager pm{launch.title_id, fs_controller, *content_provider};
|
||||
launch.version = pm.GetGameVersion().value_or(0);
|
||||
|
||||
// TODO(DarkLordZach): When FSController/Game Card Support is added, if
|
||||
// current_process_game_card use correct StorageId
|
||||
launch.base_game_storage_id = GetStorageIdForFrontendSlot(content_provider->GetSlotForEntry(
|
||||
launch.title_id, FileSys::ContentRecordType::Program));
|
||||
launch.update_storage_id = GetStorageIdForFrontendSlot(content_provider->GetSlotForEntry(
|
||||
FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program));
|
||||
|
||||
arp_manager.Register(launch.title_id, launch, std::move(nacp_data));
|
||||
}
|
||||
|
||||
void SetStatus(SystemResultStatus new_status, const char* details = nullptr) {
|
||||
status = new_status;
|
||||
if (details) {
|
||||
@ -573,7 +518,7 @@ struct System::Impl {
|
||||
bool nvdec_active{};
|
||||
|
||||
Reporter reporter;
|
||||
std::unique_ptr<Service::DMNT::CheatProcessManager> cheat_manager;
|
||||
std::unique_ptr<Memory::CheatEngine> cheat_engine;
|
||||
std::unique_ptr<Tools::Freezer> memory_freezer;
|
||||
std::array<u8, 0x20> build_id{};
|
||||
|
||||
@ -879,20 +824,11 @@ FileSys::VirtualFilesystem System::GetFilesystem() const {
|
||||
return impl->virtual_filesystem;
|
||||
}
|
||||
|
||||
void System::RegisterCheatList(std::span<const Service::DMNT::CheatEntry> list,
|
||||
void System::RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
|
||||
const std::array<u8, 32>& build_id, u64 main_region_begin,
|
||||
u64 main_region_size) {
|
||||
impl->cheat_manager->AttachToApplicationProcess(build_id, main_region_begin, main_region_size);
|
||||
|
||||
// Register cheat list
|
||||
for (const auto& cheat : list) {
|
||||
if (cheat.cheat_id == 0) {
|
||||
impl->cheat_manager->SetMasterCheat(cheat.definition);
|
||||
continue;
|
||||
}
|
||||
u32 cheat_id{};
|
||||
impl->cheat_manager->AddCheat(cheat_id, cheat.enabled, cheat.definition);
|
||||
}
|
||||
impl->cheat_engine = std::make_unique<Memory::CheatEngine>(*this, list, build_id);
|
||||
impl->cheat_engine->SetMainMemoryParameters(main_region_begin, main_region_size);
|
||||
}
|
||||
|
||||
void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set) {
|
||||
@ -1042,14 +978,6 @@ const Core::Debugger& System::GetDebugger() const {
|
||||
return *impl->debugger;
|
||||
}
|
||||
|
||||
Service::DMNT::CheatProcessManager& System::GetCheatManager() {
|
||||
return *impl->cheat_manager;
|
||||
}
|
||||
|
||||
const Service::DMNT::CheatProcessManager& System::GetCheatManager() const {
|
||||
return *impl->cheat_manager;
|
||||
}
|
||||
|
||||
Network::RoomNetwork& System::GetRoomNetwork() {
|
||||
return impl->room_network;
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ enum class ResultStatus : u16;
|
||||
} // namespace Loader
|
||||
|
||||
namespace Core::Memory {
|
||||
struct CheatEntry;
|
||||
class Memory;
|
||||
} // namespace Core::Memory
|
||||
|
||||
@ -63,11 +64,6 @@ namespace APM {
|
||||
class Controller;
|
||||
}
|
||||
|
||||
namespace DMNT {
|
||||
struct CheatEntry;
|
||||
class CheatProcessManager;
|
||||
} // namespace DMNT
|
||||
|
||||
namespace FileSystem {
|
||||
class FileSystemController;
|
||||
} // namespace FileSystem
|
||||
@ -349,7 +345,7 @@ public:
|
||||
|
||||
[[nodiscard]] FileSys::VirtualFilesystem GetFilesystem() const;
|
||||
|
||||
void RegisterCheatList(std::span<const Service::DMNT::CheatEntry> list,
|
||||
void RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
|
||||
const std::array<u8, 0x20>& build_id, u64 main_region_begin,
|
||||
u64 main_region_size);
|
||||
|
||||
@ -391,9 +387,6 @@ public:
|
||||
[[nodiscard]] Core::Debugger& GetDebugger();
|
||||
[[nodiscard]] const Core::Debugger& GetDebugger() const;
|
||||
|
||||
[[nodiscard]] Service::DMNT::CheatProcessManager& GetCheatManager();
|
||||
[[nodiscard]] const Service::DMNT::CheatProcessManager& GetCheatManager() const;
|
||||
|
||||
/// Gets a mutable reference to the Room Network.
|
||||
[[nodiscard]] Network::RoomNetwork& GetRoomNetwork();
|
||||
|
||||
|
@ -24,13 +24,12 @@
|
||||
#include "core/file_sys/vfs/vfs_cached.h"
|
||||
#include "core/file_sys/vfs/vfs_layered.h"
|
||||
#include "core/file_sys/vfs/vfs_vector.h"
|
||||
#include "core/hle/service/dmnt/cheat_parser.h"
|
||||
#include "core/hle/service/dmnt/dmnt_types.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/ns/language.h"
|
||||
#include "core/hle/service/set/settings_server.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/loader/nso.h"
|
||||
#include "core/memory/cheat_engine.h"
|
||||
|
||||
namespace FileSys {
|
||||
namespace {
|
||||
@ -80,7 +79,7 @@ VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name)
|
||||
#endif
|
||||
}
|
||||
|
||||
std::optional<std::vector<Service::DMNT::CheatEntry>> ReadCheatFileFromFolder(
|
||||
std::optional<std::vector<Core::Memory::CheatEntry>> ReadCheatFileFromFolder(
|
||||
u64 title_id, const PatchManager::BuildID& build_id_, const VirtualDir& base_path, bool upper) {
|
||||
const auto build_id_raw = Common::HexToString(build_id_, upper);
|
||||
const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
|
||||
@ -99,7 +98,7 @@ std::optional<std::vector<Service::DMNT::CheatEntry>> ReadCheatFileFromFolder(
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const Service::DMNT::CheatParser parser;
|
||||
const Core::Memory::TextCheatParser parser;
|
||||
return parser.Parse(std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
|
||||
}
|
||||
|
||||
@ -314,7 +313,7 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
|
||||
return !CollectPatches(patch_dirs, build_id).empty();
|
||||
}
|
||||
|
||||
std::vector<Service::DMNT::CheatEntry> PatchManager::CreateCheatList(
|
||||
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(
|
||||
const BuildID& build_id_) const {
|
||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
||||
if (load_dir == nullptr) {
|
||||
@ -327,7 +326,7 @@ std::vector<Service::DMNT::CheatEntry> PatchManager::CreateCheatList(
|
||||
std::sort(patch_dirs.begin(), patch_dirs.end(),
|
||||
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
|
||||
|
||||
std::vector<Service::DMNT::CheatEntry> out;
|
||||
std::vector<Core::Memory::CheatEntry> out;
|
||||
for (const auto& subdir : patch_dirs) {
|
||||
if (std::find(disabled.cbegin(), disabled.cend(), subdir->GetName()) != disabled.cend()) {
|
||||
continue;
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/vfs/vfs_types.h"
|
||||
#include "core/hle/service/dmnt/dmnt_types.h"
|
||||
#include "core/memory/dmnt_cheat_types.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
@ -20,10 +20,6 @@ namespace Service::FileSystem {
|
||||
class FileSystemController;
|
||||
}
|
||||
|
||||
namespace Service::DMNT {
|
||||
struct CheatEntry;
|
||||
}
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
class ContentProvider;
|
||||
@ -69,7 +65,7 @@ public:
|
||||
[[nodiscard]] bool HasNSOPatch(const BuildID& build_id, std::string_view name) const;
|
||||
|
||||
// Creates a CheatList object with all
|
||||
[[nodiscard]] std::vector<Service::DMNT::CheatEntry> CreateCheatList(
|
||||
[[nodiscard]] std::vector<Core::Memory::CheatEntry> CreateCheatList(
|
||||
const BuildID& build_id) const;
|
||||
|
||||
// Currently tracked RomFS patches:
|
||||
|
@ -96,9 +96,10 @@ public:
|
||||
{133, nullptr, "GetNintendoAccountVerificationUrlCache"}, // 9.0.0+
|
||||
{134, nullptr, "RefreshNintendoAccountVerificationUrlCache"}, // 9.0.0+
|
||||
{135, nullptr, "RefreshNintendoAccountVerificationUrlCacheAsyncIfSecondsElapsed"}, // 9.0.0+
|
||||
{140, nullptr, "GetNetworkServiceLicenseCache"}, // 5.0.0+
|
||||
{140, &IManagerForSystemService::GetNetworkServiceLicenseCache, "GetNetworkServiceLicenseCache"}, // 5.0.0+
|
||||
{141, nullptr, "RefreshNetworkServiceLicenseCacheAsync"}, // 5.0.0+
|
||||
{142, nullptr, "RefreshNetworkServiceLicenseCacheAsyncIfSecondsElapsed"}, // 5.0.0+
|
||||
{143, &IManagerForSystemService::Unknown143, "Unknown143"},
|
||||
{150, nullptr, "CreateAuthorizationRequest"},
|
||||
{160, nullptr, "RequiresUpdateNetworkServiceAccountIdTokenCache"},
|
||||
{161, nullptr, "RequireReauthenticationOfNetworkServiceAccount"},
|
||||
@ -114,6 +115,24 @@ private:
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void GetNetworkServiceLicenseCache(HLERequestContext& ctx) {
|
||||
LOG_ERROR(Service_ACC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u64>(0);
|
||||
rb.Push<u64>(0);
|
||||
}
|
||||
|
||||
void Unknown143(HLERequestContext& ctx) {
|
||||
LOG_ERROR(Service_ACC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u64>(0);
|
||||
rb.Push<u64>(0);
|
||||
}
|
||||
};
|
||||
|
||||
// 3.0.0+
|
||||
|
@ -2,19 +2,26 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/button_poller.h"
|
||||
#include "core/hle/service/am/event_observer.h"
|
||||
#include "core/hle/service/am/service/all_system_applet_proxies_service.h"
|
||||
#include "core/hle/service/am/service/application_proxy_service.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
WindowSystem window_system(system);
|
||||
ButtonPoller button_poller(system, window_system);
|
||||
EventObserver event_observer(system, window_system);
|
||||
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
|
||||
server_manager->RegisterNamedService("appletAE",
|
||||
std::make_shared<IAllSystemAppletProxiesService>(system));
|
||||
server_manager->RegisterNamedService("appletOE",
|
||||
std::make_shared<IApplicationProxyService>(system));
|
||||
server_manager->RegisterNamedService(
|
||||
"appletAE", std::make_shared<IAllSystemAppletProxiesService>(system, window_system));
|
||||
server_manager->RegisterNamedService(
|
||||
"appletOE", std::make_shared<IApplicationProxyService>(system, window_system));
|
||||
ServerManager::RunServer(std::move(server_manager));
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ namespace Service::AM {
|
||||
|
||||
constexpr Result ResultNoDataInChannel{ErrorModule::AM, 2};
|
||||
constexpr Result ResultNoMessages{ErrorModule::AM, 3};
|
||||
constexpr Result ResultLibraryAppletTerminated{ErrorModule::AM, 22};
|
||||
constexpr Result ResultInvalidOffset{ErrorModule::AM, 503};
|
||||
constexpr Result ResultInvalidStorageType{ErrorModule::AM, 511};
|
||||
constexpr Result ResultFatalSectionCountImbalance{ErrorModule::AM, 512};
|
||||
|
@ -66,12 +66,6 @@ enum class ScreenshotPermission : u32 {
|
||||
Disable = 2,
|
||||
};
|
||||
|
||||
struct FocusHandlingMode {
|
||||
bool notify;
|
||||
bool background;
|
||||
bool suspend;
|
||||
};
|
||||
|
||||
enum class IdleTimeDetectionExtension : u32 {
|
||||
Disabled = 0,
|
||||
Extended = 1,
|
||||
@ -244,7 +238,6 @@ struct ApplicationPlayStatistics {
|
||||
static_assert(sizeof(ApplicationPlayStatistics) == 0x18,
|
||||
"ApplicationPlayStatistics has incorrect size.");
|
||||
|
||||
using AppletResourceUserId = u64;
|
||||
using ProgramId = u64;
|
||||
|
||||
struct Applet;
|
||||
|
@ -1,27 +1,71 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/scope_exit.h"
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/am/am_results.h"
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
Applet::Applet(Core::System& system, std::unique_ptr<Process> process_)
|
||||
: context(system, "Applet"), message_queue(system), process(std::move(process_)),
|
||||
hid_registration(system, *process), gpu_error_detected_event(context),
|
||||
friend_invitation_storage_channel_event(context), notification_storage_channel_event(context),
|
||||
health_warning_disappeared_system_event(context), acquired_sleep_lock_event(context),
|
||||
pop_from_general_channel_event(context), library_applet_launchable_event(context),
|
||||
accumulated_suspended_tick_changed_event(context), sleep_lock_event(context) {
|
||||
Applet::Applet(Core::System& system, std::unique_ptr<Process> process_, bool is_application)
|
||||
: context(system, "Applet"), lifecycle_manager(system, context, is_application),
|
||||
process(std::move(process_)), hid_registration(system, *process),
|
||||
gpu_error_detected_event(context), friend_invitation_storage_channel_event(context),
|
||||
notification_storage_channel_event(context), health_warning_disappeared_system_event(context),
|
||||
acquired_sleep_lock_event(context), pop_from_general_channel_event(context),
|
||||
library_applet_launchable_event(context), accumulated_suspended_tick_changed_event(context),
|
||||
sleep_lock_event(context), state_changed_event(context) {
|
||||
|
||||
aruid = process->GetProcessId();
|
||||
aruid.pid = process->GetProcessId();
|
||||
program_id = process->GetProgramId();
|
||||
}
|
||||
|
||||
Applet::~Applet() = default;
|
||||
|
||||
void Applet::UpdateSuspensionStateLocked(bool force_message) {
|
||||
// Remove any forced resumption.
|
||||
lifecycle_manager.RemoveForceResumeIfPossible();
|
||||
|
||||
// Check if we're runnable.
|
||||
const bool curr_activity_runnable = lifecycle_manager.IsRunnable();
|
||||
const bool prev_activity_runnable = is_activity_runnable;
|
||||
const bool was_changed = curr_activity_runnable != prev_activity_runnable;
|
||||
|
||||
if (was_changed) {
|
||||
if (curr_activity_runnable) {
|
||||
process->Suspend(false);
|
||||
} else {
|
||||
process->Suspend(true);
|
||||
lifecycle_manager.RequestResumeNotification();
|
||||
}
|
||||
|
||||
is_activity_runnable = curr_activity_runnable;
|
||||
}
|
||||
|
||||
if (lifecycle_manager.GetForcedSuspend()) {
|
||||
// TODO: why is this allowed?
|
||||
return;
|
||||
}
|
||||
|
||||
// Signal if the focus state was changed or the process state was changed.
|
||||
if (lifecycle_manager.UpdateRequestedFocusState() || was_changed || force_message) {
|
||||
lifecycle_manager.SignalSystemEventIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
void Applet::SetInteractibleLocked(bool interactible) {
|
||||
if (is_interactible == interactible) {
|
||||
return;
|
||||
}
|
||||
|
||||
is_interactible = interactible;
|
||||
|
||||
hid_registration.EnableAppletToGetInput(interactible && !lifecycle_manager.GetExitRequested());
|
||||
}
|
||||
|
||||
void Applet::OnProcessTerminatedLocked() {
|
||||
is_completed = true;
|
||||
state_changed_event.Signal();
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
@ -3,25 +3,28 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
|
||||
#include "common/math_util.h"
|
||||
#include "core/hle/service/apm/apm_controller.h"
|
||||
#include "core/hle/service/caps/caps_types.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/os/event.h"
|
||||
#include "core/hle/service/os/process.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
#include "core/hle/service/am/am_types.h"
|
||||
#include "core/hle/service/am/applet_message_queue.h"
|
||||
#include "core/hle/service/am/display_layer_manager.h"
|
||||
#include "core/hle/service/am/hid_registration.h"
|
||||
#include "core/hle/service/am/process.h"
|
||||
#include "core/hle/service/am/lifecycle_manager.h"
|
||||
#include "core/hle/service/am/process_holder.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
struct Applet {
|
||||
explicit Applet(Core::System& system, std::unique_ptr<Process> process_);
|
||||
explicit Applet(Core::System& system, std::unique_ptr<Process> process_, bool is_application);
|
||||
~Applet();
|
||||
|
||||
// Lock
|
||||
@ -30,11 +33,13 @@ struct Applet {
|
||||
// Event creation helper
|
||||
KernelHelpers::ServiceContext context;
|
||||
|
||||
// Applet message queue
|
||||
AppletMessageQueue message_queue;
|
||||
// Lifecycle manager
|
||||
LifecycleManager lifecycle_manager;
|
||||
|
||||
// Process
|
||||
std::unique_ptr<Process> process;
|
||||
std::optional<ProcessHolder> process_holder;
|
||||
bool is_process_running{};
|
||||
|
||||
// Creation state
|
||||
AppletId applet_id{};
|
||||
@ -75,11 +80,9 @@ struct Applet {
|
||||
bool game_play_recording_supported{};
|
||||
GamePlayRecordingState game_play_recording_state{GamePlayRecordingState::Disabled};
|
||||
bool jit_service_launched{};
|
||||
bool is_running{};
|
||||
bool application_crash_report_enabled{};
|
||||
|
||||
// Common state
|
||||
FocusState focus_state{};
|
||||
bool sleep_lock_enabled{};
|
||||
bool vr_mode_enabled{};
|
||||
bool lcd_backlight_off_enabled{};
|
||||
@ -93,15 +96,12 @@ struct Applet {
|
||||
// Caller applet
|
||||
std::weak_ptr<Applet> caller_applet{};
|
||||
std::shared_ptr<AppletDataBroker> caller_applet_broker{};
|
||||
std::list<std::shared_ptr<Applet>> child_applets{};
|
||||
bool is_completed{};
|
||||
|
||||
// Self state
|
||||
bool exit_locked{};
|
||||
s32 fatal_section_count{};
|
||||
bool operation_mode_changed_notification_enabled{true};
|
||||
bool performance_mode_changed_notification_enabled{true};
|
||||
FocusHandlingMode focus_handling_mode{};
|
||||
bool restart_message_enabled{};
|
||||
bool out_of_focus_suspension_enabled{true};
|
||||
Capture::AlbumImageOrientation album_image_orientation{};
|
||||
bool handles_request_to_display{};
|
||||
ScreenshotPermission screenshot_permission{};
|
||||
@ -110,6 +110,9 @@ struct Applet {
|
||||
u64 suspended_ticks{};
|
||||
bool album_image_taken_notification_enabled{};
|
||||
bool record_volume_muted{};
|
||||
bool is_activity_runnable{};
|
||||
bool is_interactible{true};
|
||||
bool window_visible{true};
|
||||
|
||||
// Events
|
||||
Event gpu_error_detected_event;
|
||||
@ -121,9 +124,15 @@ struct Applet {
|
||||
Event library_applet_launchable_event;
|
||||
Event accumulated_suspended_tick_changed_event;
|
||||
Event sleep_lock_event;
|
||||
Event state_changed_event;
|
||||
|
||||
// Frontend state
|
||||
std::shared_ptr<Frontend::FrontendApplet> frontend{};
|
||||
|
||||
// Process state management
|
||||
void UpdateSuspensionStateLocked(bool force_message);
|
||||
void SetInteractibleLocked(bool interactible);
|
||||
void OnProcessTerminatedLocked();
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
|
@ -44,24 +44,8 @@ Kernel::KReadableEvent* AppletStorageChannel::GetEvent() {
|
||||
|
||||
AppletDataBroker::AppletDataBroker(Core::System& system_)
|
||||
: system(system_), context(system_, "AppletDataBroker"), in_data(context),
|
||||
interactive_in_data(context), out_data(context), interactive_out_data(context),
|
||||
state_changed_event(context), is_completed(false) {}
|
||||
interactive_in_data(context), out_data(context), interactive_out_data(context) {}
|
||||
|
||||
AppletDataBroker::~AppletDataBroker() = default;
|
||||
|
||||
void AppletDataBroker::SignalCompletion() {
|
||||
{
|
||||
std::scoped_lock lk{lock};
|
||||
|
||||
if (is_completed) {
|
||||
return;
|
||||
}
|
||||
|
||||
is_completed = true;
|
||||
state_changed_event.Signal();
|
||||
}
|
||||
|
||||
system.GetAppletManager().FocusStateChanged();
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
@ -53,16 +53,6 @@ public:
|
||||
return interactive_out_data;
|
||||
}
|
||||
|
||||
Event& GetStateChangedEvent() {
|
||||
return state_changed_event;
|
||||
}
|
||||
|
||||
bool IsCompleted() const {
|
||||
return is_completed;
|
||||
}
|
||||
|
||||
void SignalCompletion();
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
KernelHelpers::ServiceContext context;
|
||||
@ -71,10 +61,6 @@ private:
|
||||
AppletStorageChannel interactive_in_data;
|
||||
AppletStorageChannel out_data;
|
||||
AppletStorageChannel interactive_out_data;
|
||||
Event state_changed_event;
|
||||
|
||||
std::mutex lock;
|
||||
bool is_completed;
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "core/hle/service/am/frontend/applet_mii_edit_types.h"
|
||||
#include "core/hle/service/am/frontend/applet_software_keyboard_types.h"
|
||||
#include "core/hle/service/am/service/storage.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "hid_core/hid_types.h"
|
||||
|
||||
namespace Service::AM {
|
||||
@ -222,52 +223,70 @@ void PushInShowSoftwareKeyboard(Core::System& system, AppletStorageChannel& chan
|
||||
channel.Push(std::make_shared<IStorage>(system, std::move(work_buffer)));
|
||||
}
|
||||
|
||||
void PushInShowMyPageData(Core::System& system, AppletStorageChannel& channel) {
|
||||
const CommonArguments arguments{
|
||||
.arguments_version = CommonArgumentVersion::Version3,
|
||||
.size = CommonArgumentSize::Version3,
|
||||
.library_version = 0,
|
||||
.theme_color = ThemeColor::BasicBlack,
|
||||
.play_startup_sound = true,
|
||||
.system_tick = system.CoreTiming().GetClockTicks(),
|
||||
};
|
||||
|
||||
std::vector<u8> argument_data(sizeof(arguments));
|
||||
std::vector<u8> settings_data{0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCE,
|
||||
0x9E, 0xF8, 0xAF, 0xB1, 0xE8, 0x95, 0x90, 0x33, 0x6C,
|
||||
0x0B, 0x22, 0x70, 0x07, 0xF4, 0xBB, 0x00};
|
||||
settings_data.resize(0x10a8);
|
||||
std::memcpy(argument_data.data(), &arguments, sizeof(arguments));
|
||||
|
||||
channel.Push(std::make_shared<IStorage>(system, std::move(argument_data)));
|
||||
channel.Push(std::make_shared<IStorage>(system, std::move(settings_data)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AppletManager::AppletManager(Core::System& system) : m_system(system) {}
|
||||
AppletManager::~AppletManager() {
|
||||
this->Reset();
|
||||
}
|
||||
AppletManager::~AppletManager() = default;
|
||||
|
||||
void AppletManager::InsertApplet(std::shared_ptr<Applet> applet) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
m_applets.emplace(applet->aruid, std::move(applet));
|
||||
}
|
||||
|
||||
void AppletManager::TerminateAndRemoveApplet(AppletResourceUserId aruid) {
|
||||
std::shared_ptr<Applet> applet;
|
||||
bool should_stop = false;
|
||||
void AppletManager::CreateAndInsertByFrontendAppletParameters(
|
||||
std::unique_ptr<Process> process, const FrontendAppletParameters& params) {
|
||||
{
|
||||
std::scoped_lock lk{m_lock};
|
||||
m_pending_process = std::move(process);
|
||||
m_pending_parameters = params;
|
||||
}
|
||||
m_cv.notify_all();
|
||||
}
|
||||
|
||||
const auto it = m_applets.find(aruid);
|
||||
if (it == m_applets.end()) {
|
||||
void AppletManager::RequestExit() {
|
||||
std::scoped_lock lk{m_lock};
|
||||
if (m_window_system) {
|
||||
m_window_system->OnExitRequested();
|
||||
}
|
||||
}
|
||||
|
||||
void AppletManager::OperationModeChanged() {
|
||||
std::scoped_lock lk{m_lock};
|
||||
if (m_window_system) {
|
||||
m_window_system->OnOperationModeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void AppletManager::SetWindowSystem(WindowSystem* window_system) {
|
||||
std::unique_lock lk{m_lock};
|
||||
|
||||
m_window_system = window_system;
|
||||
if (!m_window_system) {
|
||||
return;
|
||||
}
|
||||
|
||||
applet = it->second;
|
||||
m_applets.erase(it);
|
||||
m_cv.wait(lk, [&] { return m_pending_process != nullptr; });
|
||||
|
||||
should_stop = m_applets.empty();
|
||||
}
|
||||
const auto& params = m_pending_parameters;
|
||||
auto applet = std::make_shared<Applet>(m_system, std::move(m_pending_process),
|
||||
params.applet_id == AppletId::Application);
|
||||
|
||||
// Terminate process.
|
||||
applet->process->Terminate();
|
||||
|
||||
// If there were no applets left, stop emulation.
|
||||
if (should_stop) {
|
||||
m_system.Exit();
|
||||
}
|
||||
}
|
||||
|
||||
void AppletManager::CreateAndInsertByFrontendAppletParameters(
|
||||
AppletResourceUserId aruid, const FrontendAppletParameters& params) {
|
||||
// TODO: this should be run inside AM so that the events will have a parent process
|
||||
// TODO: have am create the guest process
|
||||
auto applet = std::make_shared<Applet>(m_system, std::make_unique<Process>(m_system));
|
||||
|
||||
applet->aruid = aruid;
|
||||
applet->program_id = params.program_id;
|
||||
applet->applet_id = params.applet_id;
|
||||
applet->type = params.applet_type;
|
||||
@ -317,64 +336,27 @@ void AppletManager::CreateAndInsertByFrontendAppletParameters(
|
||||
case AppletId::Controller:
|
||||
PushInShowController(m_system, InitializeFakeCallerApplet(m_system, applet));
|
||||
break;
|
||||
case AppletId::MyPage:
|
||||
PushInShowMyPageData(m_system, InitializeFakeCallerApplet(m_system, applet));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Applet was started by frontend, so it is foreground.
|
||||
applet->message_queue.PushMessage(AppletMessage::ChangeIntoForeground);
|
||||
applet->message_queue.PushMessage(AppletMessage::FocusStateChanged);
|
||||
applet->focus_state = FocusState::InFocus;
|
||||
applet->lifecycle_manager.SetFocusState(FocusState::InFocus);
|
||||
|
||||
this->InsertApplet(std::move(applet));
|
||||
}
|
||||
|
||||
std::shared_ptr<Applet> AppletManager::GetByAppletResourceUserId(AppletResourceUserId aruid) const {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
if (const auto it = m_applets.find(aruid); it != m_applets.end()) {
|
||||
return it->second;
|
||||
if (applet->applet_id == AppletId::QLaunch) {
|
||||
applet->lifecycle_manager.SetFocusHandlingMode(false);
|
||||
applet->lifecycle_manager.SetOutOfFocusSuspendingEnabled(false);
|
||||
m_window_system->TrackApplet(applet, false);
|
||||
m_window_system->RequestHomeMenuToGetForeground();
|
||||
} else {
|
||||
m_window_system->TrackApplet(applet, true);
|
||||
m_window_system->RequestApplicationToGetForeground();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void AppletManager::Reset() {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
m_applets.clear();
|
||||
}
|
||||
|
||||
void AppletManager::RequestExit() {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
for (const auto& [aruid, applet] : m_applets) {
|
||||
applet->message_queue.RequestExit();
|
||||
}
|
||||
}
|
||||
|
||||
void AppletManager::RequestResume() {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
for (const auto& [aruid, applet] : m_applets) {
|
||||
applet->message_queue.RequestResume();
|
||||
}
|
||||
}
|
||||
|
||||
void AppletManager::OperationModeChanged() {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
for (const auto& [aruid, applet] : m_applets) {
|
||||
applet->message_queue.OperationModeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void AppletManager::FocusStateChanged() {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
for (const auto& [aruid, applet] : m_applets) {
|
||||
applet->message_queue.FocusStateChanged();
|
||||
}
|
||||
applet->process->Run();
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
@ -3,17 +3,23 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/am_types.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service {
|
||||
class Process;
|
||||
}
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
class WindowSystem;
|
||||
|
||||
enum class LaunchType {
|
||||
FrontendInitiated,
|
||||
ApplicationInitiated,
|
||||
@ -33,27 +39,24 @@ public:
|
||||
explicit AppletManager(Core::System& system);
|
||||
~AppletManager();
|
||||
|
||||
void InsertApplet(std::shared_ptr<Applet> applet);
|
||||
void TerminateAndRemoveApplet(AppletResourceUserId aruid);
|
||||
|
||||
void CreateAndInsertByFrontendAppletParameters(AppletResourceUserId aruid,
|
||||
void CreateAndInsertByFrontendAppletParameters(std::unique_ptr<Process> process,
|
||||
const FrontendAppletParameters& params);
|
||||
std::shared_ptr<Applet> GetByAppletResourceUserId(AppletResourceUserId aruid) const;
|
||||
|
||||
void Reset();
|
||||
|
||||
void RequestExit();
|
||||
void RequestResume();
|
||||
void OperationModeChanged();
|
||||
void FocusStateChanged();
|
||||
|
||||
public:
|
||||
void SetWindowSystem(WindowSystem* window_system);
|
||||
|
||||
private:
|
||||
Core::System& m_system;
|
||||
|
||||
mutable std::mutex m_lock{};
|
||||
std::map<AppletResourceUserId, std::shared_ptr<Applet>> m_applets{};
|
||||
std::mutex m_lock;
|
||||
std::condition_variable m_cv;
|
||||
|
||||
// AudioController state goes here
|
||||
WindowSystem* m_window_system{};
|
||||
|
||||
FrontendAppletParameters m_pending_parameters{};
|
||||
std::unique_ptr<Process> m_pending_process{};
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
|
@ -1,73 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/am/applet_message_queue.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
AppletMessageQueue::AppletMessageQueue(Core::System& system)
|
||||
: service_context{system, "AppletMessageQueue"} {
|
||||
on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived");
|
||||
on_operation_mode_changed = service_context.CreateEvent("AMMessageQueue:OperationModeChanged");
|
||||
}
|
||||
|
||||
AppletMessageQueue::~AppletMessageQueue() {
|
||||
service_context.CloseEvent(on_new_message);
|
||||
service_context.CloseEvent(on_operation_mode_changed);
|
||||
}
|
||||
|
||||
Kernel::KReadableEvent& AppletMessageQueue::GetMessageReceiveEvent() {
|
||||
return on_new_message->GetReadableEvent();
|
||||
}
|
||||
|
||||
Kernel::KReadableEvent& AppletMessageQueue::GetOperationModeChangedEvent() {
|
||||
return on_operation_mode_changed->GetReadableEvent();
|
||||
}
|
||||
|
||||
void AppletMessageQueue::PushMessage(AppletMessage msg) {
|
||||
{
|
||||
std::scoped_lock lk{lock};
|
||||
messages.push(msg);
|
||||
}
|
||||
on_new_message->Signal();
|
||||
}
|
||||
|
||||
AppletMessage AppletMessageQueue::PopMessage() {
|
||||
std::scoped_lock lk{lock};
|
||||
if (messages.empty()) {
|
||||
on_new_message->Clear();
|
||||
return AppletMessage::None;
|
||||
}
|
||||
auto msg = messages.front();
|
||||
messages.pop();
|
||||
if (messages.empty()) {
|
||||
on_new_message->Clear();
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
std::size_t AppletMessageQueue::GetMessageCount() const {
|
||||
std::scoped_lock lk{lock};
|
||||
return messages.size();
|
||||
}
|
||||
|
||||
void AppletMessageQueue::RequestExit() {
|
||||
PushMessage(AppletMessage::Exit);
|
||||
}
|
||||
|
||||
void AppletMessageQueue::RequestResume() {
|
||||
PushMessage(AppletMessage::Resume);
|
||||
}
|
||||
|
||||
void AppletMessageQueue::FocusStateChanged() {
|
||||
PushMessage(AppletMessage::FocusStateChanged);
|
||||
}
|
||||
|
||||
void AppletMessageQueue::OperationModeChanged() {
|
||||
PushMessage(AppletMessage::OperationModeChanged);
|
||||
PushMessage(AppletMessage::PerformanceModeChanged);
|
||||
on_operation_mode_changed->Signal();
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
@ -1,43 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include "core/hle/service/am/am_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KReadableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
class AppletMessageQueue {
|
||||
public:
|
||||
explicit AppletMessageQueue(Core::System& system);
|
||||
~AppletMessageQueue();
|
||||
|
||||
Kernel::KReadableEvent& GetMessageReceiveEvent();
|
||||
Kernel::KReadableEvent& GetOperationModeChangedEvent();
|
||||
void PushMessage(AppletMessage msg);
|
||||
AppletMessage PopMessage();
|
||||
std::size_t GetMessageCount() const;
|
||||
void RequestExit();
|
||||
void RequestResume();
|
||||
void FocusStateChanged();
|
||||
void OperationModeChanged();
|
||||
|
||||
private:
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
Kernel::KEvent* on_new_message;
|
||||
Kernel::KEvent* on_operation_mode_changed;
|
||||
|
||||
mutable std::mutex lock;
|
||||
std::queue<AppletMessage> messages;
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
89
src/core/hle/service/am/button_poller.cpp
Normal file
89
src/core/hle/service/am/button_poller.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/am/button_poller.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "hid_core/frontend/emulated_controller.h"
|
||||
#include "hid_core/hid_core.h"
|
||||
#include "hid_core/hid_types.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
namespace {
|
||||
|
||||
ButtonPressDuration ClassifyPressDuration(std::chrono::steady_clock::time_point start) {
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
const auto dur = std::chrono::steady_clock::now() - start;
|
||||
|
||||
// TODO: determine actual thresholds
|
||||
// TODO: these are likely different for each button
|
||||
if (dur < 500ms) {
|
||||
return ButtonPressDuration::ShortPressing;
|
||||
} else if (dur < 1000ms) {
|
||||
return ButtonPressDuration::MiddlePressing;
|
||||
} else {
|
||||
return ButtonPressDuration::LongPressing;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system)
|
||||
: m_window_system(window_system) {
|
||||
// TODO: am reads this from the home button state in hid, which is controller-agnostic.
|
||||
Core::HID::ControllerUpdateCallback engine_callback{
|
||||
.on_change =
|
||||
[this](Core::HID::ControllerTriggerType type) {
|
||||
if (type == Core::HID::ControllerTriggerType::Button) {
|
||||
this->OnButtonStateChanged();
|
||||
}
|
||||
},
|
||||
.is_npad_service = true,
|
||||
};
|
||||
|
||||
m_handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
||||
m_handheld_key = m_handheld->SetCallback(engine_callback);
|
||||
m_player1 = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
||||
m_player1_key = m_player1->SetCallback(engine_callback);
|
||||
}
|
||||
|
||||
ButtonPoller::~ButtonPoller() {
|
||||
m_handheld->DeleteCallback(m_handheld_key);
|
||||
m_player1->DeleteCallback(m_player1_key);
|
||||
}
|
||||
|
||||
void ButtonPoller::OnButtonStateChanged() {
|
||||
const bool home_button =
|
||||
m_handheld->GetHomeButtons().home.Value() || m_player1->GetHomeButtons().home.Value();
|
||||
const bool capture_button = m_handheld->GetCaptureButtons().capture.Value() ||
|
||||
m_player1->GetCaptureButtons().capture.Value();
|
||||
|
||||
// Buttons pressed which were not previously pressed
|
||||
if (home_button && !m_home_button_press_start) {
|
||||
m_home_button_press_start = std::chrono::steady_clock::now();
|
||||
}
|
||||
if (capture_button && !m_capture_button_press_start) {
|
||||
m_capture_button_press_start = std::chrono::steady_clock::now();
|
||||
}
|
||||
// if (power_button && !m_power_button_press_start) {
|
||||
// m_power_button_press_start = std::chrono::steady_clock::now();
|
||||
// }
|
||||
|
||||
// Buttons released which were previously held
|
||||
if (!home_button && m_home_button_press_start) {
|
||||
m_window_system.OnHomeButtonPressed(ClassifyPressDuration(*m_home_button_press_start));
|
||||
m_home_button_press_start = std::nullopt;
|
||||
}
|
||||
if (!capture_button && m_capture_button_press_start) {
|
||||
// TODO
|
||||
m_capture_button_press_start = std::nullopt;
|
||||
}
|
||||
// if (!power_button && m_power_button_press_start) {
|
||||
// // TODO
|
||||
// m_power_button_press_start = std::nullopt;
|
||||
// }
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
43
src/core/hle/service/am/button_poller.h
Normal file
43
src/core/hle/service/am/button_poller.h
Normal file
@ -0,0 +1,43 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
#include "hid_core/frontend/emulated_controller.h"
|
||||
|
||||
namespace Core {
|
||||
namespace HID {
|
||||
class EmulatedController;
|
||||
}
|
||||
|
||||
class System;
|
||||
} // namespace Core
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
class WindowSystem;
|
||||
|
||||
class ButtonPoller {
|
||||
public:
|
||||
explicit ButtonPoller(Core::System& system, WindowSystem& window_system);
|
||||
~ButtonPoller();
|
||||
|
||||
private:
|
||||
void OnButtonStateChanged();
|
||||
|
||||
private:
|
||||
WindowSystem& m_window_system;
|
||||
|
||||
Core::HID::EmulatedController* m_handheld{};
|
||||
int m_handheld_key{};
|
||||
Core::HID::EmulatedController* m_player1{};
|
||||
int m_player1_key{};
|
||||
|
||||
std::optional<std::chrono::steady_clock::time_point> m_home_button_press_start{};
|
||||
std::optional<std::chrono::steady_clock::time_point> m_capture_button_press_start{};
|
||||
std::optional<std::chrono::steady_clock::time_point> m_power_button_press_start{};
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
57
src/core/hle/service/am/cradle_firmware_updater.cpp
Normal file
57
src/core/hle/service/am/cradle_firmware_updater.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/am/cradle_firmware_updater.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
ICradleFirmwareUpdater::ICradleFirmwareUpdater(Core::System& system_)
|
||||
: ServiceFramework{system_, "ICradleFirmwareUpdater"}, service_context{system,
|
||||
"IHomeMenuFunctions"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "StartUpdate"},
|
||||
{1, &ICradleFirmwareUpdater::FinishUpdate, "FinishUpdate"},
|
||||
{2, &ICradleFirmwareUpdater::GetCradleDeviceInfo, "GetCradleDeviceInfo"},
|
||||
{3, &ICradleFirmwareUpdater::GetCradleDeviceInfoChangeEvent, "GetCradleDeviceInfoChangeEvent"},
|
||||
{4, nullptr, "GetUpdateProgressInfo"},
|
||||
{5, nullptr, "GetLastInternalResult"},
|
||||
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
cradle_device_info_event =
|
||||
service_context.CreateEvent("IHomeMenuFunctions:PopFromGeneralChannelEvent");
|
||||
}
|
||||
|
||||
ICradleFirmwareUpdater::~ICradleFirmwareUpdater() {
|
||||
service_context.CloseEvent(cradle_device_info_event);
|
||||
}
|
||||
|
||||
void ICradleFirmwareUpdater::FinishUpdate(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
void ICradleFirmwareUpdater::GetCradleDeviceInfo(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 5};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u64>(0);
|
||||
rb.Push<u32>(0);
|
||||
}
|
||||
|
||||
void ICradleFirmwareUpdater::GetCradleDeviceInfoChangeEvent(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(cradle_device_info_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
25
src/core/hle/service/am/cradle_firmware_updater.h
Normal file
25
src/core/hle/service/am/cradle_firmware_updater.h
Normal file
@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
class ICradleFirmwareUpdater final : public ServiceFramework<ICradleFirmwareUpdater> {
|
||||
public:
|
||||
explicit ICradleFirmwareUpdater(Core::System& system_);
|
||||
~ICradleFirmwareUpdater() override;
|
||||
|
||||
private:
|
||||
void FinishUpdate(HLERequestContext& ctx);
|
||||
void GetCradleDeviceInfo(HLERequestContext& ctx);
|
||||
void GetCradleDeviceInfoChangeEvent(HLERequestContext& ctx);
|
||||
|
||||
Kernel::KEvent* cradle_device_info_event;
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
162
src/core/hle/service/am/event_observer.cpp
Normal file
162
src/core/hle/service/am/event_observer.cpp
Normal file
@ -0,0 +1,162 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/event_observer.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
enum class UserDataTag : u32 {
|
||||
WakeupEvent,
|
||||
AppletProcess,
|
||||
};
|
||||
|
||||
EventObserver::EventObserver(Core::System& system, WindowSystem& window_system)
|
||||
: m_system(system), m_context(system, "am:EventObserver"), m_window_system(window_system),
|
||||
m_wakeup_event(m_context), m_wakeup_holder(m_wakeup_event.GetHandle()) {
|
||||
m_window_system.SetEventObserver(this);
|
||||
m_wakeup_holder.SetUserData(static_cast<uintptr_t>(UserDataTag::WakeupEvent));
|
||||
m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait));
|
||||
m_thread = std::thread([&] { this->ThreadFunc(); });
|
||||
}
|
||||
|
||||
EventObserver::~EventObserver() {
|
||||
// Signal thread and wait for processing to finish.
|
||||
m_stop_source.request_stop();
|
||||
m_wakeup_event.Signal();
|
||||
m_thread.join();
|
||||
|
||||
// Free remaining owned sessions.
|
||||
auto it = m_process_holder_list.begin();
|
||||
while (it != m_process_holder_list.end()) {
|
||||
// Get the holder.
|
||||
auto* const holder = std::addressof(*it);
|
||||
|
||||
// Remove from the list.
|
||||
it = m_process_holder_list.erase(it);
|
||||
|
||||
// Free the holder.
|
||||
delete holder;
|
||||
}
|
||||
}
|
||||
|
||||
void EventObserver::TrackAppletProcess(Applet& applet) {
|
||||
// Don't observe dummy processes.
|
||||
if (!applet.process->IsInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate new holder.
|
||||
auto* holder = new ProcessHolder(applet, *applet.process);
|
||||
holder->SetUserData(static_cast<uintptr_t>(UserDataTag::AppletProcess));
|
||||
|
||||
// Insert into list.
|
||||
{
|
||||
std::scoped_lock lk{m_lock};
|
||||
m_process_holder_list.push_back(*holder);
|
||||
holder->LinkToMultiWait(std::addressof(m_deferred_wait_list));
|
||||
}
|
||||
|
||||
// Signal wakeup.
|
||||
m_wakeup_event.Signal();
|
||||
}
|
||||
|
||||
void EventObserver::RequestUpdate() {
|
||||
m_wakeup_event.Signal();
|
||||
}
|
||||
|
||||
void EventObserver::LinkDeferred() {
|
||||
std::scoped_lock lk{m_lock};
|
||||
m_multi_wait.MoveAll(std::addressof(m_deferred_wait_list));
|
||||
}
|
||||
|
||||
MultiWaitHolder* EventObserver::WaitSignaled() {
|
||||
while (true) {
|
||||
this->LinkDeferred();
|
||||
|
||||
// If we're done, return before we start waiting.
|
||||
if (m_stop_source.stop_requested()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* selected = m_multi_wait.WaitAny(m_system.Kernel());
|
||||
if (selected != std::addressof(m_wakeup_holder)) {
|
||||
// Unlink the process.
|
||||
selected->UnlinkFromMultiWait();
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
}
|
||||
|
||||
void EventObserver::Process(MultiWaitHolder* holder) {
|
||||
switch (static_cast<UserDataTag>(holder->GetUserData())) {
|
||||
case UserDataTag::WakeupEvent:
|
||||
this->OnWakeupEvent(holder);
|
||||
break;
|
||||
case UserDataTag::AppletProcess:
|
||||
this->OnProcessEvent(static_cast<ProcessHolder*>(holder));
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void EventObserver::OnWakeupEvent(MultiWaitHolder* holder) {
|
||||
m_wakeup_event.Clear();
|
||||
|
||||
// Perform recalculation.
|
||||
m_window_system.Update();
|
||||
}
|
||||
|
||||
void EventObserver::OnProcessEvent(ProcessHolder* holder) {
|
||||
// Check process state.
|
||||
auto& applet = holder->GetApplet();
|
||||
auto& process = holder->GetProcess();
|
||||
|
||||
{
|
||||
std::scoped_lock lk{m_lock, applet.lock};
|
||||
if (process.IsTerminated()) {
|
||||
// Destroy the holder.
|
||||
this->DestroyAppletProcessHolderLocked(holder);
|
||||
} else {
|
||||
// Reset signaled state.
|
||||
process.ResetSignal();
|
||||
|
||||
// Relink wakeup event.
|
||||
holder->LinkToMultiWait(std::addressof(m_deferred_wait_list));
|
||||
}
|
||||
|
||||
// Set running.
|
||||
applet.is_process_running = process.IsRunning();
|
||||
}
|
||||
|
||||
// Perform recalculation.
|
||||
m_window_system.Update();
|
||||
}
|
||||
|
||||
void EventObserver::DestroyAppletProcessHolderLocked(ProcessHolder* holder) {
|
||||
// Remove from owned list.
|
||||
m_process_holder_list.erase(m_process_holder_list.iterator_to(*holder));
|
||||
|
||||
// Destroy and free.
|
||||
delete holder;
|
||||
}
|
||||
|
||||
void EventObserver::ThreadFunc() {
|
||||
Common::SetCurrentThreadName("am:EventObserver");
|
||||
|
||||
while (true) {
|
||||
auto* signaled_holder = this->WaitSignaled();
|
||||
if (!signaled_holder) {
|
||||
break;
|
||||
}
|
||||
|
||||
this->Process(signaled_holder);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
74
src/core/hle/service/am/event_observer.h
Normal file
74
src/core/hle/service/am/event_observer.h
Normal file
@ -0,0 +1,74 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/os/event.h"
|
||||
#include "core/hle/service/os/multi_wait.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
struct Applet;
|
||||
class ProcessHolder;
|
||||
class WindowSystem;
|
||||
|
||||
class EventObserver {
|
||||
public:
|
||||
explicit EventObserver(Core::System& system, WindowSystem& window_system);
|
||||
~EventObserver();
|
||||
|
||||
void TrackAppletProcess(Applet& applet);
|
||||
void RequestUpdate();
|
||||
|
||||
private:
|
||||
void LinkDeferred();
|
||||
MultiWaitHolder* WaitSignaled();
|
||||
void Process(MultiWaitHolder* holder);
|
||||
bool WaitAndProcessImpl();
|
||||
void LoopProcess();
|
||||
|
||||
private:
|
||||
void OnWakeupEvent(MultiWaitHolder* holder);
|
||||
void OnProcessEvent(ProcessHolder* holder);
|
||||
|
||||
private:
|
||||
void DestroyAppletProcessHolderLocked(ProcessHolder* holder);
|
||||
|
||||
private:
|
||||
void ThreadFunc();
|
||||
|
||||
private:
|
||||
// System reference and context.
|
||||
Core::System& m_system;
|
||||
KernelHelpers::ServiceContext m_context;
|
||||
|
||||
// Window manager.
|
||||
WindowSystem& m_window_system;
|
||||
|
||||
// Guest event handle to wake up the event loop processor.
|
||||
Event m_wakeup_event;
|
||||
MultiWaitHolder m_wakeup_holder;
|
||||
|
||||
// Mutex to protect remaining members.
|
||||
std::mutex m_lock{};
|
||||
|
||||
// List of owned process holders.
|
||||
Common::IntrusiveListBaseTraits<ProcessHolder>::ListType m_process_holder_list;
|
||||
|
||||
// Multi-wait objects for new tasks.
|
||||
MultiWait m_multi_wait;
|
||||
MultiWait m_deferred_wait_list;
|
||||
|
||||
// Processing thread.
|
||||
std::thread m_thread{};
|
||||
std::stop_source m_stop_source{};
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
@ -69,7 +69,11 @@ void FrontendApplet::PushInteractiveOutData(std::shared_ptr<IStorage> storage) {
|
||||
}
|
||||
|
||||
void FrontendApplet::Exit() {
|
||||
applet.lock()->caller_applet_broker->SignalCompletion();
|
||||
auto applet_ = applet.lock();
|
||||
|
||||
std::scoped_lock lk{applet_->lock};
|
||||
applet_->is_completed = true;
|
||||
applet_->state_changed_event.Signal();
|
||||
}
|
||||
|
||||
FrontendAppletSet::FrontendAppletSet() = default;
|
||||
|
@ -3,24 +3,28 @@
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/am/hid_registration.h"
|
||||
#include "core/hle/service/am/process.h"
|
||||
#include "core/hle/service/hid/hid_server.h"
|
||||
#include "core/hle/service/os/process.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "hid_core/resource_manager.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
HidRegistration::HidRegistration(Core::System& system, Process& process) : m_process(process) {
|
||||
m_hid_server = system.ServiceManager().GetService<HID::IHidServer>("hid");
|
||||
m_hid_server = system.ServiceManager().GetService<HID::IHidServer>("hid", true);
|
||||
|
||||
if (m_process.IsInitialized()) {
|
||||
m_hid_server->GetResourceManager()->RegisterAppletResourceUserId(m_process.GetProcessId(),
|
||||
true);
|
||||
m_hid_server->GetResourceManager()->SetAruidValidForVibration(m_process.GetProcessId(),
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
HidRegistration::~HidRegistration() {
|
||||
if (m_process.IsInitialized()) {
|
||||
m_hid_server->GetResourceManager()->SetAruidValidForVibration(m_process.GetProcessId(),
|
||||
false);
|
||||
m_hid_server->GetResourceManager()->UnregisterAppletResourceUserId(
|
||||
m_process.GetProcessId());
|
||||
}
|
||||
@ -28,6 +32,8 @@ HidRegistration::~HidRegistration() {
|
||||
|
||||
void HidRegistration::EnableAppletToGetInput(bool enable) {
|
||||
if (m_process.IsInitialized()) {
|
||||
m_hid_server->GetResourceManager()->SetAruidValidForVibration(m_process.GetProcessId(),
|
||||
enable);
|
||||
m_hid_server->GetResourceManager()->EnableInput(m_process.GetProcessId(), enable);
|
||||
}
|
||||
}
|
||||
|
@ -13,9 +13,11 @@ namespace Service::HID {
|
||||
class IHidServer;
|
||||
}
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
namespace Service {
|
||||
class Process;
|
||||
}
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
class HidRegistration {
|
||||
public:
|
||||
|
379
src/core/hle/service/am/lifecycle_manager.cpp
Normal file
379
src/core/hle/service/am/lifecycle_manager.cpp
Normal file
@ -0,0 +1,379 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/hle/service/am/lifecycle_manager.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
LifecycleManager::LifecycleManager(Core::System& system, KernelHelpers::ServiceContext& context,
|
||||
bool is_application)
|
||||
: m_system_event(context), m_operation_mode_changed_system_event(context),
|
||||
m_is_application(is_application) {}
|
||||
|
||||
LifecycleManager::~LifecycleManager() = default;
|
||||
|
||||
Event& LifecycleManager::GetSystemEvent() {
|
||||
return m_system_event;
|
||||
}
|
||||
|
||||
Event& LifecycleManager::GetOperationModeChangedSystemEvent() {
|
||||
return m_operation_mode_changed_system_event;
|
||||
}
|
||||
|
||||
void LifecycleManager::PushUnorderedMessage(AppletMessage message) {
|
||||
m_unordered_messages.push_back(message);
|
||||
this->SignalSystemEventIfNeeded();
|
||||
}
|
||||
|
||||
AppletMessage LifecycleManager::PopMessageInOrderOfPriority() {
|
||||
if (m_has_resume) {
|
||||
m_has_resume = false;
|
||||
return AppletMessage::Resume;
|
||||
}
|
||||
|
||||
if (m_has_acknowledged_exit != m_has_requested_exit) {
|
||||
m_has_acknowledged_exit = m_has_requested_exit;
|
||||
return AppletMessage::Exit;
|
||||
}
|
||||
|
||||
if (m_focus_state_changed_notification_enabled) {
|
||||
if (!m_is_application) {
|
||||
if (m_requested_focus_state != m_acknowledged_focus_state) {
|
||||
m_acknowledged_focus_state = m_requested_focus_state;
|
||||
switch (m_requested_focus_state) {
|
||||
case FocusState::InFocus:
|
||||
return AppletMessage::ChangeIntoForeground;
|
||||
case FocusState::NotInFocus:
|
||||
return AppletMessage::ChangeIntoBackground;
|
||||
default:
|
||||
ASSERT(false);
|
||||
}
|
||||
}
|
||||
} else if (m_has_focus_state_changed) {
|
||||
m_has_focus_state_changed = false;
|
||||
return AppletMessage::FocusStateChanged;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_has_requested_request_to_prepare_sleep != m_has_acknowledged_request_to_prepare_sleep) {
|
||||
m_has_acknowledged_request_to_prepare_sleep = true;
|
||||
return AppletMessage::RequestToPrepareSleep;
|
||||
}
|
||||
|
||||
if (m_requested_request_to_display_state != m_acknowledged_request_to_display_state) {
|
||||
m_acknowledged_request_to_display_state = m_requested_request_to_display_state;
|
||||
return AppletMessage::RequestToDisplay;
|
||||
}
|
||||
|
||||
if (m_has_operation_mode_changed) {
|
||||
m_has_operation_mode_changed = false;
|
||||
return AppletMessage::OperationModeChanged;
|
||||
}
|
||||
|
||||
if (m_has_performance_mode_changed) {
|
||||
m_has_performance_mode_changed = false;
|
||||
return AppletMessage::PerformanceModeChanged;
|
||||
}
|
||||
|
||||
if (m_has_sd_card_removed) {
|
||||
m_has_sd_card_removed = false;
|
||||
return AppletMessage::SdCardRemoved;
|
||||
}
|
||||
|
||||
if (m_has_sleep_required_by_high_temperature) {
|
||||
m_has_sleep_required_by_high_temperature = false;
|
||||
return AppletMessage::SleepRequiredByHighTemperature;
|
||||
}
|
||||
|
||||
if (m_has_sleep_required_by_low_battery) {
|
||||
m_has_sleep_required_by_low_battery = false;
|
||||
return AppletMessage::SleepRequiredByLowBattery;
|
||||
}
|
||||
|
||||
if (m_has_auto_power_down) {
|
||||
m_has_auto_power_down = false;
|
||||
return AppletMessage::AutoPowerDown;
|
||||
}
|
||||
|
||||
if (m_has_album_screen_shot_taken) {
|
||||
m_has_album_screen_shot_taken = false;
|
||||
return AppletMessage::AlbumScreenShotTaken;
|
||||
}
|
||||
|
||||
if (m_has_album_recording_saved) {
|
||||
m_has_album_recording_saved = false;
|
||||
return AppletMessage::AlbumRecordingSaved;
|
||||
}
|
||||
|
||||
if (!m_unordered_messages.empty()) {
|
||||
const auto message = m_unordered_messages.front();
|
||||
m_unordered_messages.pop_front();
|
||||
return message;
|
||||
}
|
||||
|
||||
return AppletMessage::None;
|
||||
}
|
||||
|
||||
bool LifecycleManager::ShouldSignalSystemEvent() {
|
||||
if (m_focus_state_changed_notification_enabled) {
|
||||
if (!m_is_application) {
|
||||
if (m_requested_focus_state != m_acknowledged_focus_state) {
|
||||
return true;
|
||||
}
|
||||
} else if (m_has_focus_state_changed) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return !m_unordered_messages.empty() || m_has_resume ||
|
||||
(m_has_requested_exit != m_has_acknowledged_exit) ||
|
||||
(m_has_requested_request_to_prepare_sleep !=
|
||||
m_has_acknowledged_request_to_prepare_sleep) ||
|
||||
m_has_operation_mode_changed || m_has_performance_mode_changed ||
|
||||
m_has_sd_card_removed || m_has_sleep_required_by_high_temperature ||
|
||||
m_has_sleep_required_by_low_battery || m_has_auto_power_down ||
|
||||
(m_requested_request_to_display_state != m_acknowledged_request_to_display_state) ||
|
||||
m_has_album_screen_shot_taken || m_has_album_recording_saved;
|
||||
}
|
||||
|
||||
void LifecycleManager::OnOperationAndPerformanceModeChanged() {
|
||||
if (m_operation_mode_changed_notification_enabled) {
|
||||
m_has_operation_mode_changed = true;
|
||||
}
|
||||
if (m_performance_mode_changed_notification_enabled) {
|
||||
m_has_performance_mode_changed = true;
|
||||
}
|
||||
m_operation_mode_changed_system_event.Signal();
|
||||
this->SignalSystemEventIfNeeded();
|
||||
}
|
||||
|
||||
void LifecycleManager::SignalSystemEventIfNeeded() {
|
||||
// Check our cached value for the system event.
|
||||
const bool applet_message_available = m_applet_message_available;
|
||||
|
||||
// If it's not current, we need to do an update, either clearing or signaling.
|
||||
if (applet_message_available != this->ShouldSignalSystemEvent()) {
|
||||
if (!applet_message_available) {
|
||||
m_system_event.Signal();
|
||||
m_applet_message_available = true;
|
||||
} else {
|
||||
m_system_event.Clear();
|
||||
m_applet_message_available = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LifecycleManager::PopMessage(AppletMessage* out_message) {
|
||||
const auto message = this->PopMessageInOrderOfPriority();
|
||||
this->SignalSystemEventIfNeeded();
|
||||
|
||||
*out_message = message;
|
||||
return message != AppletMessage::None;
|
||||
}
|
||||
|
||||
void LifecycleManager::SetFocusHandlingMode(bool suspend) {
|
||||
switch (m_focus_handling_mode) {
|
||||
case FocusHandlingMode::AlwaysSuspend:
|
||||
case FocusHandlingMode::SuspendHomeSleep:
|
||||
if (!suspend) {
|
||||
// Disallow suspension.
|
||||
m_focus_handling_mode = FocusHandlingMode::NoSuspend;
|
||||
}
|
||||
break;
|
||||
case FocusHandlingMode::NoSuspend:
|
||||
if (suspend) {
|
||||
// Allow suspension temporally.
|
||||
m_focus_handling_mode = FocusHandlingMode::SuspendHomeSleep;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void LifecycleManager::SetOutOfFocusSuspendingEnabled(bool enabled) {
|
||||
switch (m_focus_handling_mode) {
|
||||
case FocusHandlingMode::AlwaysSuspend:
|
||||
if (!enabled) {
|
||||
// Allow suspension temporally.
|
||||
m_focus_handling_mode = FocusHandlingMode::SuspendHomeSleep;
|
||||
}
|
||||
break;
|
||||
case FocusHandlingMode::SuspendHomeSleep:
|
||||
case FocusHandlingMode::NoSuspend:
|
||||
if (enabled) {
|
||||
// Allow suspension.
|
||||
m_focus_handling_mode = FocusHandlingMode::AlwaysSuspend;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void LifecycleManager::RemoveForceResumeIfPossible() {
|
||||
// If resume is not forced, we have nothing to do.
|
||||
if (m_suspend_mode != SuspendMode::ForceResume) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check activity state.
|
||||
// If we are already resumed, we can remove the forced state.
|
||||
switch (m_activity_state) {
|
||||
case ActivityState::ForegroundVisible:
|
||||
case ActivityState::ForegroundObscured:
|
||||
m_suspend_mode = SuspendMode::NoOverride;
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Check focus handling mode.
|
||||
switch (m_focus_handling_mode) {
|
||||
case FocusHandlingMode::AlwaysSuspend:
|
||||
case FocusHandlingMode::SuspendHomeSleep:
|
||||
// If the applet allows suspension, we can remove the forced state.
|
||||
m_suspend_mode = SuspendMode::NoOverride;
|
||||
break;
|
||||
|
||||
case FocusHandlingMode::NoSuspend:
|
||||
// If the applet is not an application, we can remove the forced state.
|
||||
// Only applications can be forced to resume.
|
||||
if (!m_is_application) {
|
||||
m_suspend_mode = SuspendMode::NoOverride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LifecycleManager::IsRunnable() const {
|
||||
// If suspend is forced, return that.
|
||||
if (m_forced_suspend) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check suspend mode override.
|
||||
switch (m_suspend_mode) {
|
||||
case SuspendMode::NoOverride:
|
||||
// Continue processing.
|
||||
break;
|
||||
|
||||
case SuspendMode::ForceResume:
|
||||
// The applet is runnable during forced resumption when its exit is requested.
|
||||
return m_has_requested_exit;
|
||||
|
||||
case SuspendMode::ForceSuspend:
|
||||
// The applet is never runnable during forced suspension.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Always run if exit is requested.
|
||||
if (m_has_requested_exit) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_activity_state == ActivityState::ForegroundVisible) {
|
||||
// The applet is runnable now.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_activity_state == ActivityState::ForegroundObscured) {
|
||||
switch (m_focus_handling_mode) {
|
||||
case FocusHandlingMode::AlwaysSuspend:
|
||||
// The applet is not runnable while running the applet.
|
||||
return false;
|
||||
|
||||
case FocusHandlingMode::SuspendHomeSleep:
|
||||
// The applet is runnable while running the applet.
|
||||
return true;
|
||||
|
||||
case FocusHandlingMode::NoSuspend:
|
||||
// The applet is always runnable.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// The activity is a suspended one.
|
||||
// The applet should be suspended unless it has disabled suspension.
|
||||
return m_focus_handling_mode == FocusHandlingMode::NoSuspend;
|
||||
}
|
||||
|
||||
FocusState LifecycleManager::GetFocusStateWhileForegroundObscured() const {
|
||||
switch (m_focus_handling_mode) {
|
||||
case FocusHandlingMode::AlwaysSuspend:
|
||||
// The applet never learns it has lost focus.
|
||||
return FocusState::InFocus;
|
||||
|
||||
case FocusHandlingMode::SuspendHomeSleep:
|
||||
// The applet learns it has lost focus when launching a child applet.
|
||||
return FocusState::NotInFocus;
|
||||
|
||||
case FocusHandlingMode::NoSuspend:
|
||||
// The applet always learns it has lost focus.
|
||||
return FocusState::NotInFocus;
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
FocusState LifecycleManager::GetFocusStateWhileBackground(bool is_obscured) const {
|
||||
switch (m_focus_handling_mode) {
|
||||
case FocusHandlingMode::AlwaysSuspend:
|
||||
// The applet never learns it has lost focus.
|
||||
return FocusState::InFocus;
|
||||
|
||||
case FocusHandlingMode::SuspendHomeSleep:
|
||||
// The applet learns it has lost focus when launching a child applet.
|
||||
return is_obscured ? FocusState::NotInFocus : FocusState::InFocus;
|
||||
|
||||
case FocusHandlingMode::NoSuspend:
|
||||
// The applet always learns it has lost focus.
|
||||
return m_is_application ? FocusState::Background : FocusState::NotInFocus;
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
bool LifecycleManager::UpdateRequestedFocusState() {
|
||||
FocusState new_state{};
|
||||
|
||||
if (m_suspend_mode == SuspendMode::NoOverride) {
|
||||
// With no forced suspend or resume, we take the focus state designated
|
||||
// by the combination of the activity flag and the focus handling mode.
|
||||
switch (m_activity_state) {
|
||||
case ActivityState::ForegroundVisible:
|
||||
new_state = FocusState::InFocus;
|
||||
break;
|
||||
|
||||
case ActivityState::ForegroundObscured:
|
||||
new_state = this->GetFocusStateWhileForegroundObscured();
|
||||
break;
|
||||
|
||||
case ActivityState::BackgroundVisible:
|
||||
new_state = this->GetFocusStateWhileBackground(false);
|
||||
break;
|
||||
|
||||
case ActivityState::BackgroundObscured:
|
||||
new_state = this->GetFocusStateWhileBackground(true);
|
||||
break;
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
} else {
|
||||
// With forced suspend or resume, the applet is guaranteed to be background.
|
||||
new_state = this->GetFocusStateWhileBackground(false);
|
||||
}
|
||||
|
||||
if (new_state != m_requested_focus_state) {
|
||||
// Mark the focus state as ready for update.
|
||||
m_requested_focus_state = new_state;
|
||||
|
||||
// We changed the focus state.
|
||||
return true;
|
||||
}
|
||||
|
||||
// We didn't change the focus state.
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
183
src/core/hle/service/am/lifecycle_manager.h
Normal file
183
src/core/hle/service/am/lifecycle_manager.h
Normal file
@ -0,0 +1,183 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "core/hle/service/am/am_types.h"
|
||||
#include "core/hle/service/os/event.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
enum class ActivityState : u32 {
|
||||
ForegroundVisible = 0,
|
||||
ForegroundObscured = 1,
|
||||
BackgroundVisible = 2,
|
||||
BackgroundObscured = 3,
|
||||
};
|
||||
|
||||
enum class FocusHandlingMode : u32 {
|
||||
AlwaysSuspend = 0,
|
||||
SuspendHomeSleep = 1,
|
||||
NoSuspend = 2,
|
||||
};
|
||||
|
||||
enum class SuspendMode : u32 {
|
||||
NoOverride = 0,
|
||||
ForceResume = 1,
|
||||
ForceSuspend = 2,
|
||||
};
|
||||
|
||||
class LifecycleManager {
|
||||
public:
|
||||
explicit LifecycleManager(Core::System& system, KernelHelpers::ServiceContext& context,
|
||||
bool is_application);
|
||||
~LifecycleManager();
|
||||
|
||||
public:
|
||||
Event& GetSystemEvent();
|
||||
Event& GetOperationModeChangedSystemEvent();
|
||||
|
||||
public:
|
||||
bool IsApplication() {
|
||||
return m_is_application;
|
||||
}
|
||||
|
||||
bool GetForcedSuspend() {
|
||||
return m_forced_suspend;
|
||||
}
|
||||
|
||||
bool GetExitRequested() {
|
||||
return m_has_requested_exit;
|
||||
}
|
||||
|
||||
ActivityState GetActivityState() {
|
||||
return m_activity_state;
|
||||
}
|
||||
|
||||
FocusState GetAndClearFocusState() {
|
||||
m_acknowledged_focus_state = m_requested_focus_state;
|
||||
return m_acknowledged_focus_state;
|
||||
}
|
||||
|
||||
void SetFocusState(FocusState state) {
|
||||
if (m_requested_focus_state != state) {
|
||||
m_has_focus_state_changed = true;
|
||||
}
|
||||
m_requested_focus_state = state;
|
||||
this->SignalSystemEventIfNeeded();
|
||||
}
|
||||
|
||||
void RequestExit() {
|
||||
m_has_requested_exit = true;
|
||||
this->SignalSystemEventIfNeeded();
|
||||
}
|
||||
|
||||
void RequestResumeNotification() {
|
||||
// NOTE: this appears to be a bug in am.
|
||||
// If an applet makes a concurrent request to receive resume notifications
|
||||
// while it is being suspended, the first resume notification will be lost.
|
||||
// This is not the case with other notification types.
|
||||
if (m_resume_notification_enabled) {
|
||||
m_has_resume = true;
|
||||
}
|
||||
}
|
||||
|
||||
void OnOperationAndPerformanceModeChanged();
|
||||
|
||||
public:
|
||||
void SetFocusStateChangedNotificationEnabled(bool enabled) {
|
||||
m_focus_state_changed_notification_enabled = enabled;
|
||||
this->SignalSystemEventIfNeeded();
|
||||
}
|
||||
|
||||
void SetOperationModeChangedNotificationEnabled(bool enabled) {
|
||||
m_operation_mode_changed_notification_enabled = enabled;
|
||||
this->SignalSystemEventIfNeeded();
|
||||
}
|
||||
|
||||
void SetPerformanceModeChangedNotificationEnabled(bool enabled) {
|
||||
m_performance_mode_changed_notification_enabled = enabled;
|
||||
this->SignalSystemEventIfNeeded();
|
||||
}
|
||||
|
||||
void SetResumeNotificationEnabled(bool enabled) {
|
||||
m_resume_notification_enabled = enabled;
|
||||
}
|
||||
|
||||
void SetActivityState(ActivityState state) {
|
||||
m_activity_state = state;
|
||||
}
|
||||
|
||||
void SetSuspendMode(SuspendMode mode) {
|
||||
m_suspend_mode = mode;
|
||||
}
|
||||
|
||||
void SetForcedSuspend(bool enabled) {
|
||||
m_forced_suspend = enabled;
|
||||
}
|
||||
|
||||
public:
|
||||
void SetFocusHandlingMode(bool suspend);
|
||||
void SetOutOfFocusSuspendingEnabled(bool enabled);
|
||||
void RemoveForceResumeIfPossible();
|
||||
bool IsRunnable() const;
|
||||
bool UpdateRequestedFocusState();
|
||||
void SignalSystemEventIfNeeded();
|
||||
|
||||
public:
|
||||
void PushUnorderedMessage(AppletMessage message);
|
||||
bool PopMessage(AppletMessage* out_message);
|
||||
|
||||
private:
|
||||
FocusState GetFocusStateWhileForegroundObscured() const;
|
||||
FocusState GetFocusStateWhileBackground(bool is_obscured) const;
|
||||
|
||||
private:
|
||||
AppletMessage PopMessageInOrderOfPriority();
|
||||
bool ShouldSignalSystemEvent();
|
||||
|
||||
private:
|
||||
Event m_system_event;
|
||||
Event m_operation_mode_changed_system_event;
|
||||
|
||||
std::list<AppletMessage> m_unordered_messages{};
|
||||
|
||||
bool m_is_application{};
|
||||
bool m_focus_state_changed_notification_enabled{true};
|
||||
bool m_operation_mode_changed_notification_enabled{true};
|
||||
bool m_performance_mode_changed_notification_enabled{true};
|
||||
bool m_resume_notification_enabled{};
|
||||
|
||||
bool m_requested_request_to_display_state{};
|
||||
bool m_acknowledged_request_to_display_state{};
|
||||
bool m_has_resume{};
|
||||
bool m_has_focus_state_changed{true};
|
||||
bool m_has_album_recording_saved{};
|
||||
bool m_has_album_screen_shot_taken{};
|
||||
bool m_has_auto_power_down{};
|
||||
bool m_has_sleep_required_by_low_battery{};
|
||||
bool m_has_sleep_required_by_high_temperature{};
|
||||
bool m_has_sd_card_removed{};
|
||||
bool m_has_performance_mode_changed{};
|
||||
bool m_has_operation_mode_changed{};
|
||||
bool m_has_requested_request_to_prepare_sleep{};
|
||||
bool m_has_acknowledged_request_to_prepare_sleep{};
|
||||
bool m_has_requested_exit{};
|
||||
bool m_has_acknowledged_exit{};
|
||||
bool m_applet_message_available{};
|
||||
|
||||
bool m_forced_suspend{};
|
||||
FocusHandlingMode m_focus_handling_mode{FocusHandlingMode::SuspendHomeSleep};
|
||||
ActivityState m_activity_state{ActivityState::ForegroundVisible};
|
||||
SuspendMode m_suspend_mode{SuspendMode::NoOverride};
|
||||
FocusState m_requested_focus_state{};
|
||||
FocusState m_acknowledged_focus_state{};
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
130
src/core/hle/service/am/process_creation.cpp
Normal file
130
src/core/hle/service/am/process_creation.cpp
Normal file
@ -0,0 +1,130 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/hle/service/am/process_creation.h"
|
||||
#include "core/hle/service/glue/glue_manager.h"
|
||||
#include "core/hle/service/os/process.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
namespace {
|
||||
|
||||
FileSys::StorageId GetStorageIdForFrontendSlot(
|
||||
std::optional<FileSys::ContentProviderUnionSlot> slot) {
|
||||
if (!slot.has_value()) {
|
||||
return FileSys::StorageId::None;
|
||||
}
|
||||
|
||||
switch (*slot) {
|
||||
case FileSys::ContentProviderUnionSlot::UserNAND:
|
||||
return FileSys::StorageId::NandUser;
|
||||
case FileSys::ContentProviderUnionSlot::SysNAND:
|
||||
return FileSys::StorageId::NandSystem;
|
||||
case FileSys::ContentProviderUnionSlot::SDMC:
|
||||
return FileSys::StorageId::SdCard;
|
||||
case FileSys::ContentProviderUnionSlot::FrontendManual:
|
||||
return FileSys::StorageId::Host;
|
||||
default:
|
||||
return FileSys::StorageId::None;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Process> CreateProcessImpl(std::unique_ptr<Loader::AppLoader>& out_loader,
|
||||
Loader::ResultStatus& out_load_result,
|
||||
Core::System& system, FileSys::VirtualFile file,
|
||||
u64 program_id, u64 program_index) {
|
||||
// Get the appropriate loader to parse this NCA.
|
||||
out_loader = Loader::GetLoader(system, file, program_id, program_index);
|
||||
|
||||
// Ensure we have a loader which can parse the NCA.
|
||||
if (!out_loader) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Try to load the process.
|
||||
auto process = std::make_unique<Process>(system);
|
||||
if (process->Initialize(*out_loader, out_load_result)) {
|
||||
return process;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
|
||||
u8 minimum_key_generation, u8 maximum_key_generation) {
|
||||
// Attempt to load program NCA.
|
||||
FileSys::VirtualFile nca_raw{};
|
||||
|
||||
// Get the program NCA from storage.
|
||||
auto& storage = system.GetContentProviderUnion();
|
||||
nca_raw = storage.GetEntryRaw(program_id, FileSys::ContentRecordType::Program);
|
||||
|
||||
// Ensure we retrieved a program NCA.
|
||||
if (!nca_raw) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ensure we have a suitable version.
|
||||
if (minimum_key_generation > 0) {
|
||||
FileSys::NCA nca(nca_raw);
|
||||
if (nca.GetStatus() == Loader::ResultStatus::Success &&
|
||||
(nca.GetKeyGeneration() < minimum_key_generation ||
|
||||
nca.GetKeyGeneration() > maximum_key_generation)) {
|
||||
LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id,
|
||||
nca.GetKeyGeneration());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Loader::AppLoader> loader;
|
||||
Loader::ResultStatus status;
|
||||
return CreateProcessImpl(loader, status, system, nca_raw, program_id, 0);
|
||||
}
|
||||
|
||||
std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control,
|
||||
std::unique_ptr<Loader::AppLoader>& out_loader,
|
||||
Loader::ResultStatus& out_load_result,
|
||||
Core::System& system, FileSys::VirtualFile file,
|
||||
u64 program_id, u64 program_index) {
|
||||
auto process =
|
||||
CreateProcessImpl(out_loader, out_load_result, system, file, program_id, program_index);
|
||||
if (!process) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FileSys::NACP nacp;
|
||||
if (out_loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
|
||||
out_control = nacp.GetRawBytes();
|
||||
} else {
|
||||
out_control.resize(sizeof(FileSys::RawNACP));
|
||||
}
|
||||
|
||||
auto& storage = system.GetContentProviderUnion();
|
||||
Service::Glue::ApplicationLaunchProperty launch{};
|
||||
launch.title_id = process->GetProgramId();
|
||||
|
||||
FileSys::PatchManager pm{launch.title_id, system.GetFileSystemController(), storage};
|
||||
launch.version = pm.GetGameVersion().value_or(0);
|
||||
|
||||
// TODO(DarkLordZach): When FSController/Game Card Support is added, if
|
||||
// current_process_game_card use correct StorageId
|
||||
launch.base_game_storage_id = GetStorageIdForFrontendSlot(
|
||||
storage.GetSlotForEntry(launch.title_id, FileSys::ContentRecordType::Program));
|
||||
launch.update_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(
|
||||
FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program));
|
||||
|
||||
system.GetARPManager().Register(launch.title_id, launch, out_control);
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
35
src/core/hle/service/am/process_creation.h
Normal file
35
src/core/hle/service/am/process_creation.h
Normal file
@ -0,0 +1,35 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs/vfs_types.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Loader {
|
||||
class AppLoader;
|
||||
enum class ResultStatus : u16;
|
||||
} // namespace Loader
|
||||
|
||||
namespace Service {
|
||||
class Process;
|
||||
}
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
|
||||
u8 minimum_key_generation, u8 maximum_key_generation);
|
||||
std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control,
|
||||
std::unique_ptr<Loader::AppLoader>& out_loader,
|
||||
Loader::ResultStatus& out_load_result,
|
||||
Core::System& system, FileSys::VirtualFile file,
|
||||
u64 program_id, u64 program_index);
|
||||
|
||||
} // namespace Service::AM
|
15
src/core/hle/service/am/process_holder.cpp
Normal file
15
src/core/hle/service/am/process_holder.cpp
Normal file
@ -0,0 +1,15 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/service/am/process_holder.h"
|
||||
#include "core/hle/service/os/process.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
ProcessHolder::ProcessHolder(Applet& applet, Process& process)
|
||||
: MultiWaitHolder(process.GetHandle()), m_applet(applet), m_process(process) {}
|
||||
|
||||
ProcessHolder::~ProcessHolder() = default;
|
||||
|
||||
} // namespace Service::AM
|
34
src/core/hle/service/am/process_holder.h
Normal file
34
src/core/hle/service/am/process_holder.h
Normal file
@ -0,0 +1,34 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/os/multi_wait_holder.h"
|
||||
|
||||
namespace Service {
|
||||
class Process;
|
||||
}
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
struct Applet;
|
||||
|
||||
class ProcessHolder : public MultiWaitHolder, public Common::IntrusiveListBaseNode<ProcessHolder> {
|
||||
public:
|
||||
explicit ProcessHolder(Applet& applet, Process& process);
|
||||
~ProcessHolder();
|
||||
|
||||
Applet& GetApplet() const {
|
||||
return m_applet;
|
||||
}
|
||||
|
||||
Process& GetProcess() const {
|
||||
return m_process;
|
||||
}
|
||||
|
||||
private:
|
||||
Applet& m_applet;
|
||||
Process& m_process;
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
@ -6,12 +6,14 @@
|
||||
#include "core/hle/service/am/service/all_system_applet_proxies_service.h"
|
||||
#include "core/hle/service/am/service/library_applet_proxy.h"
|
||||
#include "core/hle/service/am/service/system_applet_proxy.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
IAllSystemAppletProxiesService::IAllSystemAppletProxiesService(Core::System& system_)
|
||||
: ServiceFramework{system_, "appletAE"} {
|
||||
IAllSystemAppletProxiesService::IAllSystemAppletProxiesService(Core::System& system_,
|
||||
WindowSystem& window_system)
|
||||
: ServiceFramework{system_, "appletAE"}, m_window_system{window_system} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{100, D<&IAllSystemAppletProxiesService::OpenSystemAppletProxy>, "OpenSystemAppletProxy"},
|
||||
@ -36,8 +38,8 @@ Result IAllSystemAppletProxiesService::OpenSystemAppletProxy(
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
|
||||
*out_system_applet_proxy =
|
||||
std::make_shared<ISystemAppletProxy>(system, applet, process_handle.Get());
|
||||
*out_system_applet_proxy = std::make_shared<ISystemAppletProxy>(
|
||||
system, applet, process_handle.Get(), m_window_system);
|
||||
R_SUCCEED();
|
||||
} else {
|
||||
UNIMPLEMENTED();
|
||||
@ -52,8 +54,8 @@ Result IAllSystemAppletProxiesService::OpenLibraryAppletProxy(
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
|
||||
*out_library_applet_proxy =
|
||||
std::make_shared<ILibraryAppletProxy>(system, applet, process_handle.Get());
|
||||
*out_library_applet_proxy = std::make_shared<ILibraryAppletProxy>(
|
||||
system, applet, process_handle.Get(), m_window_system);
|
||||
R_SUCCEED();
|
||||
} else {
|
||||
UNIMPLEMENTED();
|
||||
@ -73,7 +75,7 @@ Result IAllSystemAppletProxiesService::OpenLibraryAppletProxyOld(
|
||||
|
||||
std::shared_ptr<Applet> IAllSystemAppletProxiesService::GetAppletFromProcessId(
|
||||
ProcessId process_id) {
|
||||
return system.GetAppletManager().GetByAppletResourceUserId(process_id.pid);
|
||||
return m_window_system.GetByAppletResourceUserId(process_id.pid);
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
@ -14,11 +14,12 @@ struct Applet;
|
||||
struct AppletAttribute;
|
||||
class ILibraryAppletProxy;
|
||||
class ISystemAppletProxy;
|
||||
class WindowSystem;
|
||||
|
||||
class IAllSystemAppletProxiesService final
|
||||
: public ServiceFramework<IAllSystemAppletProxiesService> {
|
||||
public:
|
||||
explicit IAllSystemAppletProxiesService(Core::System& system_);
|
||||
explicit IAllSystemAppletProxiesService(Core::System& system_, WindowSystem& window_system);
|
||||
~IAllSystemAppletProxiesService() override;
|
||||
|
||||
private:
|
||||
@ -35,6 +36,8 @@ private:
|
||||
|
||||
private:
|
||||
std::shared_ptr<Applet> GetAppletFromProcessId(ProcessId pid);
|
||||
|
||||
WindowSystem& m_window_system;
|
||||
};
|
||||
|
||||
} // namespace AM
|
||||
|
@ -19,7 +19,7 @@ IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_,
|
||||
{21, nullptr, "TryPopFromAppletBoundChannel"},
|
||||
{40, nullptr, "GetDisplayLogicalResolution"},
|
||||
{42, nullptr, "SetDisplayMagnification"},
|
||||
{50, nullptr, "SetHomeButtonDoubleClickEnabled"},
|
||||
{50, D<&IAppletCommonFunctions::SetHomeButtonDoubleClickEnabled>, "SetHomeButtonDoubleClickEnabled"},
|
||||
{51, D<&IAppletCommonFunctions::GetHomeButtonDoubleClickEnabled>, "GetHomeButtonDoubleClickEnabled"},
|
||||
{52, nullptr, "IsHomeButtonShortPressedBlocked"},
|
||||
{60, nullptr, "IsVrModeCurtainRequired"},
|
||||
@ -40,6 +40,13 @@ IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_,
|
||||
|
||||
IAppletCommonFunctions::~IAppletCommonFunctions() = default;
|
||||
|
||||
Result IAppletCommonFunctions::SetHomeButtonDoubleClickEnabled(
|
||||
bool home_button_double_click_enabled) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called, home_button_double_click_enabled={}",
|
||||
home_button_double_click_enabled);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAppletCommonFunctions::GetHomeButtonDoubleClickEnabled(
|
||||
Out<bool> out_home_button_double_click_enabled) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
@ -16,6 +16,7 @@ public:
|
||||
~IAppletCommonFunctions() override;
|
||||
|
||||
private:
|
||||
Result SetHomeButtonDoubleClickEnabled(bool home_button_double_click_enabled);
|
||||
Result GetHomeButtonDoubleClickEnabled(Out<bool> out_home_button_double_click_enabled);
|
||||
Result SetCpuBoostRequestPriority(s32 priority);
|
||||
Result GetCurrentApplicationId(Out<u64> out_application_id);
|
||||
|
@ -9,12 +9,16 @@
|
||||
#include "core/hle/service/am/service/application_accessor.h"
|
||||
#include "core/hle/service/am/service/library_applet_accessor.h"
|
||||
#include "core/hle/service/am/service/storage.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/glue/glue_manager.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
IApplicationAccessor::IApplicationAccessor(Core::System& system_, std::shared_ptr<Applet> applet)
|
||||
: ServiceFramework{system_, "IApplicationAccessor"}, m_applet(std::move(applet)) {
|
||||
IApplicationAccessor::IApplicationAccessor(Core::System& system_, std::shared_ptr<Applet> applet,
|
||||
WindowSystem& window_system)
|
||||
: ServiceFramework{system_, "IApplicationAccessor"}, m_window_system(window_system),
|
||||
m_applet(std::move(applet)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IApplicationAccessor::GetAppletStateChangedEvent>, "GetAppletStateChangedEvent"},
|
||||
@ -59,7 +63,15 @@ Result IApplicationAccessor::Start() {
|
||||
|
||||
Result IApplicationAccessor::RequestExit() {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
m_applet->message_queue.RequestExit();
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
if (m_applet->exit_locked) {
|
||||
m_applet->lifecycle_manager.RequestExit();
|
||||
m_applet->UpdateSuspensionStateLocked(true);
|
||||
} else {
|
||||
m_applet->process->Terminate();
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@ -71,13 +83,14 @@ Result IApplicationAccessor::Terminate() {
|
||||
|
||||
Result IApplicationAccessor::GetResult() {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
R_SUCCEED();
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
R_RETURN(m_applet->terminate_result);
|
||||
}
|
||||
|
||||
Result IApplicationAccessor::GetAppletStateChangedEvent(
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
*out_event = m_applet->caller_applet_broker->GetStateChangedEvent().GetHandle();
|
||||
*out_event = m_applet->state_changed_event.GetHandle();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@ -96,8 +109,15 @@ Result IApplicationAccessor::PushLaunchParameter(LaunchParameterKind kind,
|
||||
|
||||
Result IApplicationAccessor::GetApplicationControlProperty(
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_control_property) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
R_THROW(ResultUnknown);
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
std::vector<u8> nacp;
|
||||
R_TRY(system.GetARPManager().GetControlProperty(&nacp, m_applet->program_id));
|
||||
|
||||
std::memcpy(out_control_property.data(), nacp.data(),
|
||||
std::min(out_control_property.size(), nacp.size()));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationAccessor::SetUsers(bool enable,
|
||||
@ -114,8 +134,9 @@ Result IApplicationAccessor::GetCurrentLibraryApplet(
|
||||
}
|
||||
|
||||
Result IApplicationAccessor::RequestForApplicationToGetForeground() {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
R_THROW(ResultUnknown);
|
||||
LOG_INFO(Service_AM, "called");
|
||||
m_window_system.RequestApplicationToGetForeground();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationAccessor::CheckRightsEnvironmentAvailable(Out<bool> out_is_available) {
|
||||
|
@ -13,10 +13,12 @@ namespace Service::AM {
|
||||
struct Applet;
|
||||
class ILibraryAppletAccessor;
|
||||
class IStorage;
|
||||
class WindowSystem;
|
||||
|
||||
class IApplicationAccessor final : public ServiceFramework<IApplicationAccessor> {
|
||||
public:
|
||||
explicit IApplicationAccessor(Core::System& system_, std::shared_ptr<Applet> applet);
|
||||
explicit IApplicationAccessor(Core::System& system_, std::shared_ptr<Applet> applet,
|
||||
WindowSystem& window_system);
|
||||
~IApplicationAccessor() override;
|
||||
|
||||
private:
|
||||
@ -34,6 +36,7 @@ private:
|
||||
Result GetNsRightsEnvironmentHandle(Out<u64> out_handle);
|
||||
Result ReportApplicationExitTimeout();
|
||||
|
||||
WindowSystem& m_window_system;
|
||||
const std::shared_ptr<Applet> m_applet;
|
||||
};
|
||||
|
||||
|
@ -1,17 +1,57 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/hle/service/am/am_types.h"
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
#include "core/hle/service/am/process_creation.h"
|
||||
#include "core/hle/service/am/service/application_accessor.h"
|
||||
#include "core/hle/service/am/service/application_creator.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
IApplicationCreator::IApplicationCreator(Core::System& system_)
|
||||
: ServiceFramework{system_, "IApplicationCreator"} {
|
||||
namespace {
|
||||
|
||||
Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_application_accessor,
|
||||
Core::System& system, WindowSystem& window_system, u64 program_id) {
|
||||
FileSys::VirtualFile nca_raw{};
|
||||
|
||||
// Get the program NCA from storage.
|
||||
auto& storage = system.GetContentProviderUnion();
|
||||
nca_raw = storage.GetEntryRaw(program_id, FileSys::ContentRecordType::Program);
|
||||
|
||||
// Ensure we retrieved a program NCA.
|
||||
R_UNLESS(nca_raw != nullptr, ResultUnknown);
|
||||
|
||||
std::vector<u8> control;
|
||||
std::unique_ptr<Loader::AppLoader> loader;
|
||||
Loader::ResultStatus result;
|
||||
auto process =
|
||||
CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0);
|
||||
R_UNLESS(process != nullptr, ResultUnknown);
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
|
||||
applet->program_id = program_id;
|
||||
applet->applet_id = AppletId::Application;
|
||||
applet->type = AppletType::Application;
|
||||
applet->library_applet_mode = LibraryAppletMode::AllForeground;
|
||||
|
||||
window_system.TrackApplet(applet, true);
|
||||
|
||||
*out_application_accessor =
|
||||
std::make_shared<IApplicationAccessor>(system, applet, window_system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
IApplicationCreator::IApplicationCreator(Core::System& system_, WindowSystem& window_system)
|
||||
: ServiceFramework{system_, "IApplicationCreator"}, m_window_system{window_system} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IApplicationCreator::CreateApplication>, "CreateApplication"},
|
||||
@ -28,8 +68,9 @@ IApplicationCreator::~IApplicationCreator() = default;
|
||||
|
||||
Result IApplicationCreator::CreateApplication(
|
||||
Out<SharedPointer<IApplicationAccessor>> out_application_accessor, u64 application_id) {
|
||||
LOG_ERROR(Service_NS, "called, application_id={:x}", application_id);
|
||||
R_THROW(ResultUnknown);
|
||||
LOG_INFO(Service_NS, "called, application_id={:016X}", application_id);
|
||||
R_RETURN(
|
||||
CreateGuestApplication(out_application_accessor, system, m_window_system, application_id));
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
@ -10,14 +10,17 @@ namespace Service::AM {
|
||||
|
||||
class IApplicationAccessor;
|
||||
struct Applet;
|
||||
class WindowSystem;
|
||||
|
||||
class IApplicationCreator final : public ServiceFramework<IApplicationCreator> {
|
||||
public:
|
||||
explicit IApplicationCreator(Core::System& system_);
|
||||
explicit IApplicationCreator(Core::System& system_, WindowSystem& window_system);
|
||||
~IApplicationCreator() override;
|
||||
|
||||
private:
|
||||
Result CreateApplication(Out<SharedPointer<IApplicationAccessor>>, u64 application_id);
|
||||
|
||||
WindowSystem& m_window_system;
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
|
@ -181,7 +181,8 @@ Result IApplicationFunctions::GetDesiredLanguage(Out<u64> out_language_code) {
|
||||
}
|
||||
|
||||
Result IApplicationFunctions::SetTerminateResult(Result terminate_result) {
|
||||
LOG_INFO(Service_AM, "(STUBBED) called, result={:#x} ({}-{})", terminate_result.GetInnerValue(),
|
||||
LOG_INFO(Service_AM, "(STUBBED) called, result={:#x} ({:04}-{:04})",
|
||||
terminate_result.GetInnerValue(),
|
||||
static_cast<u32>(terminate_result.GetModule()) + 2000,
|
||||
terminate_result.GetDescription());
|
||||
|
||||
|
@ -17,9 +17,9 @@
|
||||
namespace Service::AM {
|
||||
|
||||
IApplicationProxy::IApplicationProxy(Core::System& system_, std::shared_ptr<Applet> applet,
|
||||
Kernel::KProcess* process)
|
||||
: ServiceFramework{system_, "IApplicationProxy"}, m_process{process}, m_applet{
|
||||
std::move(applet)} {
|
||||
Kernel::KProcess* process, WindowSystem& window_system)
|
||||
: ServiceFramework{system_, "IApplicationProxy"},
|
||||
m_window_system{window_system}, m_process{process}, m_applet{std::move(applet)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IApplicationProxy::GetCommonStateGetter>, "GetCommonStateGetter"},
|
||||
@ -70,7 +70,7 @@ Result IApplicationProxy::GetDebugFunctions(
|
||||
Result IApplicationProxy::GetWindowController(
|
||||
Out<SharedPointer<IWindowController>> out_window_controller) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_window_controller = std::make_shared<IWindowController>(system, m_applet);
|
||||
*out_window_controller = std::make_shared<IWindowController>(system, m_applet, m_window_system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@ -91,7 +91,8 @@ Result IApplicationProxy::GetCommonStateGetter(
|
||||
Result IApplicationProxy::GetLibraryAppletCreator(
|
||||
Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_library_applet_creator = std::make_shared<ILibraryAppletCreator>(system, m_applet);
|
||||
*out_library_applet_creator =
|
||||
std::make_shared<ILibraryAppletCreator>(system, m_applet, m_window_system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
@ -18,11 +18,12 @@ class ILibraryAppletCreator;
|
||||
class IProcessWindingController;
|
||||
class ISelfController;
|
||||
class IWindowController;
|
||||
class WindowSystem;
|
||||
|
||||
class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
|
||||
public:
|
||||
explicit IApplicationProxy(Core::System& system_, std::shared_ptr<Applet> applet,
|
||||
Kernel::KProcess* process);
|
||||
Kernel::KProcess* process, WindowSystem& window_system);
|
||||
~IApplicationProxy();
|
||||
|
||||
private:
|
||||
@ -40,6 +41,7 @@ private:
|
||||
Out<SharedPointer<IApplicationFunctions>> out_application_functions);
|
||||
|
||||
private:
|
||||
WindowSystem& m_window_system;
|
||||
Kernel::KProcess* const m_process;
|
||||
const std::shared_ptr<Applet> m_applet;
|
||||
};
|
||||
|
@ -6,12 +6,14 @@
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
#include "core/hle/service/am/service/application_proxy.h"
|
||||
#include "core/hle/service/am/service/application_proxy_service.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
IApplicationProxyService::IApplicationProxyService(Core::System& system_)
|
||||
: ServiceFramework{system_, "appletOE"} {
|
||||
IApplicationProxyService::IApplicationProxyService(Core::System& system_,
|
||||
WindowSystem& window_system)
|
||||
: ServiceFramework{system_, "appletOE"}, m_window_system{window_system} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IApplicationProxyService::OpenApplicationProxy>, "OpenApplicationProxy"},
|
||||
};
|
||||
@ -26,8 +28,8 @@ Result IApplicationProxyService::OpenApplicationProxy(
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
if (const auto applet = this->GetAppletFromProcessId(pid)) {
|
||||
*out_application_proxy =
|
||||
std::make_shared<IApplicationProxy>(system, applet, process_handle.Get());
|
||||
*out_application_proxy = std::make_shared<IApplicationProxy>(
|
||||
system, applet, process_handle.Get(), m_window_system);
|
||||
R_SUCCEED();
|
||||
} else {
|
||||
UNIMPLEMENTED();
|
||||
@ -36,7 +38,7 @@ Result IApplicationProxyService::OpenApplicationProxy(
|
||||
}
|
||||
|
||||
std::shared_ptr<Applet> IApplicationProxyService::GetAppletFromProcessId(ProcessId process_id) {
|
||||
return system.GetAppletManager().GetByAppletResourceUserId(process_id.pid);
|
||||
return m_window_system.GetByAppletResourceUserId(process_id.pid);
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
@ -12,10 +12,11 @@ namespace AM {
|
||||
|
||||
struct Applet;
|
||||
class IApplicationProxy;
|
||||
class WindowSystem;
|
||||
|
||||
class IApplicationProxyService final : public ServiceFramework<IApplicationProxyService> {
|
||||
public:
|
||||
explicit IApplicationProxyService(Core::System& system_);
|
||||
explicit IApplicationProxyService(Core::System& system_, WindowSystem& window_system);
|
||||
~IApplicationProxyService() override;
|
||||
|
||||
private:
|
||||
@ -24,6 +25,8 @@ private:
|
||||
|
||||
private:
|
||||
std::shared_ptr<Applet> GetAppletFromProcessId(ProcessId pid);
|
||||
|
||||
WindowSystem& m_window_system;
|
||||
};
|
||||
|
||||
} // namespace AM
|
||||
|
@ -80,15 +80,14 @@ ICommonStateGetter::~ICommonStateGetter() = default;
|
||||
|
||||
Result ICommonStateGetter::GetEventHandle(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_event = &m_applet->message_queue.GetMessageReceiveEvent();
|
||||
*out_event = m_applet->lifecycle_manager.GetSystemEvent().GetHandle();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ICommonStateGetter::ReceiveMessage(Out<AppletMessage> out_applet_message) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
*out_applet_message = m_applet->message_queue.PopMessage();
|
||||
if (*out_applet_message == AppletMessage::None) {
|
||||
if (!m_applet->lifecycle_manager.PopMessage(out_applet_message)) {
|
||||
LOG_ERROR(Service_AM, "Tried to pop message but none was available!");
|
||||
R_THROW(AM::ResultNoMessages);
|
||||
}
|
||||
@ -100,7 +99,7 @@ Result ICommonStateGetter::GetCurrentFocusState(Out<FocusState> out_focus_state)
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
*out_focus_state = m_applet->focus_state;
|
||||
*out_focus_state = m_applet->lifecycle_manager.GetAndClearFocusState();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
@ -137,7 +136,7 @@ Result ICommonStateGetter::GetWriterLockAccessorEx(
|
||||
Result ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_event = &m_applet->message_queue.GetOperationModeChangedEvent();
|
||||
*out_event = m_applet->lifecycle_manager.GetOperationModeChangedSystemEvent().GetHandle();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
@ -4,13 +4,16 @@
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
#include "core/hle/service/am/service/home_menu_functions.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_, std::shared_ptr<Applet> applet)
|
||||
: ServiceFramework{system_, "IHomeMenuFunctions"}, m_applet{std::move(applet)},
|
||||
m_context{system, "IHomeMenuFunctions"}, m_pop_from_general_channel_event{m_context} {
|
||||
IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_, std::shared_ptr<Applet> applet,
|
||||
WindowSystem& window_system)
|
||||
: ServiceFramework{system_, "IHomeMenuFunctions"}, m_window_system{window_system},
|
||||
m_applet{std::move(applet)}, m_context{system, "IHomeMenuFunctions"},
|
||||
m_pop_from_general_channel_event{m_context} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{10, D<&IHomeMenuFunctions::RequestToGetForeground>, "RequestToGetForeground"},
|
||||
@ -37,17 +40,20 @@ IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_, std::shared_ptr<Ap
|
||||
IHomeMenuFunctions::~IHomeMenuFunctions() = default;
|
||||
|
||||
Result IHomeMenuFunctions::RequestToGetForeground() {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_INFO(Service_AM, "called");
|
||||
m_window_system.RequestHomeMenuToGetForeground();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHomeMenuFunctions::LockForeground() {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_INFO(Service_AM, "called");
|
||||
m_window_system.RequestLockHomeMenuIntoForeground();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHomeMenuFunctions::UnlockForeground() {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_INFO(Service_AM, "called");
|
||||
m_window_system.RequestUnlockHomeMenuIntoForeground();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
@ -11,10 +11,12 @@
|
||||
namespace Service::AM {
|
||||
|
||||
struct Applet;
|
||||
class WindowSystem;
|
||||
|
||||
class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
|
||||
public:
|
||||
explicit IHomeMenuFunctions(Core::System& system_, std::shared_ptr<Applet> applet);
|
||||
explicit IHomeMenuFunctions(Core::System& system_, std::shared_ptr<Applet> applet,
|
||||
WindowSystem& window_system);
|
||||
~IHomeMenuFunctions() override;
|
||||
|
||||
private:
|
||||
@ -26,6 +28,7 @@ private:
|
||||
Result IsForceTerminateApplicationDisabledForDebug(
|
||||
Out<bool> out_is_force_terminate_application_disabled_for_debug);
|
||||
|
||||
WindowSystem& m_window_system;
|
||||
const std::shared_ptr<Applet> m_applet;
|
||||
KernelHelpers::ServiceContext m_context;
|
||||
Event m_pop_from_general_channel_event;
|
||||
|
@ -47,20 +47,21 @@ ILibraryAppletAccessor::~ILibraryAppletAccessor() = default;
|
||||
Result ILibraryAppletAccessor::GetAppletStateChangedEvent(
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_event = m_broker->GetStateChangedEvent().GetHandle();
|
||||
*out_event = m_applet->state_changed_event.GetHandle();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ILibraryAppletAccessor::IsCompleted(Out<bool> out_is_completed) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_is_completed = m_broker->IsCompleted();
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
*out_is_completed = m_applet->is_completed;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ILibraryAppletAccessor::GetResult(Out<Result> out_result) {
|
||||
Result ILibraryAppletAccessor::GetResult() {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_result = m_applet->terminate_result;
|
||||
R_SUCCEED();
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
R_RETURN(m_applet->terminate_result);
|
||||
}
|
||||
|
||||
Result ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero() {
|
||||
@ -77,7 +78,10 @@ Result ILibraryAppletAccessor::Start() {
|
||||
|
||||
Result ILibraryAppletAccessor::RequestExit() {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
m_applet->message_queue.RequestExit();
|
||||
{
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->lifecycle_manager.RequestExit();
|
||||
}
|
||||
FrontendRequestExit();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ public:
|
||||
private:
|
||||
Result GetAppletStateChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result IsCompleted(Out<bool> out_is_completed);
|
||||
Result GetResult(Out<Result> out_result);
|
||||
Result GetResult();
|
||||
Result PresetLibraryAppletGpuTimeSliceZero();
|
||||
Result Start();
|
||||
Result RequestExit();
|
||||
|
@ -7,9 +7,11 @@
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
#include "core/hle/service/am/frontend/applets.h"
|
||||
#include "core/hle/service/am/library_applet_storage.h"
|
||||
#include "core/hle/service/am/process_creation.h"
|
||||
#include "core/hle/service/am/service/library_applet_accessor.h"
|
||||
#include "core/hle/service/am/service/library_applet_creator.h"
|
||||
#include "core/hle/service/am/service/storage.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
@ -93,6 +95,7 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) {
|
||||
}
|
||||
|
||||
std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
|
||||
WindowSystem& window_system,
|
||||
std::shared_ptr<Applet> caller_applet,
|
||||
AppletId applet_id,
|
||||
LibraryAppletMode mode) {
|
||||
@ -110,53 +113,38 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
|
||||
Firmware1700 = 17,
|
||||
};
|
||||
|
||||
auto process = std::make_unique<Process>(system);
|
||||
if (!process->Initialize(program_id, Firmware1400, Firmware1700)) {
|
||||
auto process = CreateProcess(system, program_id, Firmware1400, Firmware1700);
|
||||
if (!process) {
|
||||
// Couldn't initialize the guest process
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process));
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), false);
|
||||
applet->program_id = program_id;
|
||||
applet->applet_id = applet_id;
|
||||
applet->type = AppletType::LibraryApplet;
|
||||
applet->library_applet_mode = mode;
|
||||
|
||||
// Set focus state
|
||||
switch (mode) {
|
||||
case LibraryAppletMode::AllForeground:
|
||||
case LibraryAppletMode::NoUi:
|
||||
case LibraryAppletMode::PartialForeground:
|
||||
case LibraryAppletMode::PartialForegroundIndirectDisplay:
|
||||
applet->hid_registration.EnableAppletToGetInput(true);
|
||||
applet->focus_state = FocusState::InFocus;
|
||||
applet->message_queue.PushMessage(AppletMessage::ChangeIntoForeground);
|
||||
break;
|
||||
case LibraryAppletMode::AllForegroundInitiallyHidden:
|
||||
applet->hid_registration.EnableAppletToGetInput(false);
|
||||
applet->focus_state = FocusState::NotInFocus;
|
||||
applet->display_layer_manager.SetWindowVisibility(false);
|
||||
applet->message_queue.PushMessage(AppletMessage::ChangeIntoBackground);
|
||||
break;
|
||||
}
|
||||
applet->window_visible = mode != LibraryAppletMode::AllForegroundInitiallyHidden;
|
||||
|
||||
auto broker = std::make_shared<AppletDataBroker>(system);
|
||||
applet->caller_applet = caller_applet;
|
||||
applet->caller_applet_broker = broker;
|
||||
caller_applet->child_applets.push_back(applet);
|
||||
|
||||
system.GetAppletManager().InsertApplet(applet);
|
||||
window_system.TrackApplet(applet, false);
|
||||
|
||||
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
|
||||
}
|
||||
|
||||
std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& system,
|
||||
WindowSystem& window_system,
|
||||
std::shared_ptr<Applet> caller_applet,
|
||||
AppletId applet_id,
|
||||
LibraryAppletMode mode) {
|
||||
const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id));
|
||||
|
||||
auto process = std::make_unique<Process>(system);
|
||||
auto applet = std::make_shared<Applet>(system, std::move(process));
|
||||
auto applet = std::make_shared<Applet>(system, std::move(process), false);
|
||||
applet->program_id = program_id;
|
||||
applet->applet_id = applet_id;
|
||||
applet->type = AppletType::LibraryApplet;
|
||||
@ -166,14 +154,19 @@ std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& syste
|
||||
applet->caller_applet = caller_applet;
|
||||
applet->caller_applet_broker = storage;
|
||||
applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode);
|
||||
caller_applet->child_applets.push_back(applet);
|
||||
|
||||
window_system.TrackApplet(applet, false);
|
||||
|
||||
return std::make_shared<ILibraryAppletAccessor>(system, storage, applet);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_, std::shared_ptr<Applet> applet)
|
||||
: ServiceFramework{system_, "ILibraryAppletCreator"}, m_applet{std::move(applet)} {
|
||||
ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_, std::shared_ptr<Applet> applet,
|
||||
WindowSystem& window_system)
|
||||
: ServiceFramework{system_, "ILibraryAppletCreator"},
|
||||
m_window_system{window_system}, m_applet{std::move(applet)} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&ILibraryAppletCreator::CreateLibraryApplet>, "CreateLibraryApplet"},
|
||||
{1, nullptr, "TerminateAllLibraryApplets"},
|
||||
@ -195,10 +188,12 @@ Result ILibraryAppletCreator::CreateLibraryApplet(
|
||||
|
||||
std::shared_ptr<ILibraryAppletAccessor> library_applet;
|
||||
if (ShouldCreateGuestApplet(applet_id)) {
|
||||
library_applet = CreateGuestApplet(system, m_applet, applet_id, library_applet_mode);
|
||||
library_applet =
|
||||
CreateGuestApplet(system, m_window_system, m_applet, applet_id, library_applet_mode);
|
||||
}
|
||||
if (!library_applet) {
|
||||
library_applet = CreateFrontendApplet(system, m_applet, applet_id, library_applet_mode);
|
||||
library_applet =
|
||||
CreateFrontendApplet(system, m_window_system, m_applet, applet_id, library_applet_mode);
|
||||
}
|
||||
if (!library_applet) {
|
||||
LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id);
|
||||
|
@ -12,10 +12,12 @@ namespace Service::AM {
|
||||
struct Applet;
|
||||
class ILibraryAppletAccessor;
|
||||
class IStorage;
|
||||
class WindowSystem;
|
||||
|
||||
class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
|
||||
public:
|
||||
explicit ILibraryAppletCreator(Core::System& system_, std::shared_ptr<Applet> applet);
|
||||
explicit ILibraryAppletCreator(Core::System& system_, std::shared_ptr<Applet> applet,
|
||||
WindowSystem& window_system);
|
||||
~ILibraryAppletCreator() override;
|
||||
|
||||
private:
|
||||
@ -29,6 +31,7 @@ private:
|
||||
Result CreateHandleStorage(Out<SharedPointer<IStorage>> out_storage, s64 size,
|
||||
InCopyHandle<Kernel::KTransferMemory> transfer_memory_handle);
|
||||
|
||||
WindowSystem& m_window_system;
|
||||
const std::shared_ptr<Applet> m_applet;
|
||||
};
|
||||
|
||||
|
@ -19,9 +19,9 @@
|
||||
namespace Service::AM {
|
||||
|
||||
ILibraryAppletProxy::ILibraryAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet,
|
||||
Kernel::KProcess* process)
|
||||
: ServiceFramework{system_, "ILibraryAppletProxy"}, m_process{process}, m_applet{
|
||||
std::move(applet)} {
|
||||
Kernel::KProcess* process, WindowSystem& window_system)
|
||||
: ServiceFramework{system_, "ILibraryAppletProxy"},
|
||||
m_window_system{window_system}, m_process{process}, m_applet{std::move(applet)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&ILibraryAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"},
|
||||
@ -75,7 +75,7 @@ Result ILibraryAppletProxy::GetDebugFunctions(
|
||||
Result ILibraryAppletProxy::GetWindowController(
|
||||
Out<SharedPointer<IWindowController>> out_window_controller) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_window_controller = std::make_shared<IWindowController>(system, m_applet);
|
||||
*out_window_controller = std::make_shared<IWindowController>(system, m_applet, m_window_system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@ -96,7 +96,8 @@ Result ILibraryAppletProxy::GetCommonStateGetter(
|
||||
Result ILibraryAppletProxy::GetLibraryAppletCreator(
|
||||
Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_library_applet_creator = std::make_shared<ILibraryAppletCreator>(system, m_applet);
|
||||
*out_library_applet_creator =
|
||||
std::make_shared<ILibraryAppletCreator>(system, m_applet, m_window_system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@ -118,7 +119,8 @@ Result ILibraryAppletProxy::GetAppletCommonFunctions(
|
||||
Result ILibraryAppletProxy::GetHomeMenuFunctions(
|
||||
Out<SharedPointer<IHomeMenuFunctions>> out_home_menu_functions) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_home_menu_functions = std::make_shared<IHomeMenuFunctions>(system, m_applet);
|
||||
*out_home_menu_functions =
|
||||
std::make_shared<IHomeMenuFunctions>(system, m_applet, m_window_system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
@ -21,11 +21,12 @@ class ILibraryAppletSelfAccessor;
|
||||
class IProcessWindingController;
|
||||
class ISelfController;
|
||||
class IWindowController;
|
||||
class WindowSystem;
|
||||
|
||||
class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
|
||||
public:
|
||||
explicit ILibraryAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet,
|
||||
Kernel::KProcess* process);
|
||||
Kernel::KProcess* process, WindowSystem& window_system);
|
||||
~ILibraryAppletProxy();
|
||||
|
||||
private:
|
||||
@ -47,6 +48,7 @@ private:
|
||||
Result GetGlobalStateController(
|
||||
Out<SharedPointer<IGlobalStateController>> out_global_state_controller);
|
||||
|
||||
WindowSystem& m_window_system;
|
||||
Kernel::KProcess* const m_process;
|
||||
const std::shared_ptr<Applet> m_applet;
|
||||
};
|
||||
|
@ -176,8 +176,7 @@ Result ILibraryAppletSelfAccessor::GetMainAppletStorageId(Out<FileSys::StorageId
|
||||
|
||||
Result ILibraryAppletSelfAccessor::ExitProcessAndReturn() {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
system.GetAppletManager().TerminateAndRemoveApplet(m_applet->aruid);
|
||||
m_broker->SignalCompletion();
|
||||
m_applet->process->Terminate();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
@ -86,8 +86,7 @@ ISelfController::~ISelfController() {
|
||||
Result ISelfController::Exit() {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
// TODO
|
||||
system.Exit();
|
||||
m_applet->process->Terminate();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
@ -95,7 +94,16 @@ Result ISelfController::Exit() {
|
||||
Result ISelfController::LockExit() {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
|
||||
if (m_applet->lifecycle_manager.GetExitRequested()) {
|
||||
// With exit already requested, ignore and terminate immediately.
|
||||
m_applet->process->Terminate();
|
||||
} else {
|
||||
// Otherwise, set exit lock state.
|
||||
m_applet->exit_locked = true;
|
||||
system.SetExitLocked(true);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
@ -103,10 +111,13 @@ Result ISelfController::LockExit() {
|
||||
Result ISelfController::UnlockExit() {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
|
||||
m_applet->exit_locked = false;
|
||||
system.SetExitLocked(false);
|
||||
|
||||
if (system.GetExitRequested()) {
|
||||
system.Exit();
|
||||
if (m_applet->lifecycle_manager.GetExitRequested()) {
|
||||
m_applet->process->Terminate();
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
@ -155,7 +166,7 @@ Result ISelfController::SetOperationModeChangedNotification(bool enabled) {
|
||||
LOG_INFO(Service_AM, "called, enabled={}", enabled);
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->operation_mode_changed_notification_enabled = enabled;
|
||||
m_applet->lifecycle_manager.SetOperationModeChangedNotificationEnabled(enabled);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
@ -164,17 +175,18 @@ Result ISelfController::SetPerformanceModeChangedNotification(bool enabled) {
|
||||
LOG_INFO(Service_AM, "called, enabled={}", enabled);
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->performance_mode_changed_notification_enabled = enabled;
|
||||
m_applet->lifecycle_manager.SetPerformanceModeChangedNotificationEnabled(enabled);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ISelfController::SetFocusHandlingMode(bool notify, bool background, bool suspend) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called, notify={} background={} suspend={}", notify,
|
||||
background, suspend);
|
||||
LOG_INFO(Service_AM, "called, notify={} background={} suspend={}", notify, background, suspend);
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->focus_handling_mode = {notify, background, suspend};
|
||||
m_applet->lifecycle_manager.SetFocusStateChangedNotificationEnabled(notify);
|
||||
m_applet->lifecycle_manager.SetFocusHandlingMode(suspend);
|
||||
m_applet->UpdateSuspensionStateLocked(true);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
@ -183,7 +195,7 @@ Result ISelfController::SetRestartMessageEnabled(bool enabled) {
|
||||
LOG_INFO(Service_AM, "called, enabled={}", enabled);
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->restart_message_enabled = enabled;
|
||||
m_applet->lifecycle_manager.SetResumeNotificationEnabled(enabled);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
@ -202,7 +214,8 @@ Result ISelfController::SetOutOfFocusSuspendingEnabled(bool enabled) {
|
||||
LOG_INFO(Service_AM, "called, enabled={}", enabled);
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->out_of_focus_suspension_enabled = enabled;
|
||||
m_applet->lifecycle_manager.SetOutOfFocusSuspendingEnabled(enabled);
|
||||
m_applet->UpdateSuspensionStateLocked(false);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
@ -19,9 +19,9 @@
|
||||
namespace Service::AM {
|
||||
|
||||
ISystemAppletProxy::ISystemAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet,
|
||||
Kernel::KProcess* process)
|
||||
: ServiceFramework{system_, "ISystemAppletProxy"}, m_process{process}, m_applet{
|
||||
std::move(applet)} {
|
||||
Kernel::KProcess* process, WindowSystem& window_system)
|
||||
: ServiceFramework{system_, "ISystemAppletProxy"},
|
||||
m_window_system{window_system}, m_process{process}, m_applet{std::move(applet)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&ISystemAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"},
|
||||
@ -75,7 +75,7 @@ Result ISystemAppletProxy::GetDebugFunctions(
|
||||
Result ISystemAppletProxy::GetWindowController(
|
||||
Out<SharedPointer<IWindowController>> out_window_controller) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_window_controller = std::make_shared<IWindowController>(system, m_applet);
|
||||
*out_window_controller = std::make_shared<IWindowController>(system, m_applet, m_window_system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@ -96,14 +96,15 @@ Result ISystemAppletProxy::GetCommonStateGetter(
|
||||
Result ISystemAppletProxy::GetLibraryAppletCreator(
|
||||
Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_library_applet_creator = std::make_shared<ILibraryAppletCreator>(system, m_applet);
|
||||
*out_library_applet_creator =
|
||||
std::make_shared<ILibraryAppletCreator>(system, m_applet, m_window_system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ISystemAppletProxy::GetApplicationCreator(
|
||||
Out<SharedPointer<IApplicationCreator>> out_application_creator) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_application_creator = std::make_shared<IApplicationCreator>(system);
|
||||
*out_application_creator = std::make_shared<IApplicationCreator>(system, m_window_system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@ -117,7 +118,8 @@ Result ISystemAppletProxy::GetAppletCommonFunctions(
|
||||
Result ISystemAppletProxy::GetHomeMenuFunctions(
|
||||
Out<SharedPointer<IHomeMenuFunctions>> out_home_menu_functions) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_home_menu_functions = std::make_shared<IHomeMenuFunctions>(system, m_applet);
|
||||
*out_home_menu_functions =
|
||||
std::make_shared<IHomeMenuFunctions>(system, m_applet, m_window_system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
@ -21,11 +21,12 @@ class ILibraryAppletCreator;
|
||||
class IProcessWindingController;
|
||||
class ISelfController;
|
||||
class IWindowController;
|
||||
class WindowSystem;
|
||||
|
||||
class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
|
||||
public:
|
||||
explicit ISystemAppletProxy(Core::System& system, std::shared_ptr<Applet> applet,
|
||||
Kernel::KProcess* process);
|
||||
Kernel::KProcess* process, WindowSystem& window_system);
|
||||
~ISystemAppletProxy();
|
||||
|
||||
private:
|
||||
@ -46,6 +47,7 @@ private:
|
||||
Result GetGlobalStateController(
|
||||
Out<SharedPointer<IGlobalStateController>> out_global_state_controller);
|
||||
|
||||
WindowSystem& m_window_system;
|
||||
Kernel::KProcess* const m_process;
|
||||
const std::shared_ptr<Applet> m_applet;
|
||||
};
|
||||
|
@ -4,12 +4,15 @@
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
#include "core/hle/service/am/service/window_controller.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
IWindowController::IWindowController(Core::System& system_, std::shared_ptr<Applet> applet)
|
||||
: ServiceFramework{system_, "IWindowController"}, m_applet{std::move(applet)} {
|
||||
IWindowController::IWindowController(Core::System& system_, std::shared_ptr<Applet> applet,
|
||||
WindowSystem& window_system)
|
||||
: ServiceFramework{system_, "IWindowController"},
|
||||
m_window_system{window_system}, m_applet{std::move(applet)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "CreateWindow"},
|
||||
@ -63,17 +66,9 @@ Result IWindowController::RejectToChangeIntoBackground() {
|
||||
}
|
||||
|
||||
Result IWindowController::SetAppletWindowVisibility(bool visible) {
|
||||
m_applet->display_layer_manager.SetWindowVisibility(visible);
|
||||
m_applet->hid_registration.EnableAppletToGetInput(visible);
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
if (visible) {
|
||||
m_applet->message_queue.PushMessage(AppletMessage::ChangeIntoForeground);
|
||||
m_applet->focus_state = FocusState::InFocus;
|
||||
} else {
|
||||
m_applet->focus_state = FocusState::NotInFocus;
|
||||
}
|
||||
|
||||
m_applet->message_queue.PushMessage(AppletMessage::FocusStateChanged);
|
||||
m_window_system.RequestAppletVisibilityState(*m_applet, visible);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
@ -9,10 +9,12 @@
|
||||
namespace Service::AM {
|
||||
|
||||
struct Applet;
|
||||
class WindowSystem;
|
||||
|
||||
class IWindowController final : public ServiceFramework<IWindowController> {
|
||||
public:
|
||||
explicit IWindowController(Core::System& system_, std::shared_ptr<Applet> applet);
|
||||
explicit IWindowController(Core::System& system_, std::shared_ptr<Applet> applet,
|
||||
WindowSystem& window_system);
|
||||
~IWindowController() override;
|
||||
|
||||
private:
|
||||
@ -24,6 +26,7 @@ private:
|
||||
Result SetAppletWindowVisibility(bool visible);
|
||||
Result SetAppletGpuTimeSlice(s64 time_slice);
|
||||
|
||||
WindowSystem& m_window_system;
|
||||
const std::shared_ptr<Applet> m_applet;
|
||||
};
|
||||
|
||||
|
315
src/core/hle/service/am/window_system.cpp
Normal file
315
src/core/hle/service/am/window_system.cpp
Normal file
@ -0,0 +1,315 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/am/am_results.h"
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
#include "core/hle/service/am/event_observer.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
WindowSystem::WindowSystem(Core::System& system) : m_system(system) {}
|
||||
|
||||
WindowSystem::~WindowSystem() {
|
||||
m_system.GetAppletManager().SetWindowSystem(nullptr);
|
||||
}
|
||||
|
||||
void WindowSystem::SetEventObserver(EventObserver* observer) {
|
||||
m_event_observer = observer;
|
||||
m_system.GetAppletManager().SetWindowSystem(this);
|
||||
}
|
||||
|
||||
void WindowSystem::Update() {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
// Loop through all applets and remove terminated applets.
|
||||
this->PruneTerminatedAppletsLocked();
|
||||
|
||||
// If the home menu is being locked into the foreground, handle that.
|
||||
if (this->LockHomeMenuIntoForegroundLocked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Recursively update each applet root.
|
||||
this->UpdateAppletStateLocked(m_home_menu, m_foreground_requested_applet == m_home_menu);
|
||||
this->UpdateAppletStateLocked(m_application, m_foreground_requested_applet == m_application);
|
||||
}
|
||||
|
||||
void WindowSystem::TrackApplet(std::shared_ptr<Applet> applet, bool is_application) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
if (applet->applet_id == AppletId::QLaunch) {
|
||||
ASSERT(m_home_menu == nullptr);
|
||||
m_home_menu = applet.get();
|
||||
} else if (is_application) {
|
||||
ASSERT(m_application == nullptr);
|
||||
m_application = applet.get();
|
||||
}
|
||||
|
||||
m_event_observer->TrackAppletProcess(*applet);
|
||||
m_applets.emplace(applet->aruid.pid, std::move(applet));
|
||||
}
|
||||
|
||||
std::shared_ptr<Applet> WindowSystem::GetByAppletResourceUserId(u64 aruid) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
const auto it = m_applets.find(aruid);
|
||||
if (it == m_applets.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
std::shared_ptr<Applet> WindowSystem::GetMainApplet() {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
if (m_application) {
|
||||
return m_applets.at(m_application->aruid.pid);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void WindowSystem::RequestHomeMenuToGetForeground() {
|
||||
{
|
||||
std::scoped_lock lk{m_lock};
|
||||
m_foreground_requested_applet = m_home_menu;
|
||||
}
|
||||
|
||||
m_event_observer->RequestUpdate();
|
||||
}
|
||||
|
||||
void WindowSystem::RequestApplicationToGetForeground() {
|
||||
{
|
||||
std::scoped_lock lk{m_lock};
|
||||
m_foreground_requested_applet = m_application;
|
||||
}
|
||||
|
||||
m_event_observer->RequestUpdate();
|
||||
}
|
||||
|
||||
void WindowSystem::RequestLockHomeMenuIntoForeground() {
|
||||
{
|
||||
std::scoped_lock lk{m_lock};
|
||||
m_home_menu_foreground_locked = true;
|
||||
}
|
||||
|
||||
m_event_observer->RequestUpdate();
|
||||
}
|
||||
|
||||
void WindowSystem::RequestUnlockHomeMenuIntoForeground() {
|
||||
{
|
||||
std::scoped_lock lk{m_lock};
|
||||
m_home_menu_foreground_locked = false;
|
||||
}
|
||||
|
||||
m_event_observer->RequestUpdate();
|
||||
}
|
||||
|
||||
void WindowSystem::RequestAppletVisibilityState(Applet& applet, bool visible) {
|
||||
{
|
||||
std::scoped_lock lk{applet.lock};
|
||||
applet.window_visible = visible;
|
||||
}
|
||||
|
||||
m_event_observer->RequestUpdate();
|
||||
}
|
||||
|
||||
void WindowSystem::OnOperationModeChanged() {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
for (const auto& [aruid, applet] : m_applets) {
|
||||
std::scoped_lock lk2{applet->lock};
|
||||
applet->lifecycle_manager.OnOperationAndPerformanceModeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void WindowSystem::OnExitRequested() {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
for (const auto& [aruid, applet] : m_applets) {
|
||||
std::scoped_lock lk2{applet->lock};
|
||||
applet->lifecycle_manager.RequestExit();
|
||||
}
|
||||
}
|
||||
|
||||
void WindowSystem::OnHomeButtonPressed(ButtonPressDuration type) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
// If we don't have a home menu, nothing to do.
|
||||
if (!m_home_menu) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Lock.
|
||||
std::scoped_lock lk2{m_home_menu->lock};
|
||||
|
||||
// Send home button press event to home menu.
|
||||
if (type == ButtonPressDuration::ShortPressing) {
|
||||
m_home_menu->lifecycle_manager.PushUnorderedMessage(
|
||||
AppletMessage::DetectShortPressingHomeButton);
|
||||
}
|
||||
}
|
||||
|
||||
void WindowSystem::PruneTerminatedAppletsLocked() {
|
||||
for (auto it = m_applets.begin(); it != m_applets.end(); /* ... */) {
|
||||
const auto& [aruid, applet] = *it;
|
||||
|
||||
std::scoped_lock lk{applet->lock};
|
||||
|
||||
if (!applet->process->IsTerminated()) {
|
||||
// Not terminated.
|
||||
it = std::next(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Terminated, so ensure all child applets are terminated.
|
||||
if (!applet->child_applets.empty()) {
|
||||
this->TerminateChildAppletsLocked(applet.get());
|
||||
|
||||
// Not ready to unlink until all child applets are terminated.
|
||||
it = std::next(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Erase from caller applet's list of children.
|
||||
if (auto caller_applet = applet->caller_applet.lock(); caller_applet) {
|
||||
std::scoped_lock lk2{caller_applet->lock};
|
||||
std::erase(caller_applet->child_applets, applet);
|
||||
applet->caller_applet.reset();
|
||||
|
||||
// We don't need to update the activity state of the caller applet yet.
|
||||
// It will be recalculated once we fall out of the termination handling path.
|
||||
}
|
||||
|
||||
// If this applet was foreground, it no longer is.
|
||||
if (applet.get() == m_foreground_requested_applet) {
|
||||
m_foreground_requested_applet = nullptr;
|
||||
}
|
||||
|
||||
// If this was the home menu, we should clean up.
|
||||
if (applet.get() == m_home_menu) {
|
||||
m_home_menu = nullptr;
|
||||
m_foreground_requested_applet = m_application;
|
||||
}
|
||||
|
||||
// If this was the application, we should try to switch to the home menu.
|
||||
if (applet.get() == m_application) {
|
||||
m_application = nullptr;
|
||||
m_foreground_requested_applet = m_home_menu;
|
||||
|
||||
// If we have a home menu, send it the application exited message.
|
||||
if (m_home_menu) {
|
||||
m_home_menu->lifecycle_manager.PushUnorderedMessage(
|
||||
AppletMessage::ApplicationExited);
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize applet.
|
||||
applet->OnProcessTerminatedLocked();
|
||||
|
||||
// Request update to ensure quiescence.
|
||||
m_event_observer->RequestUpdate();
|
||||
|
||||
// Unlink and advance.
|
||||
it = m_applets.erase(it);
|
||||
}
|
||||
|
||||
// If the last applet has exited, exit the system.
|
||||
if (m_applets.empty()) {
|
||||
m_system.Exit();
|
||||
}
|
||||
}
|
||||
|
||||
bool WindowSystem::LockHomeMenuIntoForegroundLocked() {
|
||||
// If the home menu is not locked into foreground, then there's nothing to do.
|
||||
if (m_home_menu == nullptr || !m_home_menu_foreground_locked) {
|
||||
m_home_menu_foreground_locked = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Terminate any direct child applets of the home menu.
|
||||
std::scoped_lock lk{m_home_menu->lock};
|
||||
|
||||
this->TerminateChildAppletsLocked(m_home_menu);
|
||||
|
||||
// When there are zero child applets left, we can proceed with the update.
|
||||
if (m_home_menu->child_applets.empty()) {
|
||||
m_home_menu->window_visible = true;
|
||||
m_foreground_requested_applet = m_home_menu;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WindowSystem::TerminateChildAppletsLocked(Applet* applet) {
|
||||
auto child_applets = applet->child_applets;
|
||||
|
||||
applet->lock.unlock();
|
||||
for (const auto& child_applet : child_applets) {
|
||||
std::scoped_lock lk{child_applet->lock};
|
||||
child_applet->process->Terminate();
|
||||
child_applet->terminate_result = AM::ResultLibraryAppletTerminated;
|
||||
}
|
||||
applet->lock.lock();
|
||||
}
|
||||
|
||||
void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground) {
|
||||
// With no applet, we don't have anything to do.
|
||||
if (!applet) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::scoped_lock lk{applet->lock};
|
||||
|
||||
const bool inherited_foreground = applet->is_process_running && is_foreground;
|
||||
const auto visible_state =
|
||||
inherited_foreground ? ActivityState::ForegroundVisible : ActivityState::BackgroundVisible;
|
||||
const auto obscured_state = inherited_foreground ? ActivityState::ForegroundObscured
|
||||
: ActivityState::BackgroundObscured;
|
||||
|
||||
const bool has_obscuring_child_applets = [&] {
|
||||
for (const auto& child_applet : applet->child_applets) {
|
||||
std::scoped_lock lk2{child_applet->lock};
|
||||
const auto mode = child_applet->library_applet_mode;
|
||||
if (child_applet->is_process_running && child_applet->window_visible &&
|
||||
(mode == LibraryAppletMode::AllForeground ||
|
||||
mode == LibraryAppletMode::AllForegroundInitiallyHidden)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}();
|
||||
|
||||
// Update visibility state.
|
||||
applet->display_layer_manager.SetWindowVisibility(is_foreground && applet->window_visible);
|
||||
|
||||
// Update interactibility state.
|
||||
applet->SetInteractibleLocked(is_foreground && applet->window_visible);
|
||||
|
||||
// Update focus state and suspension.
|
||||
const bool is_obscured = has_obscuring_child_applets || !applet->window_visible;
|
||||
const auto state = applet->lifecycle_manager.GetActivityState();
|
||||
|
||||
if (is_obscured && state != obscured_state) {
|
||||
// Set obscured state.
|
||||
applet->lifecycle_manager.SetActivityState(obscured_state);
|
||||
applet->UpdateSuspensionStateLocked(true);
|
||||
} else if (!is_obscured && state != visible_state) {
|
||||
// Set visible state.
|
||||
applet->lifecycle_manager.SetActivityState(visible_state);
|
||||
applet->UpdateSuspensionStateLocked(true);
|
||||
}
|
||||
|
||||
// Recurse into child applets.
|
||||
for (const auto& child_applet : applet->child_applets) {
|
||||
this->UpdateAppletStateLocked(child_applet.get(), is_foreground);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
83
src/core/hle/service/am/window_system.h
Normal file
83
src/core/hle/service/am/window_system.h
Normal file
@ -0,0 +1,83 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
struct Applet;
|
||||
class EventObserver;
|
||||
|
||||
enum class ButtonPressDuration {
|
||||
ShortPressing,
|
||||
MiddlePressing,
|
||||
LongPressing,
|
||||
};
|
||||
|
||||
class WindowSystem {
|
||||
public:
|
||||
explicit WindowSystem(Core::System& system);
|
||||
~WindowSystem();
|
||||
|
||||
public:
|
||||
void SetEventObserver(EventObserver* event_observer);
|
||||
void Update();
|
||||
|
||||
public:
|
||||
void TrackApplet(std::shared_ptr<Applet> applet, bool is_application);
|
||||
std::shared_ptr<Applet> GetByAppletResourceUserId(u64 aruid);
|
||||
std::shared_ptr<Applet> GetMainApplet();
|
||||
|
||||
public:
|
||||
void RequestHomeMenuToGetForeground();
|
||||
void RequestApplicationToGetForeground();
|
||||
void RequestLockHomeMenuIntoForeground();
|
||||
void RequestUnlockHomeMenuIntoForeground();
|
||||
void RequestAppletVisibilityState(Applet& applet, bool visible);
|
||||
|
||||
public:
|
||||
void OnOperationModeChanged();
|
||||
void OnExitRequested();
|
||||
void OnHomeButtonPressed(ButtonPressDuration type);
|
||||
void OnCaptureButtonPressed(ButtonPressDuration type) {}
|
||||
void OnPowerButtonPressed(ButtonPressDuration type) {}
|
||||
|
||||
private:
|
||||
void PruneTerminatedAppletsLocked();
|
||||
bool LockHomeMenuIntoForegroundLocked();
|
||||
void TerminateChildAppletsLocked(Applet* applet);
|
||||
void UpdateAppletStateLocked(Applet* applet, bool is_foreground);
|
||||
|
||||
private:
|
||||
// System reference.
|
||||
Core::System& m_system;
|
||||
|
||||
// Event observer.
|
||||
EventObserver* m_event_observer{};
|
||||
|
||||
// Lock.
|
||||
std::mutex m_lock{};
|
||||
|
||||
// Home menu state.
|
||||
bool m_home_menu_foreground_locked{};
|
||||
Applet* m_foreground_requested_applet{};
|
||||
|
||||
// Foreground roots.
|
||||
Applet* m_home_menu{};
|
||||
Applet* m_application{};
|
||||
|
||||
// Applet map by aruid.
|
||||
std::map<u64, std::shared_ptr<Applet>> m_applets{};
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
@ -1,223 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/common_funcs.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/aoc/addon_content_manager.h"
|
||||
#include "core/hle/service/aoc/purchase_event_manager.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Service::AOC {
|
||||
|
||||
static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
|
||||
return FileSys::GetBaseTitleID(title_id) == base;
|
||||
}
|
||||
|
||||
static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) {
|
||||
std::vector<u64> add_on_content;
|
||||
const auto& rcu = system.GetContentProvider();
|
||||
const auto list =
|
||||
rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
|
||||
std::transform(list.begin(), list.end(), std::back_inserter(add_on_content),
|
||||
[](const FileSys::ContentProviderEntry& rce) { return rce.title_id; });
|
||||
add_on_content.erase(
|
||||
std::remove_if(
|
||||
add_on_content.begin(), add_on_content.end(),
|
||||
[&rcu](u64 tid) {
|
||||
return rcu.GetEntry(tid, FileSys::ContentRecordType::Data)->GetStatus() !=
|
||||
Loader::ResultStatus::Success;
|
||||
}),
|
||||
add_on_content.end());
|
||||
return add_on_content;
|
||||
}
|
||||
|
||||
IAddOnContentManager::IAddOnContentManager(Core::System& system_)
|
||||
: ServiceFramework{system_, "aoc:u"}, add_on_content{AccumulateAOCTitleIDs(system)},
|
||||
service_context{system_, "aoc:u"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "CountAddOnContentByApplicationId"},
|
||||
{1, nullptr, "ListAddOnContentByApplicationId"},
|
||||
{2, D<&IAddOnContentManager::CountAddOnContent>, "CountAddOnContent"},
|
||||
{3, D<&IAddOnContentManager::ListAddOnContent>, "ListAddOnContent"},
|
||||
{4, nullptr, "GetAddOnContentBaseIdByApplicationId"},
|
||||
{5, D<&IAddOnContentManager::GetAddOnContentBaseId>, "GetAddOnContentBaseId"},
|
||||
{6, nullptr, "PrepareAddOnContentByApplicationId"},
|
||||
{7, D<&IAddOnContentManager::PrepareAddOnContent>, "PrepareAddOnContent"},
|
||||
{8, D<&IAddOnContentManager::GetAddOnContentListChangedEvent>, "GetAddOnContentListChangedEvent"},
|
||||
{9, nullptr, "GetAddOnContentLostErrorCode"},
|
||||
{10, D<&IAddOnContentManager::GetAddOnContentListChangedEventWithProcessId>, "GetAddOnContentListChangedEventWithProcessId"},
|
||||
{11, D<&IAddOnContentManager::NotifyMountAddOnContent>, "NotifyMountAddOnContent"},
|
||||
{12, D<&IAddOnContentManager::NotifyUnmountAddOnContent>, "NotifyUnmountAddOnContent"},
|
||||
{13, nullptr, "IsAddOnContentMountedForDebug"},
|
||||
{50, D<&IAddOnContentManager::CheckAddOnContentMountStatus>, "CheckAddOnContentMountStatus"},
|
||||
{100, D<&IAddOnContentManager::CreateEcPurchasedEventManager>, "CreateEcPurchasedEventManager"},
|
||||
{101, D<&IAddOnContentManager::CreatePermanentEcPurchasedEventManager>, "CreatePermanentEcPurchasedEventManager"},
|
||||
{110, nullptr, "CreateContentsServiceManager"},
|
||||
{200, nullptr, "SetRequiredAddOnContentsOnContentsAvailabilityTransition"},
|
||||
{300, nullptr, "SetupHostAddOnContent"},
|
||||
{301, nullptr, "GetRegisteredAddOnContentPath"},
|
||||
{302, nullptr, "UpdateCachedList"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
aoc_change_event = service_context.CreateEvent("GetAddOnContentListChanged:Event");
|
||||
}
|
||||
|
||||
IAddOnContentManager::~IAddOnContentManager() {
|
||||
service_context.CloseEvent(aoc_change_event);
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::CountAddOnContent(Out<u32> out_count, ClientProcessId process_id) {
|
||||
LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid);
|
||||
|
||||
const auto current = system.GetApplicationProcessProgramID();
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[current];
|
||||
if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
|
||||
*out_count = 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
*out_count = static_cast<u32>(
|
||||
std::count_if(add_on_content.begin(), add_on_content.end(),
|
||||
[current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); }));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::ListAddOnContent(Out<u32> out_count,
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_addons,
|
||||
u32 offset, u32 count, ClientProcessId process_id) {
|
||||
LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
|
||||
process_id.pid);
|
||||
|
||||
const auto current = FileSys::GetBaseTitleID(system.GetApplicationProcessProgramID());
|
||||
|
||||
std::vector<u32> out;
|
||||
const auto& disabled = Settings::values.disabled_addons[current];
|
||||
if (std::find(disabled.begin(), disabled.end(), "DLC") == disabled.end()) {
|
||||
for (u64 content_id : add_on_content) {
|
||||
if (FileSys::GetBaseTitleID(content_id) != current) {
|
||||
continue;
|
||||
}
|
||||
|
||||
out.push_back(static_cast<u32>(FileSys::GetAOCID(content_id)));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(DarkLordZach): Find the correct error code.
|
||||
R_UNLESS(out.size() >= offset, ResultUnknown);
|
||||
|
||||
*out_count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
|
||||
std::rotate(out.begin(), out.begin() + offset, out.end());
|
||||
|
||||
std::memcpy(out_addons.data(), out.data(), *out_count * sizeof(u32));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::GetAddOnContentBaseId(Out<u64> out_title_id,
|
||||
ClientProcessId process_id) {
|
||||
LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid);
|
||||
|
||||
const auto title_id = system.GetApplicationProcessProgramID();
|
||||
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
|
||||
const auto res = pm.GetControlMetadata();
|
||||
if (res.first == nullptr) {
|
||||
*out_title_id = FileSys::GetAOCBaseTitleID(title_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
*out_title_id = res.first->GetDLCBaseTitleId();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::PrepareAddOnContent(s32 addon_index, ClientProcessId process_id) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called with addon_index={}, process_id={}", addon_index,
|
||||
process_id.pid);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::GetAddOnContentListChangedEvent(
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
*out_event = &aoc_change_event->GetReadableEvent();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::GetAddOnContentListChangedEventWithProcessId(
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event, ClientProcessId process_id) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
*out_event = &aoc_change_event->GetReadableEvent();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::NotifyMountAddOnContent() {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::NotifyUnmountAddOnContent() {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::CheckAddOnContentMountStatus() {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::CreateEcPurchasedEventManager(
|
||||
OutInterface<IPurchaseEventManager> out_interface) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
*out_interface = std::make_shared<IPurchaseEventManager>(system);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAddOnContentManager::CreatePermanentEcPurchasedEventManager(
|
||||
OutInterface<IPurchaseEventManager> out_interface) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
*out_interface = std::make_shared<IPurchaseEventManager>(system);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
server_manager->RegisterNamedService("aoc:u", std::make_shared<IAddOnContentManager>(system));
|
||||
ServerManager::RunServer(std::move(server_manager));
|
||||
}
|
||||
|
||||
} // namespace Service::AOC
|
@ -1,51 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class KEvent;
|
||||
}
|
||||
|
||||
namespace Service::AOC {
|
||||
|
||||
class IPurchaseEventManager;
|
||||
|
||||
class IAddOnContentManager final : public ServiceFramework<IAddOnContentManager> {
|
||||
public:
|
||||
explicit IAddOnContentManager(Core::System& system);
|
||||
~IAddOnContentManager() override;
|
||||
|
||||
Result CountAddOnContent(Out<u32> out_count, ClientProcessId process_id);
|
||||
Result ListAddOnContent(Out<u32> out_count, OutBuffer<BufferAttr_HipcMapAlias> out_addons,
|
||||
u32 offset, u32 count, ClientProcessId process_id);
|
||||
Result GetAddOnContentBaseId(Out<u64> out_title_id, ClientProcessId process_id);
|
||||
Result PrepareAddOnContent(s32 addon_index, ClientProcessId process_id);
|
||||
Result GetAddOnContentListChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result GetAddOnContentListChangedEventWithProcessId(
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event, ClientProcessId process_id);
|
||||
Result NotifyMountAddOnContent();
|
||||
Result NotifyUnmountAddOnContent();
|
||||
Result CheckAddOnContentMountStatus();
|
||||
Result CreateEcPurchasedEventManager(OutInterface<IPurchaseEventManager> out_interface);
|
||||
Result CreatePermanentEcPurchasedEventManager(
|
||||
OutInterface<IPurchaseEventManager> out_interface);
|
||||
|
||||
private:
|
||||
std::vector<u64> add_on_content;
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
Kernel::KEvent* aoc_change_event;
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system);
|
||||
|
||||
} // namespace Service::AOC
|
340
src/core/hle/service/aoc/aoc_u.cpp
Normal file
340
src/core/hle/service/aoc/aoc_u.cpp
Normal file
@ -0,0 +1,340 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/common_funcs.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/aoc/aoc_u.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Service::AOC {
|
||||
|
||||
constexpr Result ResultNoPurchasedProductInfoAvailable{ErrorModule::NIMShop, 400};
|
||||
|
||||
static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
|
||||
return FileSys::GetBaseTitleID(title_id) == base;
|
||||
}
|
||||
|
||||
static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) {
|
||||
std::vector<u64> add_on_content;
|
||||
const auto& rcu = system.GetContentProvider();
|
||||
const auto list =
|
||||
rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
|
||||
std::transform(list.begin(), list.end(), std::back_inserter(add_on_content),
|
||||
[](const FileSys::ContentProviderEntry& rce) { return rce.title_id; });
|
||||
add_on_content.erase(
|
||||
std::remove_if(
|
||||
add_on_content.begin(), add_on_content.end(),
|
||||
[&rcu](u64 tid) {
|
||||
return rcu.GetEntry(tid, FileSys::ContentRecordType::Data)->GetStatus() !=
|
||||
Loader::ResultStatus::Success;
|
||||
}),
|
||||
add_on_content.end());
|
||||
return add_on_content;
|
||||
}
|
||||
|
||||
class IPurchaseEventManager final : public ServiceFramework<IPurchaseEventManager> {
|
||||
public:
|
||||
explicit IPurchaseEventManager(Core::System& system_)
|
||||
: ServiceFramework{system_, "IPurchaseEventManager"}, service_context{
|
||||
system, "IPurchaseEventManager"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IPurchaseEventManager::SetDefaultDeliveryTarget, "SetDefaultDeliveryTarget"},
|
||||
{1, &IPurchaseEventManager::SetDeliveryTarget, "SetDeliveryTarget"},
|
||||
{2, &IPurchaseEventManager::GetPurchasedEventReadableHandle, "GetPurchasedEventReadableHandle"},
|
||||
{3, &IPurchaseEventManager::PopPurchasedProductInfo, "PopPurchasedProductInfo"},
|
||||
{4, &IPurchaseEventManager::PopPurchasedProductInfoWithUid, "PopPurchasedProductInfoWithUid"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
purchased_event = service_context.CreateEvent("IPurchaseEventManager:PurchasedEvent");
|
||||
}
|
||||
|
||||
~IPurchaseEventManager() override {
|
||||
service_context.CloseEvent(purchased_event);
|
||||
}
|
||||
|
||||
private:
|
||||
void SetDefaultDeliveryTarget(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto unknown_1 = rp.Pop<u64>();
|
||||
[[maybe_unused]] const auto unknown_2 = ctx.ReadBuffer();
|
||||
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called, unknown_1={}", unknown_1);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void SetDeliveryTarget(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto unknown_1 = rp.Pop<u64>();
|
||||
[[maybe_unused]] const auto unknown_2 = ctx.ReadBuffer();
|
||||
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called, unknown_1={}", unknown_1);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void GetPurchasedEventReadableHandle(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AOC, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(purchased_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void PopPurchasedProductInfo(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AOC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultNoPurchasedProductInfoAvailable);
|
||||
}
|
||||
|
||||
void PopPurchasedProductInfoWithUid(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AOC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultNoPurchasedProductInfoAvailable);
|
||||
}
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
Kernel::KEvent* purchased_event;
|
||||
};
|
||||
|
||||
AOC_U::AOC_U(Core::System& system_)
|
||||
: ServiceFramework{system_, "aoc:u"}, add_on_content{AccumulateAOCTitleIDs(system)},
|
||||
service_context{system_, "aoc:u"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "CountAddOnContentByApplicationId"},
|
||||
{1, nullptr, "ListAddOnContentByApplicationId"},
|
||||
{2, &AOC_U::CountAddOnContent, "CountAddOnContent"},
|
||||
{3, &AOC_U::ListAddOnContent, "ListAddOnContent"},
|
||||
{4, nullptr, "GetAddOnContentBaseIdByApplicationId"},
|
||||
{5, &AOC_U::GetAddOnContentBaseId, "GetAddOnContentBaseId"},
|
||||
{6, nullptr, "PrepareAddOnContentByApplicationId"},
|
||||
{7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"},
|
||||
{8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"},
|
||||
{9, nullptr, "GetAddOnContentLostErrorCode"},
|
||||
{10, &AOC_U::GetAddOnContentListChangedEventWithProcessId, "GetAddOnContentListChangedEventWithProcessId"},
|
||||
{11, &AOC_U::NotifyMountAddOnContent, "NotifyMountAddOnContent"},
|
||||
{12, &AOC_U::NotifyUnmountAddOnContent, "NotifyUnmountAddOnContent"},
|
||||
{13, nullptr, "IsAddOnContentMountedForDebug"},
|
||||
{50, &AOC_U::CheckAddOnContentMountStatus, "CheckAddOnContentMountStatus"},
|
||||
{100, &AOC_U::CreateEcPurchasedEventManager, "CreateEcPurchasedEventManager"},
|
||||
{101, &AOC_U::CreatePermanentEcPurchasedEventManager, "CreatePermanentEcPurchasedEventManager"},
|
||||
{110, nullptr, "CreateContentsServiceManager"},
|
||||
{200, nullptr, "SetRequiredAddOnContentsOnContentsAvailabilityTransition"},
|
||||
{300, nullptr, "SetupHostAddOnContent"},
|
||||
{301, nullptr, "GetRegisteredAddOnContentPath"},
|
||||
{302, nullptr, "UpdateCachedList"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
aoc_change_event = service_context.CreateEvent("GetAddOnContentListChanged:Event");
|
||||
}
|
||||
|
||||
AOC_U::~AOC_U() {
|
||||
service_context.CloseEvent(aoc_change_event);
|
||||
}
|
||||
|
||||
void AOC_U::CountAddOnContent(HLERequestContext& ctx) {
|
||||
struct Parameters {
|
||||
u64 process_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 8);
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto params = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_DEBUG(Service_AOC, "called. process_id={}", params.process_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
|
||||
const auto current = system.GetApplicationProcessProgramID();
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[current];
|
||||
if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
|
||||
rb.Push<u32>(0);
|
||||
return;
|
||||
}
|
||||
|
||||
rb.Push<u32>(static_cast<u32>(
|
||||
std::count_if(add_on_content.begin(), add_on_content.end(),
|
||||
[current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); })));
|
||||
}
|
||||
|
||||
void AOC_U::ListAddOnContent(HLERequestContext& ctx) {
|
||||
struct Parameters {
|
||||
u32 offset;
|
||||
u32 count;
|
||||
u64 process_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 16);
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto [offset, count, process_id] = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
|
||||
process_id);
|
||||
|
||||
const auto current = FileSys::GetBaseTitleID(system.GetApplicationProcessProgramID());
|
||||
|
||||
std::vector<u32> out;
|
||||
const auto& disabled = Settings::values.disabled_addons[current];
|
||||
if (std::find(disabled.begin(), disabled.end(), "DLC") == disabled.end()) {
|
||||
for (u64 content_id : add_on_content) {
|
||||
if (FileSys::GetBaseTitleID(content_id) != current) {
|
||||
continue;
|
||||
}
|
||||
|
||||
out.push_back(static_cast<u32>(FileSys::GetAOCID(content_id)));
|
||||
}
|
||||
}
|
||||
|
||||
if (out.size() < offset) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
// TODO(DarkLordZach): Find the correct error code.
|
||||
rb.Push(ResultUnknown);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto out_count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
|
||||
std::rotate(out.begin(), out.begin() + offset, out.end());
|
||||
out.resize(out_count);
|
||||
|
||||
ctx.WriteBuffer(out);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(out_count);
|
||||
}
|
||||
|
||||
void AOC_U::GetAddOnContentBaseId(HLERequestContext& ctx) {
|
||||
struct Parameters {
|
||||
u64 process_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 8);
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto params = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_DEBUG(Service_AOC, "called. process_id={}", params.process_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
|
||||
const auto title_id = system.GetApplicationProcessProgramID();
|
||||
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
|
||||
const auto res = pm.GetControlMetadata();
|
||||
if (res.first == nullptr) {
|
||||
rb.Push(FileSys::GetAOCBaseTitleID(title_id));
|
||||
return;
|
||||
}
|
||||
|
||||
rb.Push(res.first->GetDLCBaseTitleId());
|
||||
}
|
||||
|
||||
void AOC_U::PrepareAddOnContent(HLERequestContext& ctx) {
|
||||
struct Parameters {
|
||||
s32 addon_index;
|
||||
u64 process_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 16);
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto [addon_index, process_id] = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called with addon_index={}, process_id={}", addon_index,
|
||||
process_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void AOC_U::GetAddOnContentListChangedEvent(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(aoc_change_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void AOC_U::GetAddOnContentListChangedEventWithProcessId(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(aoc_change_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void AOC_U::NotifyMountAddOnContent(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void AOC_U::NotifyUnmountAddOnContent(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void AOC_U::CheckAddOnContentMountStatus(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void AOC_U::CreateEcPurchasedEventManager(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IPurchaseEventManager>(system);
|
||||
}
|
||||
|
||||
void AOC_U::CreatePermanentEcPurchasedEventManager(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IPurchaseEventManager>(system);
|
||||
}
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
server_manager->RegisterNamedService("aoc:u", std::make_shared<AOC_U>(system));
|
||||
ServerManager::RunServer(std::move(server_manager));
|
||||
}
|
||||
|
||||
} // namespace Service::AOC
|
45
src/core/hle/service/aoc/aoc_u.h
Normal file
45
src/core/hle/service/aoc/aoc_u.h
Normal file
@ -0,0 +1,45 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class KEvent;
|
||||
}
|
||||
|
||||
namespace Service::AOC {
|
||||
|
||||
class AOC_U final : public ServiceFramework<AOC_U> {
|
||||
public:
|
||||
explicit AOC_U(Core::System& system);
|
||||
~AOC_U() override;
|
||||
|
||||
private:
|
||||
void CountAddOnContent(HLERequestContext& ctx);
|
||||
void ListAddOnContent(HLERequestContext& ctx);
|
||||
void GetAddOnContentBaseId(HLERequestContext& ctx);
|
||||
void PrepareAddOnContent(HLERequestContext& ctx);
|
||||
void GetAddOnContentListChangedEvent(HLERequestContext& ctx);
|
||||
void GetAddOnContentListChangedEventWithProcessId(HLERequestContext& ctx);
|
||||
void NotifyMountAddOnContent(HLERequestContext& ctx);
|
||||
void NotifyUnmountAddOnContent(HLERequestContext& ctx);
|
||||
void CheckAddOnContentMountStatus(HLERequestContext& ctx);
|
||||
void CreateEcPurchasedEventManager(HLERequestContext& ctx);
|
||||
void CreatePermanentEcPurchasedEventManager(HLERequestContext& ctx);
|
||||
|
||||
std::vector<u64> add_on_content;
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
Kernel::KEvent* aoc_change_event;
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system);
|
||||
|
||||
} // namespace Service::AOC
|
@ -1,67 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/hle/service/aoc/purchase_event_manager.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
|
||||
namespace Service::AOC {
|
||||
|
||||
constexpr Result ResultNoPurchasedProductInfoAvailable{ErrorModule::NIMShop, 400};
|
||||
|
||||
IPurchaseEventManager::IPurchaseEventManager(Core::System& system_)
|
||||
: ServiceFramework{system_, "IPurchaseEventManager"}, service_context{system,
|
||||
"IPurchaseEventManager"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IPurchaseEventManager::SetDefaultDeliveryTarget>, "SetDefaultDeliveryTarget"},
|
||||
{1, D<&IPurchaseEventManager::SetDeliveryTarget>, "SetDeliveryTarget"},
|
||||
{2, D<&IPurchaseEventManager::GetPurchasedEvent>, "GetPurchasedEvent"},
|
||||
{3, D<&IPurchaseEventManager::PopPurchasedProductInfo>, "PopPurchasedProductInfo"},
|
||||
{4, D<&IPurchaseEventManager::PopPurchasedProductInfoWithUid>, "PopPurchasedProductInfoWithUid"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
purchased_event = service_context.CreateEvent("IPurchaseEventManager:PurchasedEvent");
|
||||
}
|
||||
|
||||
IPurchaseEventManager::~IPurchaseEventManager() {
|
||||
service_context.CloseEvent(purchased_event);
|
||||
}
|
||||
|
||||
Result IPurchaseEventManager::SetDefaultDeliveryTarget(
|
||||
ClientProcessId process_id, InBuffer<BufferAttr_HipcMapAlias> in_buffer) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called, process_id={}", process_id.pid);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IPurchaseEventManager::SetDeliveryTarget(u64 unknown,
|
||||
InBuffer<BufferAttr_HipcMapAlias> in_buffer) {
|
||||
LOG_WARNING(Service_AOC, "(STUBBED) called, unknown={}", unknown);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IPurchaseEventManager::GetPurchasedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_WARNING(Service_AOC, "called");
|
||||
|
||||
*out_event = &purchased_event->GetReadableEvent();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IPurchaseEventManager::PopPurchasedProductInfo() {
|
||||
LOG_DEBUG(Service_AOC, "(STUBBED) called");
|
||||
|
||||
R_RETURN(ResultNoPurchasedProductInfoAvailable);
|
||||
}
|
||||
|
||||
Result IPurchaseEventManager::PopPurchasedProductInfoWithUid() {
|
||||
LOG_DEBUG(Service_AOC, "(STUBBED) called");
|
||||
|
||||
R_RETURN(ResultNoPurchasedProductInfoAvailable);
|
||||
}
|
||||
|
||||
} // namespace Service::AOC
|
@ -1,30 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/os/event.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::AOC {
|
||||
|
||||
class IPurchaseEventManager final : public ServiceFramework<IPurchaseEventManager> {
|
||||
public:
|
||||
explicit IPurchaseEventManager(Core::System& system_);
|
||||
~IPurchaseEventManager() override;
|
||||
|
||||
Result SetDefaultDeliveryTarget(ClientProcessId process_id,
|
||||
InBuffer<BufferAttr_HipcMapAlias> in_buffer);
|
||||
Result SetDeliveryTarget(u64 unknown, InBuffer<BufferAttr_HipcMapAlias> in_buffer);
|
||||
Result GetPurchasedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result PopPurchasedProductInfo();
|
||||
Result PopPurchasedProductInfoWithUid();
|
||||
|
||||
private:
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
Kernel::KEvent* purchased_event;
|
||||
};
|
||||
|
||||
} // namespace Service::AOC
|
@ -1,237 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/dmnt/cheat_interface.h"
|
||||
#include "core/hle/service/dmnt/cheat_process_manager.h"
|
||||
#include "core/hle/service/dmnt/dmnt_results.h"
|
||||
#include "core/hle/service/dmnt/dmnt_types.h"
|
||||
|
||||
namespace Service::DMNT {
|
||||
|
||||
ICheatInterface::ICheatInterface(Core::System& system_, CheatProcessManager& manager)
|
||||
: ServiceFramework{system_, "dmnt:cht"}, cheat_process_manager{manager} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{65000, C<&ICheatInterface::HasCheatProcess>, "HasCheatProcess"},
|
||||
{65001, C<&ICheatInterface::GetCheatProcessEvent>, "GetCheatProcessEvent"},
|
||||
{65002, C<&ICheatInterface::GetCheatProcessMetadata>, "GetCheatProcessMetadata"},
|
||||
{65003, C<&ICheatInterface::ForceOpenCheatProcess>, "ForceOpenCheatProcess"},
|
||||
{65004, C<&ICheatInterface::PauseCheatProcess>, "PauseCheatProcess"},
|
||||
{65005, C<&ICheatInterface::ResumeCheatProcess>, "ResumeCheatProcess"},
|
||||
{65006, C<&ICheatInterface::ForceCloseCheatProcess>, "ForceCloseCheatProcess"},
|
||||
{65100, C<&ICheatInterface::GetCheatProcessMappingCount>, "GetCheatProcessMappingCount"},
|
||||
{65101, C<&ICheatInterface::GetCheatProcessMappings>, "GetCheatProcessMappings"},
|
||||
{65102, C<&ICheatInterface::ReadCheatProcessMemory>, "ReadCheatProcessMemory"},
|
||||
{65103, C<&ICheatInterface::WriteCheatProcessMemory>, "WriteCheatProcessMemory"},
|
||||
{65104, C<&ICheatInterface::QueryCheatProcessMemory>, "QueryCheatProcessMemory"},
|
||||
{65200, C<&ICheatInterface::GetCheatCount>, "GetCheatCount"},
|
||||
{65201, C<&ICheatInterface::GetCheats>, "GetCheats"},
|
||||
{65202, C<&ICheatInterface::GetCheatById>, "GetCheatById"},
|
||||
{65203, C<&ICheatInterface::ToggleCheat>, "ToggleCheat"},
|
||||
{65204, C<&ICheatInterface::AddCheat>, "AddCheat"},
|
||||
{65205, C<&ICheatInterface::RemoveCheat>, "RemoveCheat"},
|
||||
{65206, C<&ICheatInterface::ReadStaticRegister>, "ReadStaticRegister"},
|
||||
{65207, C<&ICheatInterface::WriteStaticRegister>, "WriteStaticRegister"},
|
||||
{65208, C<&ICheatInterface::ResetStaticRegisters>, "ResetStaticRegisters"},
|
||||
{65209, C<&ICheatInterface::SetMasterCheat>, "SetMasterCheat"},
|
||||
{65300, C<&ICheatInterface::GetFrozenAddressCount>, "GetFrozenAddressCount"},
|
||||
{65301, C<&ICheatInterface::GetFrozenAddresses>, "GetFrozenAddresses"},
|
||||
{65302, C<&ICheatInterface::GetFrozenAddress>, "GetFrozenAddress"},
|
||||
{65303, C<&ICheatInterface::EnableFrozenAddress>, "EnableFrozenAddress"},
|
||||
{65304, C<&ICheatInterface::DisableFrozenAddress>, "DisableFrozenAddress"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
ICheatInterface::~ICheatInterface() = default;
|
||||
|
||||
Result ICheatInterface::HasCheatProcess(Out<bool> out_has_cheat) {
|
||||
LOG_INFO(CheatEngine, "called");
|
||||
*out_has_cheat = cheat_process_manager.HasCheatProcess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ICheatInterface::GetCheatProcessEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_INFO(CheatEngine, "called");
|
||||
*out_event = &cheat_process_manager.GetCheatProcessEvent();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ICheatInterface::GetCheatProcessMetadata(Out<CheatProcessMetadata> out_metadata) {
|
||||
LOG_INFO(CheatEngine, "called");
|
||||
R_RETURN(cheat_process_manager.GetCheatProcessMetadata(*out_metadata));
|
||||
}
|
||||
|
||||
Result ICheatInterface::ForceOpenCheatProcess() {
|
||||
LOG_INFO(CheatEngine, "called");
|
||||
R_UNLESS(R_SUCCEEDED(cheat_process_manager.ForceOpenCheatProcess()), ResultCheatNotAttached);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ICheatInterface::PauseCheatProcess() {
|
||||
LOG_INFO(CheatEngine, "called");
|
||||
R_RETURN(cheat_process_manager.PauseCheatProcess());
|
||||
}
|
||||
|
||||
Result ICheatInterface::ResumeCheatProcess() {
|
||||
LOG_INFO(CheatEngine, "called");
|
||||
R_RETURN(cheat_process_manager.ResumeCheatProcess());
|
||||
}
|
||||
|
||||
Result ICheatInterface::ForceCloseCheatProcess() {
|
||||
LOG_WARNING(CheatEngine, "(STUBBED) called");
|
||||
R_RETURN(cheat_process_manager.ForceCloseCheatProcess());
|
||||
}
|
||||
|
||||
Result ICheatInterface::GetCheatProcessMappingCount(Out<u64> out_count) {
|
||||
LOG_WARNING(CheatEngine, "(STUBBED) called");
|
||||
R_RETURN(cheat_process_manager.GetCheatProcessMappingCount(*out_count));
|
||||
}
|
||||
|
||||
Result ICheatInterface::GetCheatProcessMappings(
|
||||
Out<u64> out_count, u64 offset,
|
||||
OutArray<Kernel::Svc::MemoryInfo, BufferAttr_HipcMapAlias> out_mappings) {
|
||||
LOG_INFO(CheatEngine, "called, offset={}", offset);
|
||||
R_UNLESS(!out_mappings.empty(), ResultCheatNullBuffer);
|
||||
R_RETURN(cheat_process_manager.GetCheatProcessMappings(*out_count, offset, out_mappings));
|
||||
}
|
||||
|
||||
Result ICheatInterface::ReadCheatProcessMemory(u64 address, u64 size,
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_buffer) {
|
||||
LOG_DEBUG(CheatEngine, "called, address={}, size={}", address, size);
|
||||
R_UNLESS(!out_buffer.empty(), ResultCheatNullBuffer);
|
||||
R_RETURN(cheat_process_manager.ReadCheatProcessMemory(address, size, out_buffer));
|
||||
}
|
||||
|
||||
Result ICheatInterface::WriteCheatProcessMemory(u64 address, u64 size,
|
||||
InBuffer<BufferAttr_HipcMapAlias> buffer) {
|
||||
LOG_DEBUG(CheatEngine, "called, address={}, size={}", address, size);
|
||||
R_UNLESS(!buffer.empty(), ResultCheatNullBuffer);
|
||||
R_RETURN(cheat_process_manager.WriteCheatProcessMemory(address, size, buffer));
|
||||
}
|
||||
|
||||
Result ICheatInterface::QueryCheatProcessMemory(Out<Kernel::Svc::MemoryInfo> out_mapping,
|
||||
u64 address) {
|
||||
LOG_WARNING(CheatEngine, "(STUBBED) called, address={}", address);
|
||||
R_RETURN(cheat_process_manager.QueryCheatProcessMemory(out_mapping, address));
|
||||
}
|
||||
|
||||
Result ICheatInterface::GetCheatCount(Out<u64> out_count) {
|
||||
LOG_INFO(CheatEngine, "called");
|
||||
R_RETURN(cheat_process_manager.GetCheatCount(*out_count));
|
||||
}
|
||||
|
||||
Result ICheatInterface::GetCheats(Out<u64> out_count, u64 offset,
|
||||
OutArray<CheatEntry, BufferAttr_HipcMapAlias> out_cheats) {
|
||||
LOG_INFO(CheatEngine, "called, offset={}", offset);
|
||||
R_UNLESS(!out_cheats.empty(), ResultCheatNullBuffer);
|
||||
R_RETURN(cheat_process_manager.GetCheats(*out_count, offset, out_cheats));
|
||||
}
|
||||
|
||||
Result ICheatInterface::GetCheatById(OutLargeData<CheatEntry, BufferAttr_HipcMapAlias> out_cheat,
|
||||
u32 cheat_id) {
|
||||
LOG_INFO(CheatEngine, "called, cheat_id={}", cheat_id);
|
||||
R_RETURN(cheat_process_manager.GetCheatById(out_cheat, cheat_id));
|
||||
}
|
||||
|
||||
Result ICheatInterface::ToggleCheat(u32 cheat_id) {
|
||||
LOG_INFO(CheatEngine, "called, cheat_id={}", cheat_id);
|
||||
R_RETURN(cheat_process_manager.ToggleCheat(cheat_id));
|
||||
}
|
||||
|
||||
Result ICheatInterface::AddCheat(
|
||||
Out<u32> out_cheat_id, bool is_enabled,
|
||||
InLargeData<CheatDefinition, BufferAttr_HipcMapAlias> cheat_definition) {
|
||||
LOG_INFO(CheatEngine, "called, is_enabled={}", is_enabled);
|
||||
R_RETURN(cheat_process_manager.AddCheat(*out_cheat_id, is_enabled, *cheat_definition));
|
||||
}
|
||||
|
||||
Result ICheatInterface::RemoveCheat(u32 cheat_id) {
|
||||
LOG_INFO(CheatEngine, "called, cheat_id={}", cheat_id);
|
||||
R_RETURN(cheat_process_manager.RemoveCheat(cheat_id));
|
||||
}
|
||||
|
||||
Result ICheatInterface::ReadStaticRegister(Out<u64> out_value, u8 register_index) {
|
||||
LOG_DEBUG(CheatEngine, "called, register_index={}", register_index);
|
||||
R_RETURN(cheat_process_manager.ReadStaticRegister(*out_value, register_index));
|
||||
}
|
||||
|
||||
Result ICheatInterface::WriteStaticRegister(u8 register_index, u64 value) {
|
||||
LOG_DEBUG(CheatEngine, "called, register_index={, value={}", register_index, value);
|
||||
R_RETURN(cheat_process_manager.WriteStaticRegister(register_index, value));
|
||||
}
|
||||
|
||||
Result ICheatInterface::ResetStaticRegisters() {
|
||||
LOG_INFO(CheatEngine, "called");
|
||||
R_RETURN(cheat_process_manager.ResetStaticRegisters());
|
||||
}
|
||||
|
||||
Result ICheatInterface::SetMasterCheat(
|
||||
InLargeData<CheatDefinition, BufferAttr_HipcMapAlias> cheat_definition) {
|
||||
LOG_INFO(CheatEngine, "called, name={}, num_opcodes={}", cheat_definition->readable_name.data(),
|
||||
cheat_definition->num_opcodes);
|
||||
R_RETURN(cheat_process_manager.SetMasterCheat(*cheat_definition));
|
||||
}
|
||||
|
||||
Result ICheatInterface::GetFrozenAddressCount(Out<u64> out_count) {
|
||||
LOG_INFO(CheatEngine, "called");
|
||||
R_RETURN(cheat_process_manager.GetFrozenAddressCount(*out_count));
|
||||
}
|
||||
|
||||
Result ICheatInterface::GetFrozenAddresses(
|
||||
Out<u64> out_count, u64 offset,
|
||||
OutArray<FrozenAddressEntry, BufferAttr_HipcMapAlias> out_frozen_address) {
|
||||
LOG_INFO(CheatEngine, "called, offset={}", offset);
|
||||
R_UNLESS(!out_frozen_address.empty(), ResultCheatNullBuffer);
|
||||
R_RETURN(cheat_process_manager.GetFrozenAddresses(*out_count, offset, out_frozen_address));
|
||||
}
|
||||
|
||||
Result ICheatInterface::GetFrozenAddress(Out<FrozenAddressEntry> out_frozen_address_entry,
|
||||
u64 address) {
|
||||
LOG_INFO(CheatEngine, "called, address={}", address);
|
||||
R_RETURN(cheat_process_manager.GetFrozenAddress(*out_frozen_address_entry, address));
|
||||
}
|
||||
|
||||
Result ICheatInterface::EnableFrozenAddress(Out<u64> out_value, u64 address, u64 width) {
|
||||
LOG_INFO(CheatEngine, "called, address={}, width={}", address, width);
|
||||
R_UNLESS(width > 0, ResultFrozenAddressInvalidWidth);
|
||||
R_UNLESS(width <= sizeof(u64), ResultFrozenAddressInvalidWidth);
|
||||
R_UNLESS((width & (width - 1)) == 0, ResultFrozenAddressInvalidWidth);
|
||||
R_RETURN(cheat_process_manager.EnableFrozenAddress(*out_value, address, width));
|
||||
}
|
||||
|
||||
Result ICheatInterface::DisableFrozenAddress(u64 address) {
|
||||
LOG_INFO(CheatEngine, "called, address={}", address);
|
||||
R_RETURN(cheat_process_manager.DisableFrozenAddress(address));
|
||||
}
|
||||
|
||||
void ICheatInterface::InitializeCheatManager() {
|
||||
LOG_INFO(CheatEngine, "called");
|
||||
}
|
||||
|
||||
Result ICheatInterface::ReadCheatProcessMemoryUnsafe(u64 process_addr, std::span<u8> out_data,
|
||||
size_t size) {
|
||||
LOG_DEBUG(CheatEngine, "called, process_addr={}, size={}", process_addr, size);
|
||||
R_RETURN(cheat_process_manager.ReadCheatProcessMemoryUnsafe(process_addr, &out_data, size));
|
||||
}
|
||||
|
||||
Result ICheatInterface::WriteCheatProcessMemoryUnsafe(u64 process_addr, std::span<const u8> data,
|
||||
size_t size) {
|
||||
LOG_DEBUG(CheatEngine, "called, process_addr={}, size={}", process_addr, size);
|
||||
R_RETURN(cheat_process_manager.WriteCheatProcessMemoryUnsafe(process_addr, &data, size));
|
||||
}
|
||||
|
||||
Result ICheatInterface::PauseCheatProcessUnsafe() {
|
||||
LOG_INFO(CheatEngine, "called");
|
||||
R_RETURN(cheat_process_manager.PauseCheatProcessUnsafe());
|
||||
}
|
||||
|
||||
Result ICheatInterface::ResumeCheatProcessUnsafe() {
|
||||
LOG_INFO(CheatEngine, "called");
|
||||
R_RETURN(cheat_process_manager.ResumeCheatProcessUnsafe());
|
||||
}
|
||||
|
||||
} // namespace Service::DMNT
|
@ -1,86 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class KEvent;
|
||||
class KReadableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Kernel::Svc {
|
||||
struct MemoryInfo;
|
||||
}
|
||||
|
||||
namespace Service::DMNT {
|
||||
struct CheatDefinition;
|
||||
struct CheatEntry;
|
||||
struct CheatProcessMetadata;
|
||||
struct FrozenAddressEntry;
|
||||
class CheatProcessManager;
|
||||
|
||||
class ICheatInterface final : public ServiceFramework<ICheatInterface> {
|
||||
public:
|
||||
explicit ICheatInterface(Core::System& system_, CheatProcessManager& manager);
|
||||
~ICheatInterface() override;
|
||||
|
||||
private:
|
||||
Result HasCheatProcess(Out<bool> out_has_cheat);
|
||||
Result GetCheatProcessEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result GetCheatProcessMetadata(Out<CheatProcessMetadata> out_metadata);
|
||||
Result ForceOpenCheatProcess();
|
||||
Result PauseCheatProcess();
|
||||
Result ResumeCheatProcess();
|
||||
Result ForceCloseCheatProcess();
|
||||
|
||||
Result GetCheatProcessMappingCount(Out<u64> out_count);
|
||||
Result GetCheatProcessMappings(
|
||||
Out<u64> out_count, u64 offset,
|
||||
OutArray<Kernel::Svc::MemoryInfo, BufferAttr_HipcMapAlias> out_mappings);
|
||||
Result ReadCheatProcessMemory(u64 address, u64 size,
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_buffer);
|
||||
Result WriteCheatProcessMemory(u64 address, u64 size, InBuffer<BufferAttr_HipcMapAlias> buffer);
|
||||
|
||||
Result QueryCheatProcessMemory(Out<Kernel::Svc::MemoryInfo> out_mapping, u64 address);
|
||||
Result GetCheatCount(Out<u64> out_count);
|
||||
Result GetCheats(Out<u64> out_count, u64 offset,
|
||||
OutArray<CheatEntry, BufferAttr_HipcMapAlias> out_cheats);
|
||||
Result GetCheatById(OutLargeData<CheatEntry, BufferAttr_HipcMapAlias> out_cheat, u32 cheat_id);
|
||||
Result ToggleCheat(u32 cheat_id);
|
||||
|
||||
Result AddCheat(Out<u32> out_cheat_id, bool enabled,
|
||||
InLargeData<CheatDefinition, BufferAttr_HipcMapAlias> cheat_definition);
|
||||
Result RemoveCheat(u32 cheat_id);
|
||||
Result ReadStaticRegister(Out<u64> out_value, u8 register_index);
|
||||
Result WriteStaticRegister(u8 register_index, u64 value);
|
||||
Result ResetStaticRegisters();
|
||||
Result SetMasterCheat(InLargeData<CheatDefinition, BufferAttr_HipcMapAlias> cheat_definition);
|
||||
Result GetFrozenAddressCount(Out<u64> out_count);
|
||||
Result GetFrozenAddresses(
|
||||
Out<u64> out_count, u64 offset,
|
||||
OutArray<FrozenAddressEntry, BufferAttr_HipcMapAlias> out_frozen_address);
|
||||
Result GetFrozenAddress(Out<FrozenAddressEntry> out_frozen_address_entry, u64 address);
|
||||
Result EnableFrozenAddress(Out<u64> out_value, u64 address, u64 width);
|
||||
Result DisableFrozenAddress(u64 address);
|
||||
|
||||
private:
|
||||
void InitializeCheatManager();
|
||||
|
||||
Result ReadCheatProcessMemoryUnsafe(u64 process_addr, std::span<u8> out_data, size_t size);
|
||||
Result WriteCheatProcessMemoryUnsafe(u64 process_addr, std::span<const u8> data, size_t size);
|
||||
|
||||
Result PauseCheatProcessUnsafe();
|
||||
Result ResumeCheatProcessUnsafe();
|
||||
|
||||
CheatProcessManager& cheat_process_manager;
|
||||
};
|
||||
|
||||
} // namespace Service::DMNT
|
@ -1,117 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <optional>
|
||||
|
||||
#include "core/hle/service/dmnt/cheat_parser.h"
|
||||
#include "core/hle/service/dmnt/dmnt_types.h"
|
||||
|
||||
namespace Service::DMNT {
|
||||
|
||||
CheatParser::CheatParser() {}
|
||||
|
||||
CheatParser::~CheatParser() = default;
|
||||
|
||||
std::vector<CheatEntry> CheatParser::Parse(std::string_view data) const {
|
||||
std::vector<CheatEntry> out(1);
|
||||
std::optional<u64> current_entry;
|
||||
|
||||
for (std::size_t i = 0; i < data.size(); ++i) {
|
||||
if (std::isspace(data[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (data[i] == '{') {
|
||||
current_entry = 0;
|
||||
|
||||
if (out[*current_entry].definition.num_opcodes > 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::size_t name_size{};
|
||||
const auto name = ExtractName(name_size, data, i + 1, '}');
|
||||
if (name.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
|
||||
std::min<std::size_t>(out[*current_entry].definition.readable_name.size(),
|
||||
name.size()));
|
||||
out[*current_entry]
|
||||
.definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
|
||||
'\0';
|
||||
|
||||
i += name_size + 1;
|
||||
} else if (data[i] == '[') {
|
||||
current_entry = out.size();
|
||||
out.emplace_back();
|
||||
|
||||
std::size_t name_size{};
|
||||
const auto name = ExtractName(name_size, data, i + 1, ']');
|
||||
if (name.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
|
||||
std::min<std::size_t>(out[*current_entry].definition.readable_name.size(),
|
||||
name.size()));
|
||||
out[*current_entry]
|
||||
.definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
|
||||
'\0';
|
||||
|
||||
i += name_size + 1;
|
||||
} else if (std::isxdigit(data[i])) {
|
||||
if (!current_entry || out[*current_entry].definition.num_opcodes >=
|
||||
out[*current_entry].definition.opcodes.size()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto hex = std::string(data.substr(i, 8));
|
||||
if (!std::all_of(hex.begin(), hex.end(), ::isxdigit)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto value = static_cast<u32>(std::strtoul(hex.c_str(), nullptr, 0x10));
|
||||
out[*current_entry].definition.opcodes[out[*current_entry].definition.num_opcodes++] =
|
||||
value;
|
||||
|
||||
i += 8;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
out[0].enabled = out[0].definition.num_opcodes > 0;
|
||||
out[0].cheat_id = 0;
|
||||
|
||||
for (u32 i = 1; i < out.size(); ++i) {
|
||||
out[i].enabled = out[i].definition.num_opcodes > 0;
|
||||
out[i].cheat_id = i;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string_view CheatParser::ExtractName(std::size_t& out_name_size, std::string_view data,
|
||||
std::size_t start_index, char match) const {
|
||||
auto end_index = start_index;
|
||||
while (data[end_index] != match) {
|
||||
++end_index;
|
||||
if (end_index > data.size()) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
out_name_size = end_index - start_index;
|
||||
|
||||
// Clamp name if it's too big
|
||||
if (out_name_size > sizeof(CheatDefinition::readable_name)) {
|
||||
end_index = start_index + sizeof(CheatDefinition::readable_name);
|
||||
}
|
||||
|
||||
return data.substr(start_index, end_index - start_index);
|
||||
}
|
||||
|
||||
} // namespace Service::DMNT
|
@ -1,24 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace Service::DMNT {
|
||||
struct CheatEntry;
|
||||
|
||||
class CheatParser final {
|
||||
public:
|
||||
CheatParser();
|
||||
~CheatParser();
|
||||
|
||||
std::vector<CheatEntry> Parse(std::string_view data) const;
|
||||
|
||||
private:
|
||||
std::string_view ExtractName(std::size_t& out_name_size, std::string_view data,
|
||||
std::size_t start_index, char match) const;
|
||||
};
|
||||
|
||||
} // namespace Service::DMNT
|
@ -1,597 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "core/arm/debug.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/dmnt/cheat_process_manager.h"
|
||||
#include "core/hle/service/dmnt/cheat_virtual_machine.h"
|
||||
#include "core/hle/service/dmnt/dmnt_results.h"
|
||||
#include "core/hle/service/hid/hid_server.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "hid_core/resource_manager.h"
|
||||
#include "hid_core/resources/npad/npad.h"
|
||||
|
||||
namespace Service::DMNT {
|
||||
constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12};
|
||||
|
||||
CheatProcessManager::CheatProcessManager(Core::System& system_)
|
||||
: system{system_}, service_context{system_, "dmnt:cht"}, core_timing{system_.CoreTiming()} {
|
||||
update_event = Core::Timing::CreateEvent("CheatEngine::FrameCallback",
|
||||
[this](s64 time, std::chrono::nanoseconds ns_late)
|
||||
-> std::optional<std::chrono::nanoseconds> {
|
||||
FrameCallback(ns_late);
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < MaxCheatCount; i++) {
|
||||
ResetCheatEntry(i);
|
||||
}
|
||||
|
||||
cheat_process_event = service_context.CreateEvent("CheatProcessManager::ProcessEvent");
|
||||
unsafe_break_event = service_context.CreateEvent("CheatProcessManager::ProcessEvent");
|
||||
}
|
||||
|
||||
CheatProcessManager::~CheatProcessManager() {
|
||||
service_context.CloseEvent(cheat_process_event);
|
||||
service_context.CloseEvent(unsafe_break_event);
|
||||
core_timing.UnscheduleEvent(update_event);
|
||||
}
|
||||
|
||||
void CheatProcessManager::SetVirtualMachine(std::unique_ptr<CheatVirtualMachine> vm) {
|
||||
cheat_vm = std::move(vm);
|
||||
SetNeedsReloadVm(true);
|
||||
}
|
||||
|
||||
bool CheatProcessManager::HasActiveCheatProcess() {
|
||||
// Note: This function *MUST* be called only with the cheat lock held.
|
||||
bool has_cheat_process =
|
||||
cheat_process_debug_handle != InvalidHandle &&
|
||||
system.ApplicationProcess()->GetProcessId() == cheat_process_metadata.process_id;
|
||||
|
||||
if (!has_cheat_process) {
|
||||
CloseActiveCheatProcess();
|
||||
}
|
||||
|
||||
return has_cheat_process;
|
||||
}
|
||||
|
||||
void CheatProcessManager::CloseActiveCheatProcess() {
|
||||
if (cheat_process_debug_handle != InvalidHandle) {
|
||||
// We don't need to do any unsafe breaking.
|
||||
broken_unsafe = false;
|
||||
unsafe_break_event->Signal();
|
||||
core_timing.UnscheduleEvent(update_event);
|
||||
|
||||
// Close resources.
|
||||
cheat_process_debug_handle = InvalidHandle;
|
||||
|
||||
// Save cheat toggles.
|
||||
if (always_save_cheat_toggles || should_save_cheat_toggles) {
|
||||
// TODO: save cheat toggles
|
||||
should_save_cheat_toggles = false;
|
||||
}
|
||||
|
||||
// Clear metadata.
|
||||
cheat_process_metadata = {};
|
||||
|
||||
// Clear cheat list.
|
||||
ResetAllCheatEntries();
|
||||
|
||||
// Clear frozen addresses.
|
||||
{
|
||||
auto it = frozen_addresses_map.begin();
|
||||
while (it != frozen_addresses_map.end()) {
|
||||
it = frozen_addresses_map.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
// Signal to our fans.
|
||||
cheat_process_event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
Result CheatProcessManager::EnsureCheatProcess() {
|
||||
R_UNLESS(HasActiveCheatProcess(), ResultCheatNotAttached);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void CheatProcessManager::SetNeedsReloadVm(bool reload) {
|
||||
needs_reload_vm = reload;
|
||||
}
|
||||
|
||||
void CheatProcessManager::ResetCheatEntry(size_t i) {
|
||||
if (i < MaxCheatCount) {
|
||||
cheat_entries[i] = {};
|
||||
cheat_entries[i].cheat_id = static_cast<u32>(i);
|
||||
|
||||
SetNeedsReloadVm(true);
|
||||
}
|
||||
}
|
||||
|
||||
void CheatProcessManager::ResetAllCheatEntries() {
|
||||
for (size_t i = 0; i < MaxCheatCount; i++) {
|
||||
ResetCheatEntry(i);
|
||||
}
|
||||
|
||||
cheat_vm->ResetStaticRegisters();
|
||||
}
|
||||
|
||||
CheatEntry* CheatProcessManager::GetCheatEntryById(size_t i) {
|
||||
if (i < MaxCheatCount) {
|
||||
return cheat_entries.data() + i;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CheatEntry* CheatProcessManager::GetCheatEntryByReadableName(const char* readable_name) {
|
||||
// Check all non-master cheats for match.
|
||||
for (size_t i = 1; i < MaxCheatCount; i++) {
|
||||
if (std::strncmp(cheat_entries[i].definition.readable_name.data(), readable_name,
|
||||
sizeof(cheat_entries[i].definition.readable_name)) == 0) {
|
||||
return cheat_entries.data() + i;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CheatEntry* CheatProcessManager::GetFreeCheatEntry() {
|
||||
// Check all non-master cheats for availability.
|
||||
for (size_t i = 1; i < MaxCheatCount; i++) {
|
||||
if (cheat_entries[i].definition.num_opcodes == 0) {
|
||||
return cheat_entries.data() + i;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool CheatProcessManager::HasCheatProcess() {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
return HasActiveCheatProcess();
|
||||
}
|
||||
|
||||
Kernel::KReadableEvent& CheatProcessManager::GetCheatProcessEvent() const {
|
||||
return cheat_process_event->GetReadableEvent();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::AttachToApplicationProcess(const std::array<u8, 0x20>& build_id,
|
||||
VAddr main_region_begin,
|
||||
u64 main_region_size) {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
|
||||
// Close the active process, if needed.
|
||||
{
|
||||
if (this->HasActiveCheatProcess()) {
|
||||
// When forcing attach, we're done.
|
||||
this->CloseActiveCheatProcess();
|
||||
}
|
||||
}
|
||||
|
||||
// Get the application process's ID.
|
||||
cheat_process_metadata.process_id = system.ApplicationProcess()->GetProcessId();
|
||||
|
||||
// Get process handle, use it to learn memory extents.
|
||||
{
|
||||
const auto& page_table = system.ApplicationProcess()->GetPageTable();
|
||||
cheat_process_metadata.program_id = system.GetApplicationProcessProgramID();
|
||||
cheat_process_metadata.heap_extents = {
|
||||
.base = GetInteger(page_table.GetHeapRegionStart()),
|
||||
.size = page_table.GetHeapRegionSize(),
|
||||
};
|
||||
cheat_process_metadata.aslr_extents = {
|
||||
.base = GetInteger(page_table.GetAliasCodeRegionStart()),
|
||||
.size = page_table.GetAliasCodeRegionSize(),
|
||||
};
|
||||
cheat_process_metadata.alias_extents = {
|
||||
.base = GetInteger(page_table.GetAliasRegionStart()),
|
||||
.size = page_table.GetAliasRegionSize(),
|
||||
};
|
||||
}
|
||||
|
||||
// Get module information from loader.
|
||||
{
|
||||
cheat_process_metadata.main_nso_extents = {
|
||||
.base = main_region_begin,
|
||||
.size = main_region_size,
|
||||
};
|
||||
cheat_process_metadata.main_nso_build_id = build_id;
|
||||
}
|
||||
|
||||
// Set our debug handle.
|
||||
cheat_process_debug_handle = cheat_process_metadata.process_id;
|
||||
|
||||
// Reset broken state.
|
||||
broken_unsafe = false;
|
||||
unsafe_break_event->Signal();
|
||||
|
||||
// start the process.
|
||||
core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, update_event);
|
||||
|
||||
// Signal to our fans.
|
||||
cheat_process_event->Signal();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::GetCheatProcessMetadata(CheatProcessMetadata& out_metadata) {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
R_TRY(EnsureCheatProcess());
|
||||
|
||||
out_metadata = cheat_process_metadata;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::ForceOpenCheatProcess() {
|
||||
// R_RETURN(AttachToApplicationProcess(false));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::PauseCheatProcess() {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
|
||||
R_TRY(EnsureCheatProcess());
|
||||
R_RETURN(PauseCheatProcessUnsafe());
|
||||
}
|
||||
|
||||
Result CheatProcessManager::PauseCheatProcessUnsafe() {
|
||||
broken_unsafe = true;
|
||||
unsafe_break_event->Clear();
|
||||
if (system.ApplicationProcess()->IsSuspended()) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
R_RETURN(system.ApplicationProcess()->SetActivity(Kernel::Svc::ProcessActivity::Paused));
|
||||
}
|
||||
|
||||
Result CheatProcessManager::ResumeCheatProcess() {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
|
||||
R_TRY(EnsureCheatProcess());
|
||||
R_RETURN(ResumeCheatProcessUnsafe());
|
||||
}
|
||||
|
||||
Result CheatProcessManager::ResumeCheatProcessUnsafe() {
|
||||
broken_unsafe = true;
|
||||
unsafe_break_event->Clear();
|
||||
if (!system.ApplicationProcess()->IsSuspended()) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
system.ApplicationProcess()->SetActivity(Kernel::Svc::ProcessActivity::Runnable);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::ForceCloseCheatProcess() {
|
||||
CloseActiveCheatProcess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::GetCheatProcessMappingCount(u64& out_count) {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
R_TRY(this->EnsureCheatProcess());
|
||||
|
||||
// TODO: Call svc::QueryDebugProcessMemory
|
||||
|
||||
out_count = 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::GetCheatProcessMappings(
|
||||
u64& out_count, u64 offset, std::span<Kernel::Svc::MemoryInfo> out_mappings) {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
R_TRY(this->EnsureCheatProcess());
|
||||
|
||||
// TODO: Call svc::QueryDebugProcessMemory
|
||||
|
||||
out_count = 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::ReadCheatProcessMemory(u64 process_address, u64 size,
|
||||
std::span<u8> out_data) {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
|
||||
R_TRY(EnsureCheatProcess());
|
||||
R_RETURN(ReadCheatProcessMemoryUnsafe(process_address, &out_data, size));
|
||||
}
|
||||
|
||||
Result CheatProcessManager::ReadCheatProcessMemoryUnsafe(u64 process_address, void* out_data,
|
||||
size_t size) {
|
||||
// Return zero on invalid address
|
||||
if (!system.ApplicationMemory().IsValidVirtualAddress(process_address)) {
|
||||
std::memset(out_data, 0, size);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
system.ApplicationMemory().ReadBlock(process_address, out_data, size);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::WriteCheatProcessMemory(u64 process_address, u64 size,
|
||||
std::span<const u8> data) {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
|
||||
R_TRY(EnsureCheatProcess());
|
||||
R_RETURN(WriteCheatProcessMemoryUnsafe(process_address, &data, size));
|
||||
}
|
||||
|
||||
Result CheatProcessManager::WriteCheatProcessMemoryUnsafe(u64 process_address, const void* data,
|
||||
size_t size) {
|
||||
// Skip invalid memory write address
|
||||
if (!system.ApplicationMemory().IsValidVirtualAddress(process_address)) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
if (system.ApplicationMemory().WriteBlock(process_address, data, size)) {
|
||||
Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), process_address, size);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::QueryCheatProcessMemory(Out<Kernel::Svc::MemoryInfo> mapping,
|
||||
u64 address) {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
R_TRY(this->EnsureCheatProcess());
|
||||
|
||||
// TODO: Call svc::QueryDebugProcessMemory
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::GetCheatCount(u64& out_count) {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
R_TRY(EnsureCheatProcess());
|
||||
|
||||
out_count = std::count_if(cheat_entries.begin(), cheat_entries.end(),
|
||||
[](const auto& entry) { return entry.definition.num_opcodes != 0; });
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::GetCheats(u64& out_count, u64 offset,
|
||||
std::span<CheatEntry> out_cheats) {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
R_TRY(EnsureCheatProcess());
|
||||
|
||||
size_t count = 0, total_count = 0;
|
||||
for (size_t i = 0; i < MaxCheatCount && count < out_cheats.size(); i++) {
|
||||
if (cheat_entries[i].definition.num_opcodes) {
|
||||
total_count++;
|
||||
if (total_count > offset) {
|
||||
out_cheats[count++] = cheat_entries[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out_count = count;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::GetCheatById(CheatEntry* out_cheat, u32 cheat_id) {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
R_TRY(EnsureCheatProcess());
|
||||
|
||||
const CheatEntry* entry = GetCheatEntryById(cheat_id);
|
||||
R_UNLESS(entry != nullptr, ResultCheatUnknownId);
|
||||
R_UNLESS(entry->definition.num_opcodes != 0, ResultCheatUnknownId);
|
||||
|
||||
*out_cheat = *entry;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::ToggleCheat(u32 cheat_id) {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
R_TRY(EnsureCheatProcess());
|
||||
|
||||
CheatEntry* entry = GetCheatEntryById(cheat_id);
|
||||
R_UNLESS(entry != nullptr, ResultCheatUnknownId);
|
||||
R_UNLESS(entry->definition.num_opcodes != 0, ResultCheatUnknownId);
|
||||
|
||||
R_UNLESS(cheat_id != 0, ResultCheatCannotDisable);
|
||||
|
||||
entry->enabled = !entry->enabled;
|
||||
|
||||
// Trigger a VM reload.
|
||||
SetNeedsReloadVm(true);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::AddCheat(u32& out_cheat_id, bool enabled,
|
||||
const CheatDefinition& cheat_definition) {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
R_TRY(EnsureCheatProcess());
|
||||
|
||||
R_UNLESS(cheat_definition.num_opcodes != 0, ResultCheatInvalid);
|
||||
R_UNLESS(cheat_definition.num_opcodes <= cheat_definition.opcodes.size(), ResultCheatInvalid);
|
||||
|
||||
CheatEntry* new_entry = GetFreeCheatEntry();
|
||||
R_UNLESS(new_entry != nullptr, ResultCheatOutOfResource);
|
||||
|
||||
new_entry->enabled = enabled;
|
||||
new_entry->definition = cheat_definition;
|
||||
|
||||
// Trigger a VM reload.
|
||||
SetNeedsReloadVm(true);
|
||||
|
||||
// Set output id.
|
||||
out_cheat_id = new_entry->cheat_id;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::RemoveCheat(u32 cheat_id) {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
R_TRY(EnsureCheatProcess());
|
||||
R_UNLESS(cheat_id < MaxCheatCount, ResultCheatUnknownId);
|
||||
|
||||
ResetCheatEntry(cheat_id);
|
||||
SetNeedsReloadVm(true);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::ReadStaticRegister(u64& out_value, u64 register_index) {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
R_TRY(EnsureCheatProcess());
|
||||
R_UNLESS(register_index < CheatVirtualMachine::NumStaticRegisters, ResultCheatInvalid);
|
||||
|
||||
out_value = cheat_vm->GetStaticRegister(register_index);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::WriteStaticRegister(u64 register_index, u64 value) {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
R_TRY(EnsureCheatProcess());
|
||||
R_UNLESS(register_index < CheatVirtualMachine::NumStaticRegisters, ResultCheatInvalid);
|
||||
|
||||
cheat_vm->SetStaticRegister(register_index, value);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::ResetStaticRegisters() {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
R_TRY(EnsureCheatProcess());
|
||||
|
||||
cheat_vm->ResetStaticRegisters();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::SetMasterCheat(const CheatDefinition& cheat_definition) {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
R_TRY(EnsureCheatProcess());
|
||||
|
||||
R_UNLESS(cheat_definition.num_opcodes != 0, ResultCheatInvalid);
|
||||
R_UNLESS(cheat_definition.num_opcodes <= cheat_definition.opcodes.size(), ResultCheatInvalid);
|
||||
|
||||
cheat_entries[0] = {
|
||||
.enabled = true,
|
||||
.definition = cheat_definition,
|
||||
};
|
||||
|
||||
// Trigger a VM reload.
|
||||
SetNeedsReloadVm(true);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::GetFrozenAddressCount(u64& out_count) {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
R_TRY(EnsureCheatProcess());
|
||||
|
||||
out_count = std::distance(frozen_addresses_map.begin(), frozen_addresses_map.end());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::GetFrozenAddresses(u64& out_count, u64 offset,
|
||||
std::span<FrozenAddressEntry> out_frozen_address) {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
R_TRY(EnsureCheatProcess());
|
||||
|
||||
u64 total_count = 0, written_count = 0;
|
||||
for (const auto& [address, value] : frozen_addresses_map) {
|
||||
if (written_count >= out_frozen_address.size()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset <= total_count) {
|
||||
out_frozen_address[written_count].address = address;
|
||||
out_frozen_address[written_count].value = value;
|
||||
written_count++;
|
||||
}
|
||||
total_count++;
|
||||
}
|
||||
|
||||
out_count = written_count;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::GetFrozenAddress(FrozenAddressEntry& out_frozen_address_entry,
|
||||
u64 address) {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
R_TRY(EnsureCheatProcess());
|
||||
|
||||
const auto it = frozen_addresses_map.find(address);
|
||||
R_UNLESS(it != frozen_addresses_map.end(), ResultFrozenAddressNotFound);
|
||||
|
||||
out_frozen_address_entry = {
|
||||
.address = it->first,
|
||||
.value = it->second,
|
||||
};
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::EnableFrozenAddress(u64& out_value, u64 address, u64 width) {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
R_TRY(EnsureCheatProcess());
|
||||
|
||||
const auto it = frozen_addresses_map.find(address);
|
||||
R_UNLESS(it == frozen_addresses_map.end(), ResultFrozenAddressAlreadyExists);
|
||||
|
||||
FrozenAddressValue value{};
|
||||
value.width = static_cast<u8>(width);
|
||||
R_TRY(ReadCheatProcessMemoryUnsafe(address, &value.value, width));
|
||||
|
||||
frozen_addresses_map.insert({address, value});
|
||||
out_value = value.value;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheatProcessManager::DisableFrozenAddress(u64 address) {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
R_TRY(EnsureCheatProcess());
|
||||
|
||||
const auto it = frozen_addresses_map.find(address);
|
||||
R_UNLESS(it != frozen_addresses_map.end(), ResultFrozenAddressNotFound);
|
||||
|
||||
frozen_addresses_map.erase(it);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
u64 CheatProcessManager::HidKeysDown() const {
|
||||
const auto hid = system.ServiceManager().GetService<Service::HID::IHidServer>("hid");
|
||||
if (hid == nullptr) {
|
||||
LOG_WARNING(CheatEngine, "Attempted to read input state, but hid is not initialized!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto applet_resource = hid->GetResourceManager();
|
||||
if (applet_resource == nullptr || applet_resource->GetNpad() == nullptr) {
|
||||
LOG_WARNING(CheatEngine,
|
||||
"Attempted to read input state, but applet resource is not initialized!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto press_state = applet_resource->GetNpad()->GetAndResetPressState();
|
||||
return static_cast<u64>(press_state & Core::HID::NpadButton::All);
|
||||
}
|
||||
|
||||
void CheatProcessManager::DebugLog(u8 id, u64 value) const {
|
||||
LOG_INFO(CheatEngine, "Cheat triggered DebugLog: ID '{:01X}' Value '{:016X}'", id, value);
|
||||
}
|
||||
|
||||
void CheatProcessManager::CommandLog(std::string_view data) const {
|
||||
LOG_DEBUG(CheatEngine, "[DmntCheatVm]: {}",
|
||||
data.back() == '\n' ? data.substr(0, data.size() - 1) : data);
|
||||
}
|
||||
|
||||
void CheatProcessManager::FrameCallback(std::chrono::nanoseconds ns_late) {
|
||||
std::scoped_lock lk(cheat_lock);
|
||||
|
||||
if (cheat_vm == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (needs_reload_vm) {
|
||||
cheat_vm->LoadProgram(cheat_entries);
|
||||
needs_reload_vm = false;
|
||||
}
|
||||
|
||||
if (cheat_vm->GetProgramSize() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
cheat_vm->Execute(cheat_process_metadata);
|
||||
}
|
||||
|
||||
} // namespace Service::DMNT
|
@ -1,124 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <span>
|
||||
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/dmnt/dmnt_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
#include "common/intrusive_red_black_tree.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class KEvent;
|
||||
class KReadableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Kernel::Svc {
|
||||
struct MemoryInfo;
|
||||
}
|
||||
|
||||
namespace Service::DMNT {
|
||||
class CheatVirtualMachine;
|
||||
|
||||
class CheatProcessManager final {
|
||||
public:
|
||||
static constexpr size_t MaxCheatCount = 0x80;
|
||||
static constexpr size_t MaxFrozenAddressCount = 0x80;
|
||||
|
||||
CheatProcessManager(Core::System& system_);
|
||||
~CheatProcessManager();
|
||||
|
||||
void SetVirtualMachine(std::unique_ptr<CheatVirtualMachine> vm);
|
||||
|
||||
bool HasCheatProcess();
|
||||
Kernel::KReadableEvent& GetCheatProcessEvent() const;
|
||||
Result GetCheatProcessMetadata(CheatProcessMetadata& out_metadata);
|
||||
Result AttachToApplicationProcess(const std::array<u8, 0x20>& build_id, VAddr main_region_begin,
|
||||
u64 main_region_size);
|
||||
Result ForceOpenCheatProcess();
|
||||
Result PauseCheatProcess();
|
||||
Result PauseCheatProcessUnsafe();
|
||||
Result ResumeCheatProcess();
|
||||
Result ResumeCheatProcessUnsafe();
|
||||
Result ForceCloseCheatProcess();
|
||||
|
||||
Result GetCheatProcessMappingCount(u64& out_count);
|
||||
Result GetCheatProcessMappings(u64& out_count, u64 offset,
|
||||
std::span<Kernel::Svc::MemoryInfo> out_mappings);
|
||||
Result ReadCheatProcessMemory(u64 process_address, u64 size, std::span<u8> out_data);
|
||||
Result ReadCheatProcessMemoryUnsafe(u64 process_address, void* out_data, size_t size);
|
||||
Result WriteCheatProcessMemory(u64 process_address, u64 size, std::span<const u8> data);
|
||||
Result WriteCheatProcessMemoryUnsafe(u64 process_address, const void* data, size_t size);
|
||||
|
||||
Result QueryCheatProcessMemory(Out<Kernel::Svc::MemoryInfo> mapping, u64 address);
|
||||
Result GetCheatCount(u64& out_count);
|
||||
Result GetCheats(u64& out_count, u64 offset, std::span<CheatEntry> out_cheats);
|
||||
Result GetCheatById(CheatEntry* out_cheat, u32 cheat_id);
|
||||
Result ToggleCheat(u32 cheat_id);
|
||||
|
||||
Result AddCheat(u32& out_cheat_id, bool enabled, const CheatDefinition& cheat_definition);
|
||||
Result RemoveCheat(u32 cheat_id);
|
||||
Result ReadStaticRegister(u64& out_value, u64 register_index);
|
||||
Result WriteStaticRegister(u64 register_index, u64 value);
|
||||
Result ResetStaticRegisters();
|
||||
Result SetMasterCheat(const CheatDefinition& cheat_definition);
|
||||
Result GetFrozenAddressCount(u64& out_count);
|
||||
Result GetFrozenAddresses(u64& out_count, u64 offset,
|
||||
std::span<FrozenAddressEntry> out_frozen_address);
|
||||
Result GetFrozenAddress(FrozenAddressEntry& out_frozen_address_entry, u64 address);
|
||||
Result EnableFrozenAddress(u64& out_value, u64 address, u64 width);
|
||||
Result DisableFrozenAddress(u64 address);
|
||||
|
||||
u64 HidKeysDown() const;
|
||||
void DebugLog(u8 id, u64 value) const;
|
||||
void CommandLog(std::string_view data) const;
|
||||
|
||||
private:
|
||||
bool HasActiveCheatProcess();
|
||||
void CloseActiveCheatProcess();
|
||||
Result EnsureCheatProcess();
|
||||
void SetNeedsReloadVm(bool reload);
|
||||
void ResetCheatEntry(size_t i);
|
||||
void ResetAllCheatEntries();
|
||||
CheatEntry* GetCheatEntryById(size_t i);
|
||||
CheatEntry* GetCheatEntryByReadableName(const char* readable_name);
|
||||
CheatEntry* GetFreeCheatEntry();
|
||||
|
||||
void FrameCallback(std::chrono::nanoseconds ns_late);
|
||||
|
||||
static constexpr u64 InvalidHandle = 0;
|
||||
|
||||
mutable std::mutex cheat_lock;
|
||||
Kernel::KEvent* unsafe_break_event;
|
||||
|
||||
Kernel::KEvent* cheat_process_event;
|
||||
u64 cheat_process_debug_handle = InvalidHandle;
|
||||
CheatProcessMetadata cheat_process_metadata = {};
|
||||
|
||||
bool broken_unsafe = false;
|
||||
bool needs_reload_vm = false;
|
||||
std::unique_ptr<CheatVirtualMachine> cheat_vm;
|
||||
|
||||
bool enable_cheats_by_default = true;
|
||||
bool always_save_cheat_toggles = false;
|
||||
bool should_save_cheat_toggles = false;
|
||||
std::array<CheatEntry, MaxCheatCount> cheat_entries = {};
|
||||
// TODO: Replace with IntrusiveRedBlackTree
|
||||
std::map<u64, FrozenAddressValue> frozen_addresses_map = {};
|
||||
|
||||
Core::System& system;
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
std::shared_ptr<Core::Timing::EventType> update_event;
|
||||
Core::Timing::CoreTiming& core_timing;
|
||||
};
|
||||
|
||||
} // namespace Service::DMNT
|
@ -1,25 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/dmnt/cheat_interface.h"
|
||||
#include "core/hle/service/dmnt/cheat_process_manager.h"
|
||||
#include "core/hle/service/dmnt/cheat_virtual_machine.h"
|
||||
#include "core/hle/service/dmnt/dmnt.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
|
||||
namespace Service::DMNT {
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
|
||||
auto& cheat_manager = system.GetCheatManager();
|
||||
auto cheat_vm = std::make_unique<CheatVirtualMachine>(cheat_manager);
|
||||
cheat_manager.SetVirtualMachine(std::move(cheat_vm));
|
||||
|
||||
server_manager->RegisterNamedService("dmnt:cht",
|
||||
std::make_shared<ICheatInterface>(system, cheat_manager));
|
||||
ServerManager::RunServer(std::move(server_manager));
|
||||
}
|
||||
|
||||
} // namespace Service::DMNT
|
@ -1,14 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
};
|
||||
|
||||
namespace Service::DMNT {
|
||||
|
||||
void LoopProcess(Core::System& system);
|
||||
|
||||
} // namespace Service::DMNT
|
@ -1,25 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Service::DMNT {
|
||||
|
||||
constexpr Result ResultDebuggingDisabled(ErrorModule::DMNT, 2);
|
||||
|
||||
constexpr Result ResultCheatNotAttached(ErrorModule::DMNT, 6500);
|
||||
constexpr Result ResultCheatNullBuffer(ErrorModule::DMNT, 6501);
|
||||
constexpr Result ResultCheatInvalidBuffer(ErrorModule::DMNT, 6502);
|
||||
constexpr Result ResultCheatUnknownId(ErrorModule::DMNT, 6503);
|
||||
constexpr Result ResultCheatOutOfResource(ErrorModule::DMNT, 6504);
|
||||
constexpr Result ResultCheatInvalid(ErrorModule::DMNT, 6505);
|
||||
constexpr Result ResultCheatCannotDisable(ErrorModule::DMNT, 6506);
|
||||
constexpr Result ResultFrozenAddressInvalidWidth(ErrorModule::DMNT, 6600);
|
||||
constexpr Result ResultFrozenAddressAlreadyExists(ErrorModule::DMNT, 6601);
|
||||
constexpr Result ResultFrozenAddressNotFound(ErrorModule::DMNT, 6602);
|
||||
constexpr Result ResultFrozenAddressOutOfResource(ErrorModule::DMNT, 6603);
|
||||
constexpr Result ResultVirtualMachineInvalidConditionDepth(ErrorModule::DMNT, 6700);
|
||||
|
||||
} // namespace Service::DMNT
|
@ -1,54 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::DMNT {
|
||||
|
||||
struct MemoryRegionExtents {
|
||||
u64 base{};
|
||||
u64 size{};
|
||||
};
|
||||
static_assert(sizeof(MemoryRegionExtents) == 0x10, "MemoryRegionExtents is an invalid size");
|
||||
|
||||
struct CheatProcessMetadata {
|
||||
u64 process_id{};
|
||||
u64 program_id{};
|
||||
MemoryRegionExtents main_nso_extents{};
|
||||
MemoryRegionExtents heap_extents{};
|
||||
MemoryRegionExtents alias_extents{};
|
||||
MemoryRegionExtents aslr_extents{};
|
||||
std::array<u8, 0x20> main_nso_build_id{};
|
||||
};
|
||||
static_assert(sizeof(CheatProcessMetadata) == 0x70, "CheatProcessMetadata is an invalid size");
|
||||
|
||||
struct CheatDefinition {
|
||||
std::array<char, 0x40> readable_name;
|
||||
u32 num_opcodes;
|
||||
std::array<u32, 0x100> opcodes;
|
||||
};
|
||||
static_assert(sizeof(CheatDefinition) == 0x444, "CheatDefinition is an invalid size");
|
||||
|
||||
struct CheatEntry {
|
||||
bool enabled;
|
||||
u32 cheat_id;
|
||||
CheatDefinition definition;
|
||||
};
|
||||
static_assert(sizeof(CheatEntry) == 0x44C, "CheatEntry is an invalid size");
|
||||
static_assert(std::is_trivial_v<CheatEntry>, "CheatEntry type must be trivially copyable.");
|
||||
|
||||
struct FrozenAddressValue {
|
||||
u64 value;
|
||||
u8 width;
|
||||
};
|
||||
static_assert(sizeof(FrozenAddressValue) == 0x10, "FrozenAddressValue is an invalid size");
|
||||
|
||||
struct FrozenAddressEntry {
|
||||
u64 address;
|
||||
FrozenAddressValue value;
|
||||
};
|
||||
static_assert(sizeof(FrozenAddressEntry) == 0x18, "FrozenAddressEntry is an invalid size");
|
||||
|
||||
} // namespace Service::DMNT
|
@ -37,9 +37,14 @@ Result ARPManager::GetControlProperty(std::vector<u8>* out_control_property, u64
|
||||
|
||||
const auto iter = entries.find(title_id);
|
||||
if (iter == entries.end()) {
|
||||
if (entries.empty()) {
|
||||
return Glue::ResultProcessIdNotRegistered;
|
||||
}
|
||||
|
||||
*out_control_property = entries.begin()->second.control;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
*out_control_property = iter->second.control;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
@ -26,9 +26,12 @@ Service::PSC::Time::SystemClockContext g_report_ephemeral_clock_context{};
|
||||
template <typename T>
|
||||
T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys,
|
||||
const char* category, const char* name) {
|
||||
T v{};
|
||||
auto res = set_sys->GetSettingsItemValueImpl(v, category, name);
|
||||
std::vector<u8> interval_buf;
|
||||
auto res = set_sys->GetSettingsItemValueImpl(interval_buf, category, name);
|
||||
ASSERT(res == ResultSuccess);
|
||||
|
||||
T v{};
|
||||
std::memcpy(&v, interval_buf.data(), sizeof(T));
|
||||
return v;
|
||||
}
|
||||
|
||||
|
@ -23,11 +23,7 @@ void LoopProcess(Core::System& system) {
|
||||
std::shared_ptr<ResourceManager> resource_manager =
|
||||
std::make_shared<ResourceManager>(system, firmware_settings);
|
||||
|
||||
// TODO: Remove this hack when am is emulated properly.
|
||||
resource_manager->Initialize();
|
||||
resource_manager->RegisterAppletResourceUserId(system.ApplicationProcess()->GetProcessId(),
|
||||
true);
|
||||
resource_manager->SetAruidValidForVibration(system.ApplicationProcess()->GetProcessId(), true);
|
||||
|
||||
server_manager->RegisterNamedService(
|
||||
"hid", std::make_shared<IHidServer>(system, resource_manager, firmware_settings));
|
||||
|
@ -32,8 +32,8 @@ public:
|
||||
void GetTagInfo(HLERequestContext& ctx);
|
||||
void AttachActivateEvent(HLERequestContext& ctx);
|
||||
void AttachDeactivateEvent(HLERequestContext& ctx);
|
||||
void ReadMifare(HLERequestContext& ctx);
|
||||
void SetNfcEnabled(HLERequestContext& ctx);
|
||||
void ReadMifare(HLERequestContext& ctx);
|
||||
void WriteMifare(HLERequestContext& ctx);
|
||||
void SendCommandByPassThrough(HLERequestContext& ctx);
|
||||
|
||||
|
@ -419,6 +419,22 @@ void IGeneralService::GetCurrentNetworkProfile(HLERequestContext& ctx) {
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IGeneralService::EnumerateNetworkInterfaces(HLERequestContext& ctx) {
|
||||
LOG_ERROR(Service_NIFM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(0);
|
||||
}
|
||||
|
||||
void IGeneralService::EnumerateNetworkProfiles(HLERequestContext& ctx) {
|
||||
LOG_ERROR(Service_NIFM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(0);
|
||||
}
|
||||
|
||||
void IGeneralService::RemoveNetworkProfile(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
|
||||
@ -506,16 +522,27 @@ void IGeneralService::GetCurrentIpConfigInfo(HLERequestContext& ctx) {
|
||||
rb.PushRaw<IpConfigInfo>(ip_config_info);
|
||||
}
|
||||
|
||||
void IGeneralService::SetWirelessCommunicationEnabled(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto is_enabled{rp.Pop<bool>()};
|
||||
LOG_DEBUG(Service_NIFM, "called, is_enabled={}", is_enabled);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IGeneralService::IsWirelessCommunicationEnabled(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
const auto is_enabled = false;
|
||||
|
||||
LOG_DEBUG(Service_NIFM, "called, is_enabled={}", is_enabled);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u8>(1);
|
||||
rb.Push<u8>(is_enabled);
|
||||
}
|
||||
|
||||
void IGeneralService::GetInternetConnectionStatus(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIFM, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_NIFM, "(STUBBED) called");
|
||||
|
||||
struct Output {
|
||||
u8 type{static_cast<u8>(NetworkInterfaceType::WiFi_Ieee80211)};
|
||||
@ -565,6 +592,21 @@ void IGeneralService::IsAnyForegroundRequestAccepted(HLERequestContext& ctx) {
|
||||
rb.Push<u8>(is_accepted);
|
||||
}
|
||||
|
||||
void IGeneralService::ConfirmSystemAvailability(HLERequestContext& ctx) {
|
||||
LOG_ERROR(Service_NIFM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(false);
|
||||
}
|
||||
|
||||
void IGeneralService::GetCurrentAccessPoint(HLERequestContext& ctx) {
|
||||
LOG_ERROR(Service_NIFM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
IGeneralService::IGeneralService(Core::System& system_)
|
||||
: ServiceFramework{system_, "IGeneralService"}, network{system_.GetRoomNetwork()} {
|
||||
// clang-format off
|
||||
@ -573,8 +615,8 @@ IGeneralService::IGeneralService(Core::System& system_)
|
||||
{2, &IGeneralService::CreateScanRequest, "CreateScanRequest"},
|
||||
{4, &IGeneralService::CreateRequest, "CreateRequest"},
|
||||
{5, &IGeneralService::GetCurrentNetworkProfile, "GetCurrentNetworkProfile"},
|
||||
{6, nullptr, "EnumerateNetworkInterfaces"},
|
||||
{7, nullptr, "EnumerateNetworkProfiles"},
|
||||
{6, &IGeneralService::EnumerateNetworkInterfaces, "EnumerateNetworkInterfaces"},
|
||||
{7, &IGeneralService::EnumerateNetworkProfiles, "EnumerateNetworkProfiles"},
|
||||
{8, nullptr, "GetNetworkProfile"},
|
||||
{9, nullptr, "SetNetworkProfile"},
|
||||
{10, &IGeneralService::RemoveNetworkProfile, "RemoveNetworkProfile"},
|
||||
@ -583,7 +625,7 @@ IGeneralService::IGeneralService(Core::System& system_)
|
||||
{13, nullptr, "GetCurrentAccessPointOld"},
|
||||
{14, &IGeneralService::CreateTemporaryNetworkProfile, "CreateTemporaryNetworkProfile"},
|
||||
{15, &IGeneralService::GetCurrentIpConfigInfo, "GetCurrentIpConfigInfo"},
|
||||
{16, nullptr, "SetWirelessCommunicationEnabled"},
|
||||
{16, &IGeneralService::SetWirelessCommunicationEnabled, "SetWirelessCommunicationEnabled"},
|
||||
{17, &IGeneralService::IsWirelessCommunicationEnabled, "IsWirelessCommunicationEnabled"},
|
||||
{18, &IGeneralService::GetInternetConnectionStatus, "GetInternetConnectionStatus"},
|
||||
{19, nullptr, "SetEthernetCommunicationEnabled"},
|
||||
@ -600,10 +642,10 @@ IGeneralService::IGeneralService(Core::System& system_)
|
||||
{30, nullptr, "SetEthernetCommunicationEnabledForTest"},
|
||||
{31, nullptr, "GetTelemetorySystemEventReadableHandle"},
|
||||
{32, nullptr, "GetTelemetryInfo"},
|
||||
{33, nullptr, "ConfirmSystemAvailability"},
|
||||
{33, &IGeneralService::ConfirmSystemAvailability, "ConfirmSystemAvailability"},
|
||||
{34, nullptr, "SetBackgroundRequestEnabled"},
|
||||
{35, nullptr, "GetScanData"},
|
||||
{36, nullptr, "GetCurrentAccessPoint"},
|
||||
{36, &IGeneralService::GetCurrentAccessPoint, "GetCurrentAccessPoint"},
|
||||
{37, nullptr, "Shutdown"},
|
||||
{38, nullptr, "GetAllowedChannels"},
|
||||
{39, nullptr, "NotifyApplicationSuspended"},
|
||||
|
@ -27,15 +27,20 @@ private:
|
||||
void CreateScanRequest(HLERequestContext& ctx);
|
||||
void CreateRequest(HLERequestContext& ctx);
|
||||
void GetCurrentNetworkProfile(HLERequestContext& ctx);
|
||||
void EnumerateNetworkInterfaces(HLERequestContext& ctx);
|
||||
void EnumerateNetworkProfiles(HLERequestContext& ctx);
|
||||
void RemoveNetworkProfile(HLERequestContext& ctx);
|
||||
void GetCurrentIpAddress(HLERequestContext& ctx);
|
||||
void CreateTemporaryNetworkProfile(HLERequestContext& ctx);
|
||||
void GetCurrentIpConfigInfo(HLERequestContext& ctx);
|
||||
void SetWirelessCommunicationEnabled(HLERequestContext& ctx);
|
||||
void IsWirelessCommunicationEnabled(HLERequestContext& ctx);
|
||||
void GetInternetConnectionStatus(HLERequestContext& ctx);
|
||||
void IsEthernetCommunicationEnabled(HLERequestContext& ctx);
|
||||
void IsAnyInternetRequestAccepted(HLERequestContext& ctx);
|
||||
void IsAnyForegroundRequestAccepted(HLERequestContext& ctx);
|
||||
void ConfirmSystemAvailability(HLERequestContext& ctx);
|
||||
void GetCurrentAccessPoint(HLERequestContext& ctx);
|
||||
|
||||
Network::RoomNetwork& network;
|
||||
};
|
||||
|
@ -3,66 +3,25 @@
|
||||
|
||||
#include "common/scope_exit.h"
|
||||
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/service/am/process.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/kernel/svc_types.h"
|
||||
#include "core/hle/service/os/process.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Service::AM {
|
||||
namespace Service {
|
||||
|
||||
Process::Process(Core::System& system)
|
||||
: m_system(system), m_process(), m_main_thread_priority(), m_main_thread_stack_size(),
|
||||
m_program_id(), m_process_started() {}
|
||||
m_process_started() {}
|
||||
|
||||
Process::~Process() {
|
||||
this->Finalize();
|
||||
}
|
||||
|
||||
bool Process::Initialize(u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) {
|
||||
bool Process::Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result) {
|
||||
// First, ensure we are not holding another process.
|
||||
this->Finalize();
|
||||
|
||||
// Get the filesystem controller.
|
||||
auto& fsc = m_system.GetFileSystemController();
|
||||
|
||||
// Attempt to load program NCA.
|
||||
const FileSys::RegisteredCache* bis_system{};
|
||||
FileSys::VirtualFile nca_raw{};
|
||||
|
||||
// Get the program NCA from built-in storage.
|
||||
bis_system = fsc.GetSystemNANDContents();
|
||||
if (bis_system) {
|
||||
nca_raw = bis_system->GetEntryRaw(program_id, FileSys::ContentRecordType::Program);
|
||||
}
|
||||
|
||||
// Ensure we retrieved a program NCA.
|
||||
if (!nca_raw) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure we have a suitable version.
|
||||
if (minimum_key_generation > 0) {
|
||||
FileSys::NCA nca(nca_raw);
|
||||
if (nca.GetStatus() == Loader::ResultStatus::Success &&
|
||||
(nca.GetKeyGeneration() < minimum_key_generation ||
|
||||
nca.GetKeyGeneration() > maximum_key_generation)) {
|
||||
LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id,
|
||||
nca.GetKeyGeneration());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the appropriate loader to parse this NCA.
|
||||
auto app_loader = Loader::GetLoader(m_system, nca_raw, program_id, 0);
|
||||
|
||||
// Ensure we have a loader which can parse the NCA.
|
||||
if (!app_loader) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the process.
|
||||
auto* const process = Kernel::KProcess::Create(m_system.Kernel());
|
||||
Kernel::KProcess::Register(m_system.Kernel(), process);
|
||||
@ -73,7 +32,8 @@ bool Process::Initialize(u64 program_id, u8 minimum_key_generation, u8 maximum_k
|
||||
};
|
||||
|
||||
// Insert process modules into memory.
|
||||
const auto [load_result, load_parameters] = app_loader->Load(*process, m_system);
|
||||
const auto [load_result, load_parameters] = loader.Load(*process, m_system);
|
||||
out_load_result = load_result;
|
||||
|
||||
// Ensure loading was successful.
|
||||
if (load_result != Loader::ResultStatus::Success) {
|
||||
@ -114,7 +74,6 @@ void Process::Finalize() {
|
||||
m_process = nullptr;
|
||||
m_main_thread_priority = 0;
|
||||
m_main_thread_stack_size = 0;
|
||||
m_program_id = 0;
|
||||
m_process_started = false;
|
||||
}
|
||||
|
||||
@ -142,6 +101,31 @@ void Process::Terminate() {
|
||||
}
|
||||
}
|
||||
|
||||
void Process::ResetSignal() {
|
||||
if (m_process) {
|
||||
m_process->Reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool Process::IsRunning() const {
|
||||
if (m_process) {
|
||||
const auto state = m_process->GetState();
|
||||
return state == Kernel::KProcess::State::Running ||
|
||||
state == Kernel::KProcess::State::RunningAttached ||
|
||||
state == Kernel::KProcess::State::DebugBreak;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Process::IsTerminated() const {
|
||||
if (m_process) {
|
||||
return m_process->IsTerminated();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 Process::GetProcessId() const {
|
||||
if (m_process) {
|
||||
return m_process->GetProcessId();
|
||||
@ -150,4 +134,19 @@ u64 Process::GetProcessId() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
u64 Process::GetProgramId() const {
|
||||
if (m_process) {
|
||||
return m_process->GetProgramId();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Process::Suspend(bool suspended) {
|
||||
if (m_process) {
|
||||
m_process->SetActivity(suspended ? Kernel::Svc::ProcessActivity::Paused
|
||||
: Kernel::Svc::ProcessActivity::Runnable);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service
|
@ -3,38 +3,47 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KProcess;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::AM {
|
||||
namespace Loader {
|
||||
class AppLoader;
|
||||
enum class ResultStatus : u16;
|
||||
} // namespace Loader
|
||||
|
||||
namespace Kernel {
|
||||
class KProcess;
|
||||
}
|
||||
|
||||
namespace Service {
|
||||
|
||||
class Process {
|
||||
public:
|
||||
explicit Process(Core::System& system);
|
||||
~Process();
|
||||
|
||||
bool Initialize(u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation);
|
||||
bool Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result);
|
||||
void Finalize();
|
||||
|
||||
bool Run();
|
||||
void Terminate();
|
||||
void Suspend(bool suspended);
|
||||
void ResetSignal();
|
||||
|
||||
bool IsInitialized() const {
|
||||
return m_process != nullptr;
|
||||
}
|
||||
|
||||
bool IsRunning() const;
|
||||
bool IsTerminated() const;
|
||||
|
||||
u64 GetProcessId() const;
|
||||
u64 GetProgramId() const {
|
||||
return m_program_id;
|
||||
}
|
||||
Kernel::KProcess* GetProcess() const {
|
||||
u64 GetProgramId() const;
|
||||
|
||||
Kernel::KProcess* GetHandle() const {
|
||||
return m_process;
|
||||
}
|
||||
|
||||
@ -43,8 +52,7 @@ private:
|
||||
Kernel::KProcess* m_process{};
|
||||
s32 m_main_thread_priority{};
|
||||
u64 m_main_thread_stack_size{};
|
||||
u64 m_program_id{};
|
||||
bool m_process_started{};
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
} // namespace Service
|
@ -5,7 +5,7 @@
|
||||
|
||||
#include "core/hle/service/acc/acc.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/aoc/addon_content_manager.h"
|
||||
#include "core/hle/service/aoc/aoc_u.h"
|
||||
#include "core/hle/service/apm/apm.h"
|
||||
#include "core/hle/service/audio/audio.h"
|
||||
#include "core/hle/service/bcat/bcat.h"
|
||||
@ -13,7 +13,6 @@
|
||||
#include "core/hle/service/btdrv/btdrv.h"
|
||||
#include "core/hle/service/btm/btm.h"
|
||||
#include "core/hle/service/caps/caps.h"
|
||||
#include "core/hle/service/dmnt/dmnt.h"
|
||||
#include "core/hle/service/erpt/erpt.h"
|
||||
#include "core/hle/service/es/es.h"
|
||||
#include "core/hle/service/eupld/eupld.h"
|
||||
@ -129,7 +128,6 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
|
||||
kernel.RunOnGuestCoreProcess("spl", [&] { SPL::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("ssl", [&] { SSL::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("usb", [&] { USB::LoopProcess(system); });
|
||||
kernel.RunOnGuestCoreProcess("dmnt", [&] { DMNT::LoopProcess(system); });
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
|
@ -52,10 +52,6 @@ SystemSettings DefaultSystemSettings() {
|
||||
settings.battery_percentage_flag = true;
|
||||
settings.chinese_traditional_input_method = ChineseTraditionalInputMethod::Unknown0;
|
||||
settings.vibration_master_volume = 1.0f;
|
||||
settings.touch_screen_mode = TouchScreenMode::Standard;
|
||||
settings.nfc_enable_flag = true;
|
||||
settings.bluetooth_enable_flag = true;
|
||||
settings.wireless_lan_enable_flag = true;
|
||||
|
||||
const auto language_code =
|
||||
available_language_codes[static_cast<s32>(::Settings::values.language_index.GetValue())];
|
||||
|
@ -26,7 +26,7 @@
|
||||
namespace Service::Set {
|
||||
|
||||
namespace {
|
||||
constexpr u32 SETTINGS_VERSION{4u};
|
||||
constexpr u32 SETTINGS_VERSION{3u};
|
||||
constexpr auto SETTINGS_MAGIC = Common::MakeMagic('y', 'u', 'z', 'u', '_', 's', 'e', 't');
|
||||
struct SettingsHeader {
|
||||
u64 magic;
|
||||
@ -307,9 +307,6 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
|
||||
|
||||
SetupSettings();
|
||||
|
||||
m_system_settings.region_code =
|
||||
static_cast<SystemRegionCode>(::Settings::values.region_index.GetValue());
|
||||
|
||||
// TODO: Remove this when starter applet is fully functional
|
||||
EulaVersion eula_version{
|
||||
.version = 0x10000,
|
||||
@ -715,7 +712,7 @@ Result ISystemSettingsServer::GetSettingsItemValueSize(
|
||||
}
|
||||
|
||||
Result ISystemSettingsServer::GetSettingsItemValue(
|
||||
Out<u64> out_size, OutBuffer<BufferAttr_HipcMapAlias> out_data,
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_data,
|
||||
InLargeData<SettingItemName, BufferAttr_HipcPointer> setting_category_buffer,
|
||||
InLargeData<SettingItemName, BufferAttr_HipcPointer> setting_name_buffer) {
|
||||
const std::string setting_category{Common::StringFromBuffer(*setting_category_buffer)};
|
||||
@ -723,7 +720,7 @@ Result ISystemSettingsServer::GetSettingsItemValue(
|
||||
|
||||
LOG_INFO(Service_SET, "called, category={}, name={}", setting_category, setting_name);
|
||||
|
||||
R_RETURN(GetSettingsItemValueImpl(out_data, *out_size, setting_category, setting_name));
|
||||
R_RETURN(GetSettingsItemValueImpl(out_data, setting_category, setting_name));
|
||||
}
|
||||
|
||||
Result ISystemSettingsServer::GetTvSettings(Out<TvSettings> out_tv_settings) {
|
||||
@ -1363,16 +1360,13 @@ void ISystemSettingsServer::SetSaveNeeded() {
|
||||
m_save_needed = true;
|
||||
}
|
||||
|
||||
Result ISystemSettingsServer::GetSettingsItemValueImpl(std::span<u8> out_value, u64& out_size,
|
||||
Result ISystemSettingsServer::GetSettingsItemValueImpl(std::vector<u8>& out_value,
|
||||
const std::string& category,
|
||||
const std::string& name) {
|
||||
auto settings{GetSettings()};
|
||||
R_UNLESS(settings.contains(category) && settings[category].contains(name), ResultUnknown);
|
||||
|
||||
ASSERT_MSG(out_value.size() >= settings[category][name].size(),
|
||||
"Stored type is bigger than requested type");
|
||||
out_size = std::min<u64>(settings[category][name].size(), out_value.size());
|
||||
std::memcpy(out_value.data(), settings[category][name].data(), out_size);
|
||||
out_value = settings[category][name];
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
@ -34,17 +34,20 @@ public:
|
||||
explicit ISystemSettingsServer(Core::System& system_);
|
||||
~ISystemSettingsServer() override;
|
||||
|
||||
Result GetSettingsItemValueImpl(std::span<u8> out_value, u64& out_size,
|
||||
const std::string& category, const std::string& name);
|
||||
Result GetSettingsItemValueImpl(std::vector<u8>& out_value, const std::string& category,
|
||||
const std::string& name);
|
||||
|
||||
template <typename T>
|
||||
Result GetSettingsItemValueImpl(T& out_value, const std::string& category,
|
||||
Result GetSettingsItemValueImpl(T& value, const std::string& category,
|
||||
const std::string& name) {
|
||||
u64 data_size{};
|
||||
std::vector<u8> data(sizeof(T));
|
||||
R_TRY(GetSettingsItemValueImpl(data, data_size, category, name));
|
||||
std::memcpy(&out_value, data.data(), data_size);
|
||||
R_SUCCEED();
|
||||
std::vector<u8> data;
|
||||
const auto result = GetSettingsItemValueImpl(data, category, name);
|
||||
if (result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
ASSERT(data.size() >= sizeof(T));
|
||||
std::memcpy(&value, data.data(), sizeof(T));
|
||||
return result;
|
||||
}
|
||||
|
||||
public:
|
||||
@ -81,7 +84,7 @@ public:
|
||||
InLargeData<SettingItemName, BufferAttr_HipcPointer> setting_category_buffer,
|
||||
InLargeData<SettingItemName, BufferAttr_HipcPointer> setting_name_buf);
|
||||
Result GetSettingsItemValue(
|
||||
Out<u64> out_size, OutBuffer<BufferAttr_HipcMapAlias> out_data,
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_data,
|
||||
InLargeData<SettingItemName, BufferAttr_HipcPointer> setting_category_buffer,
|
||||
InLargeData<SettingItemName, BufferAttr_HipcPointer> setting_name_buffer);
|
||||
Result GetTvSettings(Out<TvSettings> out_tv_settings);
|
||||
|
288
src/core/memory/cheat_engine.cpp
Normal file
288
src/core/memory/cheat_engine.cpp
Normal file
@ -0,0 +1,288 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <locale>
|
||||
#include "common/hex_util.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/arm/debug.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/k_page_table.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_process_page_table.h"
|
||||
#include "core/hle/kernel/svc_types.h"
|
||||
#include "core/hle/service/hid/hid_server.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/memory/cheat_engine.h"
|
||||
#include "hid_core/resource_manager.h"
|
||||
#include "hid_core/resources/npad/npad.h"
|
||||
|
||||
namespace Core::Memory {
|
||||
namespace {
|
||||
constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12};
|
||||
|
||||
std::string_view ExtractName(std::size_t& out_name_size, std::string_view data,
|
||||
std::size_t start_index, char match) {
|
||||
auto end_index = start_index;
|
||||
while (data[end_index] != match) {
|
||||
++end_index;
|
||||
if (end_index > data.size()) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
out_name_size = end_index - start_index;
|
||||
|
||||
// Clamp name if it's too big
|
||||
if (out_name_size > sizeof(CheatDefinition::readable_name)) {
|
||||
end_index = start_index + sizeof(CheatDefinition::readable_name);
|
||||
}
|
||||
|
||||
return data.substr(start_index, end_index - start_index);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
StandardVmCallbacks::StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_)
|
||||
: metadata{metadata_}, system{system_} {}
|
||||
|
||||
StandardVmCallbacks::~StandardVmCallbacks() = default;
|
||||
|
||||
void StandardVmCallbacks::MemoryReadUnsafe(VAddr address, void* data, u64 size) {
|
||||
// Return zero on invalid address
|
||||
if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
|
||||
std::memset(data, 0, size);
|
||||
return;
|
||||
}
|
||||
|
||||
system.ApplicationMemory().ReadBlock(address, data, size);
|
||||
}
|
||||
|
||||
void StandardVmCallbacks::MemoryWriteUnsafe(VAddr address, const void* data, u64 size) {
|
||||
// Skip invalid memory write address
|
||||
if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (system.ApplicationMemory().WriteBlock(address, data, size)) {
|
||||
Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), address, size);
|
||||
}
|
||||
}
|
||||
|
||||
u64 StandardVmCallbacks::HidKeysDown() {
|
||||
const auto hid = system.ServiceManager().GetService<Service::HID::IHidServer>("hid");
|
||||
if (hid == nullptr) {
|
||||
LOG_WARNING(CheatEngine, "Attempted to read input state, but hid is not initialized!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto applet_resource = hid->GetResourceManager();
|
||||
if (applet_resource == nullptr || applet_resource->GetNpad() == nullptr) {
|
||||
LOG_WARNING(CheatEngine,
|
||||
"Attempted to read input state, but applet resource is not initialized!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto press_state = applet_resource->GetNpad()->GetAndResetPressState();
|
||||
return static_cast<u64>(press_state & HID::NpadButton::All);
|
||||
}
|
||||
|
||||
void StandardVmCallbacks::PauseProcess() {
|
||||
if (system.ApplicationProcess()->IsSuspended()) {
|
||||
return;
|
||||
}
|
||||
system.ApplicationProcess()->SetActivity(Kernel::Svc::ProcessActivity::Paused);
|
||||
}
|
||||
|
||||
void StandardVmCallbacks::ResumeProcess() {
|
||||
if (!system.ApplicationProcess()->IsSuspended()) {
|
||||
return;
|
||||
}
|
||||
system.ApplicationProcess()->SetActivity(Kernel::Svc::ProcessActivity::Runnable);
|
||||
}
|
||||
|
||||
void StandardVmCallbacks::DebugLog(u8 id, u64 value) {
|
||||
LOG_INFO(CheatEngine, "Cheat triggered DebugLog: ID '{:01X}' Value '{:016X}'", id, value);
|
||||
}
|
||||
|
||||
void StandardVmCallbacks::CommandLog(std::string_view data) {
|
||||
LOG_DEBUG(CheatEngine, "[DmntCheatVm]: {}",
|
||||
data.back() == '\n' ? data.substr(0, data.size() - 1) : data);
|
||||
}
|
||||
|
||||
bool StandardVmCallbacks::IsAddressInRange(VAddr in) const {
|
||||
if ((in < metadata.main_nso_extents.base ||
|
||||
in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) &&
|
||||
(in < metadata.heap_extents.base ||
|
||||
in >= metadata.heap_extents.base + metadata.heap_extents.size) &&
|
||||
(in < metadata.alias_extents.base ||
|
||||
in >= metadata.alias_extents.base + metadata.alias_extents.size) &&
|
||||
(in < metadata.aslr_extents.base ||
|
||||
in >= metadata.aslr_extents.base + metadata.aslr_extents.size)) {
|
||||
LOG_DEBUG(CheatEngine,
|
||||
"Cheat attempting to access memory at invalid address={:016X}, if this "
|
||||
"persists, "
|
||||
"the cheat may be incorrect. However, this may be normal early in execution if "
|
||||
"the game has not properly set up yet.",
|
||||
in);
|
||||
return false; ///< Invalid addresses will hard crash
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CheatParser::~CheatParser() = default;
|
||||
|
||||
TextCheatParser::~TextCheatParser() = default;
|
||||
|
||||
std::vector<CheatEntry> TextCheatParser::Parse(std::string_view data) const {
|
||||
std::vector<CheatEntry> out(1);
|
||||
std::optional<u64> current_entry;
|
||||
|
||||
for (std::size_t i = 0; i < data.size(); ++i) {
|
||||
if (::isspace(data[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (data[i] == '{') {
|
||||
current_entry = 0;
|
||||
|
||||
if (out[*current_entry].definition.num_opcodes > 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::size_t name_size{};
|
||||
const auto name = ExtractName(name_size, data, i + 1, '}');
|
||||
if (name.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
|
||||
std::min<std::size_t>(out[*current_entry].definition.readable_name.size(),
|
||||
name.size()));
|
||||
out[*current_entry]
|
||||
.definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
|
||||
'\0';
|
||||
|
||||
i += name_size + 1;
|
||||
} else if (data[i] == '[') {
|
||||
current_entry = out.size();
|
||||
out.emplace_back();
|
||||
|
||||
std::size_t name_size{};
|
||||
const auto name = ExtractName(name_size, data, i + 1, ']');
|
||||
if (name.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
|
||||
std::min<std::size_t>(out[*current_entry].definition.readable_name.size(),
|
||||
name.size()));
|
||||
out[*current_entry]
|
||||
.definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
|
||||
'\0';
|
||||
|
||||
i += name_size + 1;
|
||||
} else if (::isxdigit(data[i])) {
|
||||
if (!current_entry || out[*current_entry].definition.num_opcodes >=
|
||||
out[*current_entry].definition.opcodes.size()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto hex = std::string(data.substr(i, 8));
|
||||
if (!std::all_of(hex.begin(), hex.end(), ::isxdigit)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto value = static_cast<u32>(std::strtoul(hex.c_str(), nullptr, 0x10));
|
||||
out[*current_entry].definition.opcodes[out[*current_entry].definition.num_opcodes++] =
|
||||
value;
|
||||
|
||||
i += 8;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
out[0].enabled = out[0].definition.num_opcodes > 0;
|
||||
out[0].cheat_id = 0;
|
||||
|
||||
for (u32 i = 1; i < out.size(); ++i) {
|
||||
out[i].enabled = out[i].definition.num_opcodes > 0;
|
||||
out[i].cheat_id = i;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
CheatEngine::CheatEngine(System& system_, std::vector<CheatEntry> cheats_,
|
||||
const std::array<u8, 0x20>& build_id_)
|
||||
: vm{std::make_unique<StandardVmCallbacks>(system_, metadata)},
|
||||
cheats(std::move(cheats_)), core_timing{system_.CoreTiming()}, system{system_} {
|
||||
metadata.main_nso_build_id = build_id_;
|
||||
}
|
||||
|
||||
CheatEngine::~CheatEngine() {
|
||||
core_timing.UnscheduleEvent(event);
|
||||
}
|
||||
|
||||
void CheatEngine::Initialize() {
|
||||
event = Core::Timing::CreateEvent(
|
||||
"CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
|
||||
[this](s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
FrameCallback(ns_late);
|
||||
return std::nullopt;
|
||||
});
|
||||
core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event);
|
||||
|
||||
metadata.process_id = system.ApplicationProcess()->GetProcessId();
|
||||
metadata.title_id = system.GetApplicationProcessProgramID();
|
||||
|
||||
const auto& page_table = system.ApplicationProcess()->GetPageTable();
|
||||
metadata.heap_extents = {
|
||||
.base = GetInteger(page_table.GetHeapRegionStart()),
|
||||
.size = page_table.GetHeapRegionSize(),
|
||||
};
|
||||
metadata.aslr_extents = {
|
||||
.base = GetInteger(page_table.GetAliasCodeRegionStart()),
|
||||
.size = page_table.GetAliasCodeRegionSize(),
|
||||
};
|
||||
metadata.alias_extents = {
|
||||
.base = GetInteger(page_table.GetAliasRegionStart()),
|
||||
.size = page_table.GetAliasRegionSize(),
|
||||
};
|
||||
|
||||
is_pending_reload.exchange(true);
|
||||
}
|
||||
|
||||
void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) {
|
||||
metadata.main_nso_extents = {
|
||||
.base = main_region_begin,
|
||||
.size = main_region_size,
|
||||
};
|
||||
}
|
||||
|
||||
void CheatEngine::Reload(std::vector<CheatEntry> reload_cheats) {
|
||||
cheats = std::move(reload_cheats);
|
||||
is_pending_reload.exchange(true);
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
|
||||
|
||||
void CheatEngine::FrameCallback(std::chrono::nanoseconds ns_late) {
|
||||
if (is_pending_reload.exchange(false)) {
|
||||
vm.LoadProgram(cheats);
|
||||
}
|
||||
|
||||
if (vm.GetProgramSize() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
MICROPROFILE_SCOPE(Cheat_Engine);
|
||||
|
||||
vm.Execute(metadata);
|
||||
}
|
||||
|
||||
} // namespace Core::Memory
|
88
src/core/memory/cheat_engine.h
Normal file
88
src/core/memory/cheat_engine.h
Normal file
@ -0,0 +1,88 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "core/memory/dmnt_cheat_types.h"
|
||||
#include "core/memory/dmnt_cheat_vm.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Core::Timing {
|
||||
class CoreTiming;
|
||||
struct EventType;
|
||||
} // namespace Core::Timing
|
||||
|
||||
namespace Core::Memory {
|
||||
|
||||
class StandardVmCallbacks : public DmntCheatVm::Callbacks {
|
||||
public:
|
||||
StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_);
|
||||
~StandardVmCallbacks() override;
|
||||
|
||||
void MemoryReadUnsafe(VAddr address, void* data, u64 size) override;
|
||||
void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) override;
|
||||
u64 HidKeysDown() override;
|
||||
void PauseProcess() override;
|
||||
void ResumeProcess() override;
|
||||
void DebugLog(u8 id, u64 value) override;
|
||||
void CommandLog(std::string_view data) override;
|
||||
|
||||
private:
|
||||
bool IsAddressInRange(VAddr address) const;
|
||||
|
||||
const CheatProcessMetadata& metadata;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
// Intermediary class that parses a text file or other disk format for storing cheats into a
|
||||
// CheatList object, that can be used for execution.
|
||||
class CheatParser {
|
||||
public:
|
||||
virtual ~CheatParser();
|
||||
|
||||
[[nodiscard]] virtual std::vector<CheatEntry> Parse(std::string_view data) const = 0;
|
||||
};
|
||||
|
||||
// CheatParser implementation that parses text files
|
||||
class TextCheatParser final : public CheatParser {
|
||||
public:
|
||||
~TextCheatParser() override;
|
||||
|
||||
[[nodiscard]] std::vector<CheatEntry> Parse(std::string_view data) const override;
|
||||
};
|
||||
|
||||
// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming
|
||||
class CheatEngine final {
|
||||
public:
|
||||
CheatEngine(System& system_, std::vector<CheatEntry> cheats_,
|
||||
const std::array<u8, 0x20>& build_id_);
|
||||
~CheatEngine();
|
||||
|
||||
void Initialize();
|
||||
void SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size);
|
||||
|
||||
void Reload(std::vector<CheatEntry> reload_cheats);
|
||||
|
||||
private:
|
||||
void FrameCallback(std::chrono::nanoseconds ns_late);
|
||||
|
||||
DmntCheatVm vm;
|
||||
CheatProcessMetadata metadata;
|
||||
|
||||
std::vector<CheatEntry> cheats;
|
||||
std::atomic_bool is_pending_reload{false};
|
||||
|
||||
std::shared_ptr<Core::Timing::EventType> event;
|
||||
Core::Timing::CoreTiming& core_timing;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
} // namespace Core::Memory
|
37
src/core/memory/dmnt_cheat_types.h
Normal file
37
src/core/memory/dmnt_cheat_types.h
Normal file
@ -0,0 +1,37 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core::Memory {
|
||||
|
||||
struct MemoryRegionExtents {
|
||||
u64 base{};
|
||||
u64 size{};
|
||||
};
|
||||
|
||||
struct CheatProcessMetadata {
|
||||
u64 process_id{};
|
||||
u64 title_id{};
|
||||
MemoryRegionExtents main_nso_extents{};
|
||||
MemoryRegionExtents heap_extents{};
|
||||
MemoryRegionExtents alias_extents{};
|
||||
MemoryRegionExtents aslr_extents{};
|
||||
std::array<u8, 0x20> main_nso_build_id{};
|
||||
};
|
||||
|
||||
struct CheatDefinition {
|
||||
std::array<char, 0x40> readable_name{};
|
||||
u32 num_opcodes{};
|
||||
std::array<u32, 0x100> opcodes{};
|
||||
};
|
||||
|
||||
struct CheatEntry {
|
||||
bool enabled{};
|
||||
u32 cheat_id{};
|
||||
CheatDefinition definition{};
|
||||
};
|
||||
|
||||
} // namespace Core::Memory
|
@ -1,221 +1,226 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fmt/printf.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/hle/service/dmnt/cheat_process_manager.h"
|
||||
#include "core/hle/service/dmnt/cheat_virtual_machine.h"
|
||||
#include "core/memory/dmnt_cheat_types.h"
|
||||
#include "core/memory/dmnt_cheat_vm.h"
|
||||
|
||||
namespace Service::DMNT {
|
||||
namespace Core::Memory {
|
||||
|
||||
CheatVirtualMachine::CheatVirtualMachine(CheatProcessManager& cheat_manager)
|
||||
: manager(cheat_manager) {}
|
||||
DmntCheatVm::DmntCheatVm(std::unique_ptr<Callbacks> callbacks_)
|
||||
: callbacks(std::move(callbacks_)) {}
|
||||
|
||||
CheatVirtualMachine::~CheatVirtualMachine() = default;
|
||||
DmntCheatVm::~DmntCheatVm() = default;
|
||||
|
||||
void CheatVirtualMachine::DebugLog(u32 log_id, u64 value) const {
|
||||
manager.DebugLog(static_cast<u8>(log_id), value);
|
||||
void DmntCheatVm::DebugLog(u32 log_id, u64 value) {
|
||||
callbacks->DebugLog(static_cast<u8>(log_id), value);
|
||||
}
|
||||
|
||||
void CheatVirtualMachine::LogOpcode(const CheatVmOpcode& opcode) const {
|
||||
void DmntCheatVm::LogOpcode(const CheatVmOpcode& opcode) {
|
||||
if (auto store_static = std::get_if<StoreStaticOpcode>(&opcode.opcode)) {
|
||||
manager.CommandLog("Opcode: Store Static");
|
||||
manager.CommandLog(fmt::format("Bit Width: {:X}", store_static->bit_width));
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog("Opcode: Store Static");
|
||||
callbacks->CommandLog(fmt::format("Bit Width: {:X}", store_static->bit_width));
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Mem Type: {:X}", static_cast<u32>(store_static->mem_type)));
|
||||
manager.CommandLog(fmt::format("Reg Idx: {:X}", store_static->offset_register));
|
||||
manager.CommandLog(fmt::format("Rel Addr: {:X}", store_static->rel_address));
|
||||
manager.CommandLog(fmt::format("Value: {:X}", store_static->value.bit64));
|
||||
callbacks->CommandLog(fmt::format("Reg Idx: {:X}", store_static->offset_register));
|
||||
callbacks->CommandLog(fmt::format("Rel Addr: {:X}", store_static->rel_address));
|
||||
callbacks->CommandLog(fmt::format("Value: {:X}", store_static->value.bit64));
|
||||
} else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&opcode.opcode)) {
|
||||
manager.CommandLog("Opcode: Begin Conditional");
|
||||
manager.CommandLog(fmt::format("Bit Width: {:X}", begin_cond->bit_width));
|
||||
manager.CommandLog(fmt::format("Mem Type: {:X}", static_cast<u32>(begin_cond->mem_type)));
|
||||
manager.CommandLog(fmt::format("Cond Type: {:X}", static_cast<u32>(begin_cond->cond_type)));
|
||||
manager.CommandLog(fmt::format("Rel Addr: {:X}", begin_cond->rel_address));
|
||||
manager.CommandLog(fmt::format("Value: {:X}", begin_cond->value.bit64));
|
||||
callbacks->CommandLog("Opcode: Begin Conditional");
|
||||
callbacks->CommandLog(fmt::format("Bit Width: {:X}", begin_cond->bit_width));
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Mem Type: {:X}", static_cast<u32>(begin_cond->mem_type)));
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Cond Type: {:X}", static_cast<u32>(begin_cond->cond_type)));
|
||||
callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_cond->rel_address));
|
||||
callbacks->CommandLog(fmt::format("Value: {:X}", begin_cond->value.bit64));
|
||||
} else if (std::holds_alternative<EndConditionalOpcode>(opcode.opcode)) {
|
||||
manager.CommandLog("Opcode: End Conditional");
|
||||
callbacks->CommandLog("Opcode: End Conditional");
|
||||
} else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&opcode.opcode)) {
|
||||
if (ctrl_loop->start_loop) {
|
||||
manager.CommandLog("Opcode: Start Loop");
|
||||
manager.CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index));
|
||||
manager.CommandLog(fmt::format("Num Iters: {:X}", ctrl_loop->num_iters));
|
||||
callbacks->CommandLog("Opcode: Start Loop");
|
||||
callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index));
|
||||
callbacks->CommandLog(fmt::format("Num Iters: {:X}", ctrl_loop->num_iters));
|
||||
} else {
|
||||
manager.CommandLog("Opcode: End Loop");
|
||||
manager.CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index));
|
||||
callbacks->CommandLog("Opcode: End Loop");
|
||||
callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index));
|
||||
}
|
||||
} else if (auto ldr_static = std::get_if<LoadRegisterStaticOpcode>(&opcode.opcode)) {
|
||||
manager.CommandLog("Opcode: Load Register Static");
|
||||
manager.CommandLog(fmt::format("Reg Idx: {:X}", ldr_static->reg_index));
|
||||
manager.CommandLog(fmt::format("Value: {:X}", ldr_static->value));
|
||||
callbacks->CommandLog("Opcode: Load Register Static");
|
||||
callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ldr_static->reg_index));
|
||||
callbacks->CommandLog(fmt::format("Value: {:X}", ldr_static->value));
|
||||
} else if (auto ldr_memory = std::get_if<LoadRegisterMemoryOpcode>(&opcode.opcode)) {
|
||||
manager.CommandLog("Opcode: Load Register Memory");
|
||||
manager.CommandLog(fmt::format("Bit Width: {:X}", ldr_memory->bit_width));
|
||||
manager.CommandLog(fmt::format("Reg Idx: {:X}", ldr_memory->reg_index));
|
||||
manager.CommandLog(fmt::format("Mem Type: {:X}", static_cast<u32>(ldr_memory->mem_type)));
|
||||
manager.CommandLog(fmt::format("From Reg: {:d}", ldr_memory->load_from_reg));
|
||||
manager.CommandLog(fmt::format("Rel Addr: {:X}", ldr_memory->rel_address));
|
||||
callbacks->CommandLog("Opcode: Load Register Memory");
|
||||
callbacks->CommandLog(fmt::format("Bit Width: {:X}", ldr_memory->bit_width));
|
||||
callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ldr_memory->reg_index));
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Mem Type: {:X}", static_cast<u32>(ldr_memory->mem_type)));
|
||||
callbacks->CommandLog(fmt::format("From Reg: {:d}", ldr_memory->load_from_reg));
|
||||
callbacks->CommandLog(fmt::format("Rel Addr: {:X}", ldr_memory->rel_address));
|
||||
} else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&opcode.opcode)) {
|
||||
manager.CommandLog("Opcode: Store Static to Address");
|
||||
manager.CommandLog(fmt::format("Bit Width: {:X}", str_static->bit_width));
|
||||
manager.CommandLog(fmt::format("Reg Idx: {:X}", str_static->reg_index));
|
||||
callbacks->CommandLog("Opcode: Store Static to Address");
|
||||
callbacks->CommandLog(fmt::format("Bit Width: {:X}", str_static->bit_width));
|
||||
callbacks->CommandLog(fmt::format("Reg Idx: {:X}", str_static->reg_index));
|
||||
if (str_static->add_offset_reg) {
|
||||
manager.CommandLog(fmt::format("O Reg Idx: {:X}", str_static->offset_reg_index));
|
||||
callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", str_static->offset_reg_index));
|
||||
}
|
||||
manager.CommandLog(fmt::format("Incr Reg: {:d}", str_static->increment_reg));
|
||||
manager.CommandLog(fmt::format("Value: {:X}", str_static->value));
|
||||
callbacks->CommandLog(fmt::format("Incr Reg: {:d}", str_static->increment_reg));
|
||||
callbacks->CommandLog(fmt::format("Value: {:X}", str_static->value));
|
||||
} else if (auto perform_math_static =
|
||||
std::get_if<PerformArithmeticStaticOpcode>(&opcode.opcode)) {
|
||||
manager.CommandLog("Opcode: Perform Static Arithmetic");
|
||||
manager.CommandLog(fmt::format("Bit Width: {:X}", perform_math_static->bit_width));
|
||||
manager.CommandLog(fmt::format("Reg Idx: {:X}", perform_math_static->reg_index));
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog("Opcode: Perform Static Arithmetic");
|
||||
callbacks->CommandLog(fmt::format("Bit Width: {:X}", perform_math_static->bit_width));
|
||||
callbacks->CommandLog(fmt::format("Reg Idx: {:X}", perform_math_static->reg_index));
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Math Type: {:X}", static_cast<u32>(perform_math_static->math_type)));
|
||||
manager.CommandLog(fmt::format("Value: {:X}", perform_math_static->value));
|
||||
callbacks->CommandLog(fmt::format("Value: {:X}", perform_math_static->value));
|
||||
} else if (auto begin_keypress_cond =
|
||||
std::get_if<BeginKeypressConditionalOpcode>(&opcode.opcode)) {
|
||||
manager.CommandLog("Opcode: Begin Keypress Conditional");
|
||||
manager.CommandLog(fmt::format("Key Mask: {:X}", begin_keypress_cond->key_mask));
|
||||
callbacks->CommandLog("Opcode: Begin Keypress Conditional");
|
||||
callbacks->CommandLog(fmt::format("Key Mask: {:X}", begin_keypress_cond->key_mask));
|
||||
} else if (auto perform_math_reg =
|
||||
std::get_if<PerformArithmeticRegisterOpcode>(&opcode.opcode)) {
|
||||
manager.CommandLog("Opcode: Perform Register Arithmetic");
|
||||
manager.CommandLog(fmt::format("Bit Width: {:X}", perform_math_reg->bit_width));
|
||||
manager.CommandLog(fmt::format("Dst Idx: {:X}", perform_math_reg->dst_reg_index));
|
||||
manager.CommandLog(fmt::format("Src1 Idx: {:X}", perform_math_reg->src_reg_1_index));
|
||||
callbacks->CommandLog("Opcode: Perform Register Arithmetic");
|
||||
callbacks->CommandLog(fmt::format("Bit Width: {:X}", perform_math_reg->bit_width));
|
||||
callbacks->CommandLog(fmt::format("Dst Idx: {:X}", perform_math_reg->dst_reg_index));
|
||||
callbacks->CommandLog(fmt::format("Src1 Idx: {:X}", perform_math_reg->src_reg_1_index));
|
||||
if (perform_math_reg->has_immediate) {
|
||||
manager.CommandLog(fmt::format("Value: {:X}", perform_math_reg->value.bit64));
|
||||
callbacks->CommandLog(fmt::format("Value: {:X}", perform_math_reg->value.bit64));
|
||||
} else {
|
||||
manager.CommandLog(fmt::format("Src2 Idx: {:X}", perform_math_reg->src_reg_2_index));
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Src2 Idx: {:X}", perform_math_reg->src_reg_2_index));
|
||||
}
|
||||
} else if (auto str_register = std::get_if<StoreRegisterToAddressOpcode>(&opcode.opcode)) {
|
||||
manager.CommandLog("Opcode: Store Register to Address");
|
||||
manager.CommandLog(fmt::format("Bit Width: {:X}", str_register->bit_width));
|
||||
manager.CommandLog(fmt::format("S Reg Idx: {:X}", str_register->str_reg_index));
|
||||
manager.CommandLog(fmt::format("A Reg Idx: {:X}", str_register->addr_reg_index));
|
||||
manager.CommandLog(fmt::format("Incr Reg: {:d}", str_register->increment_reg));
|
||||
callbacks->CommandLog("Opcode: Store Register to Address");
|
||||
callbacks->CommandLog(fmt::format("Bit Width: {:X}", str_register->bit_width));
|
||||
callbacks->CommandLog(fmt::format("S Reg Idx: {:X}", str_register->str_reg_index));
|
||||
callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", str_register->addr_reg_index));
|
||||
callbacks->CommandLog(fmt::format("Incr Reg: {:d}", str_register->increment_reg));
|
||||
switch (str_register->ofs_type) {
|
||||
case StoreRegisterOffsetType::None:
|
||||
break;
|
||||
case StoreRegisterOffsetType::Reg:
|
||||
manager.CommandLog(fmt::format("O Reg Idx: {:X}", str_register->ofs_reg_index));
|
||||
callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", str_register->ofs_reg_index));
|
||||
break;
|
||||
case StoreRegisterOffsetType::Imm:
|
||||
manager.CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address));
|
||||
callbacks->CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address));
|
||||
break;
|
||||
case StoreRegisterOffsetType::MemReg:
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Mem Type: {:X}", static_cast<u32>(str_register->mem_type)));
|
||||
break;
|
||||
case StoreRegisterOffsetType::MemImm:
|
||||
case StoreRegisterOffsetType::MemImmReg:
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Mem Type: {:X}", static_cast<u32>(str_register->mem_type)));
|
||||
manager.CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address));
|
||||
callbacks->CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address));
|
||||
break;
|
||||
}
|
||||
} else if (auto begin_reg_cond = std::get_if<BeginRegisterConditionalOpcode>(&opcode.opcode)) {
|
||||
manager.CommandLog("Opcode: Begin Register Conditional");
|
||||
manager.CommandLog(fmt::format("Bit Width: {:X}", begin_reg_cond->bit_width));
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog("Opcode: Begin Register Conditional");
|
||||
callbacks->CommandLog(fmt::format("Bit Width: {:X}", begin_reg_cond->bit_width));
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Cond Type: {:X}", static_cast<u32>(begin_reg_cond->cond_type)));
|
||||
manager.CommandLog(fmt::format("V Reg Idx: {:X}", begin_reg_cond->val_reg_index));
|
||||
callbacks->CommandLog(fmt::format("V Reg Idx: {:X}", begin_reg_cond->val_reg_index));
|
||||
switch (begin_reg_cond->comp_type) {
|
||||
case CompareRegisterValueType::StaticValue:
|
||||
manager.CommandLog("Comp Type: Static Value");
|
||||
manager.CommandLog(fmt::format("Value: {:X}", begin_reg_cond->value.bit64));
|
||||
callbacks->CommandLog("Comp Type: Static Value");
|
||||
callbacks->CommandLog(fmt::format("Value: {:X}", begin_reg_cond->value.bit64));
|
||||
break;
|
||||
case CompareRegisterValueType::OtherRegister:
|
||||
manager.CommandLog("Comp Type: Other Register");
|
||||
manager.CommandLog(fmt::format("X Reg Idx: {:X}", begin_reg_cond->other_reg_index));
|
||||
callbacks->CommandLog("Comp Type: Other Register");
|
||||
callbacks->CommandLog(fmt::format("X Reg Idx: {:X}", begin_reg_cond->other_reg_index));
|
||||
break;
|
||||
case CompareRegisterValueType::MemoryRelAddr:
|
||||
manager.CommandLog("Comp Type: Memory Relative Address");
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog("Comp Type: Memory Relative Address");
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Mem Type: {:X}", static_cast<u32>(begin_reg_cond->mem_type)));
|
||||
manager.CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address));
|
||||
callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address));
|
||||
break;
|
||||
case CompareRegisterValueType::MemoryOfsReg:
|
||||
manager.CommandLog("Comp Type: Memory Offset Register");
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog("Comp Type: Memory Offset Register");
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Mem Type: {:X}", static_cast<u32>(begin_reg_cond->mem_type)));
|
||||
manager.CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index));
|
||||
callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index));
|
||||
break;
|
||||
case CompareRegisterValueType::RegisterRelAddr:
|
||||
manager.CommandLog("Comp Type: Register Relative Address");
|
||||
manager.CommandLog(fmt::format("A Reg Idx: {:X}", begin_reg_cond->addr_reg_index));
|
||||
manager.CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address));
|
||||
callbacks->CommandLog("Comp Type: Register Relative Address");
|
||||
callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", begin_reg_cond->addr_reg_index));
|
||||
callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address));
|
||||
break;
|
||||
case CompareRegisterValueType::RegisterOfsReg:
|
||||
manager.CommandLog("Comp Type: Register Offset Register");
|
||||
manager.CommandLog(fmt::format("A Reg Idx: {:X}", begin_reg_cond->addr_reg_index));
|
||||
manager.CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index));
|
||||
callbacks->CommandLog("Comp Type: Register Offset Register");
|
||||
callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", begin_reg_cond->addr_reg_index));
|
||||
callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index));
|
||||
break;
|
||||
}
|
||||
} else if (auto save_restore_reg = std::get_if<SaveRestoreRegisterOpcode>(&opcode.opcode)) {
|
||||
manager.CommandLog("Opcode: Save or Restore Register");
|
||||
manager.CommandLog(fmt::format("Dst Idx: {:X}", save_restore_reg->dst_index));
|
||||
manager.CommandLog(fmt::format("Src Idx: {:X}", save_restore_reg->src_index));
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog("Opcode: Save or Restore Register");
|
||||
callbacks->CommandLog(fmt::format("Dst Idx: {:X}", save_restore_reg->dst_index));
|
||||
callbacks->CommandLog(fmt::format("Src Idx: {:X}", save_restore_reg->src_index));
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Op Type: {:d}", static_cast<u32>(save_restore_reg->op_type)));
|
||||
} else if (auto save_restore_regmask =
|
||||
std::get_if<SaveRestoreRegisterMaskOpcode>(&opcode.opcode)) {
|
||||
manager.CommandLog("Opcode: Save or Restore Register Mask");
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog("Opcode: Save or Restore Register Mask");
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Op Type: {:d}", static_cast<u32>(save_restore_regmask->op_type)));
|
||||
for (std::size_t i = 0; i < NumRegisters; i++) {
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Act[{:02X}]: {:d}", i, save_restore_regmask->should_operate[i]));
|
||||
}
|
||||
} else if (auto rw_static_reg = std::get_if<ReadWriteStaticRegisterOpcode>(&opcode.opcode)) {
|
||||
manager.CommandLog("Opcode: Read/Write Static Register");
|
||||
callbacks->CommandLog("Opcode: Read/Write Static Register");
|
||||
if (rw_static_reg->static_idx < NumReadableStaticRegisters) {
|
||||
manager.CommandLog("Op Type: ReadStaticRegister");
|
||||
callbacks->CommandLog("Op Type: ReadStaticRegister");
|
||||
} else {
|
||||
manager.CommandLog("Op Type: WriteStaticRegister");
|
||||
callbacks->CommandLog("Op Type: WriteStaticRegister");
|
||||
}
|
||||
manager.CommandLog(fmt::format("Reg Idx {:X}", rw_static_reg->idx));
|
||||
manager.CommandLog(fmt::format("Stc Idx {:X}", rw_static_reg->static_idx));
|
||||
callbacks->CommandLog(fmt::format("Reg Idx {:X}", rw_static_reg->idx));
|
||||
callbacks->CommandLog(fmt::format("Stc Idx {:X}", rw_static_reg->static_idx));
|
||||
} else if (auto debug_log = std::get_if<DebugLogOpcode>(&opcode.opcode)) {
|
||||
manager.CommandLog("Opcode: Debug Log");
|
||||
manager.CommandLog(fmt::format("Bit Width: {:X}", debug_log->bit_width));
|
||||
manager.CommandLog(fmt::format("Log ID: {:X}", debug_log->log_id));
|
||||
manager.CommandLog(fmt::format("Val Type: {:X}", static_cast<u32>(debug_log->val_type)));
|
||||
callbacks->CommandLog("Opcode: Debug Log");
|
||||
callbacks->CommandLog(fmt::format("Bit Width: {:X}", debug_log->bit_width));
|
||||
callbacks->CommandLog(fmt::format("Log ID: {:X}", debug_log->log_id));
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Val Type: {:X}", static_cast<u32>(debug_log->val_type)));
|
||||
switch (debug_log->val_type) {
|
||||
case DebugLogValueType::RegisterValue:
|
||||
manager.CommandLog("Val Type: Register Value");
|
||||
manager.CommandLog(fmt::format("X Reg Idx: {:X}", debug_log->val_reg_index));
|
||||
callbacks->CommandLog("Val Type: Register Value");
|
||||
callbacks->CommandLog(fmt::format("X Reg Idx: {:X}", debug_log->val_reg_index));
|
||||
break;
|
||||
case DebugLogValueType::MemoryRelAddr:
|
||||
manager.CommandLog("Val Type: Memory Relative Address");
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog("Val Type: Memory Relative Address");
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Mem Type: {:X}", static_cast<u32>(debug_log->mem_type)));
|
||||
manager.CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address));
|
||||
callbacks->CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address));
|
||||
break;
|
||||
case DebugLogValueType::MemoryOfsReg:
|
||||
manager.CommandLog("Val Type: Memory Offset Register");
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog("Val Type: Memory Offset Register");
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Mem Type: {:X}", static_cast<u32>(debug_log->mem_type)));
|
||||
manager.CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index));
|
||||
callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index));
|
||||
break;
|
||||
case DebugLogValueType::RegisterRelAddr:
|
||||
manager.CommandLog("Val Type: Register Relative Address");
|
||||
manager.CommandLog(fmt::format("A Reg Idx: {:X}", debug_log->addr_reg_index));
|
||||
manager.CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address));
|
||||
callbacks->CommandLog("Val Type: Register Relative Address");
|
||||
callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", debug_log->addr_reg_index));
|
||||
callbacks->CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address));
|
||||
break;
|
||||
case DebugLogValueType::RegisterOfsReg:
|
||||
manager.CommandLog("Val Type: Register Offset Register");
|
||||
manager.CommandLog(fmt::format("A Reg Idx: {:X}", debug_log->addr_reg_index));
|
||||
manager.CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index));
|
||||
callbacks->CommandLog("Val Type: Register Offset Register");
|
||||
callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", debug_log->addr_reg_index));
|
||||
callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index));
|
||||
break;
|
||||
}
|
||||
} else if (auto instr = std::get_if<UnrecognizedInstruction>(&opcode.opcode)) {
|
||||
manager.CommandLog(fmt::format("Unknown opcode: {:X}", static_cast<u32>(instr->opcode)));
|
||||
callbacks->CommandLog(fmt::format("Unknown opcode: {:X}", static_cast<u32>(instr->opcode)));
|
||||
}
|
||||
}
|
||||
|
||||
bool CheatVirtualMachine::DecodeNextOpcode(CheatVmOpcode& out) {
|
||||
DmntCheatVm::Callbacks::~Callbacks() = default;
|
||||
|
||||
bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
|
||||
// If we've ever seen a decode failure, return false.
|
||||
bool valid = decode_success;
|
||||
CheatVmOpcode opcode = {};
|
||||
@ -629,7 +634,7 @@ bool CheatVirtualMachine::DecodeNextOpcode(CheatVmOpcode& out) {
|
||||
return valid;
|
||||
}
|
||||
|
||||
void CheatVirtualMachine::SkipConditionalBlock(bool is_if) {
|
||||
void DmntCheatVm::SkipConditionalBlock(bool is_if) {
|
||||
if (condition_depth > 0) {
|
||||
// We want to continue until we're out of the current block.
|
||||
const std::size_t desired_depth = condition_depth - 1;
|
||||
@ -663,7 +668,7 @@ void CheatVirtualMachine::SkipConditionalBlock(bool is_if) {
|
||||
}
|
||||
}
|
||||
|
||||
u64 CheatVirtualMachine::GetVmInt(VmInt value, u32 bit_width) {
|
||||
u64 DmntCheatVm::GetVmInt(VmInt value, u32 bit_width) {
|
||||
switch (bit_width) {
|
||||
case 1:
|
||||
return value.bit8;
|
||||
@ -679,7 +684,7 @@ u64 CheatVirtualMachine::GetVmInt(VmInt value, u32 bit_width) {
|
||||
}
|
||||
}
|
||||
|
||||
u64 CheatVirtualMachine::GetCheatProcessAddress(const CheatProcessMetadata& metadata,
|
||||
u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata,
|
||||
MemoryAccessType mem_type, u64 rel_address) {
|
||||
switch (mem_type) {
|
||||
case MemoryAccessType::MainNso:
|
||||
@ -694,7 +699,7 @@ u64 CheatVirtualMachine::GetCheatProcessAddress(const CheatProcessMetadata& meta
|
||||
}
|
||||
}
|
||||
|
||||
void CheatVirtualMachine::ResetState() {
|
||||
void DmntCheatVm::ResetState() {
|
||||
registers.fill(0);
|
||||
saved_values.fill(0);
|
||||
loop_tops.fill(0);
|
||||
@ -703,7 +708,7 @@ void CheatVirtualMachine::ResetState() {
|
||||
decode_success = true;
|
||||
}
|
||||
|
||||
bool CheatVirtualMachine::LoadProgram(std::span<const CheatEntry> entries) {
|
||||
bool DmntCheatVm::LoadProgram(const std::vector<CheatEntry>& entries) {
|
||||
// Reset opcode count.
|
||||
num_opcodes = 0;
|
||||
|
||||
@ -724,31 +729,31 @@ bool CheatVirtualMachine::LoadProgram(std::span<const CheatEntry> entries) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void CheatVirtualMachine::Execute(const CheatProcessMetadata& metadata) {
|
||||
void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
|
||||
CheatVmOpcode cur_opcode{};
|
||||
|
||||
// Get Keys down.
|
||||
u64 kDown = manager.HidKeysDown();
|
||||
u64 kDown = callbacks->HidKeysDown();
|
||||
|
||||
manager.CommandLog("Started VM execution.");
|
||||
manager.CommandLog(fmt::format("Main NSO: {:012X}", metadata.main_nso_extents.base));
|
||||
manager.CommandLog(fmt::format("Heap: {:012X}", metadata.main_nso_extents.base));
|
||||
manager.CommandLog(fmt::format("Keys Down: {:08X}", static_cast<u32>(kDown & 0x0FFFFFFF)));
|
||||
callbacks->CommandLog("Started VM execution.");
|
||||
callbacks->CommandLog(fmt::format("Main NSO: {:012X}", metadata.main_nso_extents.base));
|
||||
callbacks->CommandLog(fmt::format("Heap: {:012X}", metadata.main_nso_extents.base));
|
||||
callbacks->CommandLog(fmt::format("Keys Down: {:08X}", static_cast<u32>(kDown & 0x0FFFFFFF)));
|
||||
|
||||
// Clear VM state.
|
||||
ResetState();
|
||||
|
||||
// Loop until program finishes.
|
||||
while (DecodeNextOpcode(cur_opcode)) {
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Instruction Ptr: {:04X}", static_cast<u32>(instruction_ptr)));
|
||||
|
||||
for (std::size_t i = 0; i < NumRegisters; i++) {
|
||||
manager.CommandLog(fmt::format("Registers[{:02X}]: {:016X}", i, registers[i]));
|
||||
callbacks->CommandLog(fmt::format("Registers[{:02X}]: {:016X}", i, registers[i]));
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < NumRegisters; i++) {
|
||||
manager.CommandLog(fmt::format("SavedRegs[{:02X}]: {:016X}", i, saved_values[i]));
|
||||
callbacks->CommandLog(fmt::format("SavedRegs[{:02X}]: {:016X}", i, saved_values[i]));
|
||||
}
|
||||
LogOpcode(cur_opcode);
|
||||
|
||||
@ -768,8 +773,7 @@ void CheatVirtualMachine::Execute(const CheatProcessMetadata& metadata) {
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
manager.WriteCheatProcessMemoryUnsafe(dst_address, &dst_value,
|
||||
store_static->bit_width);
|
||||
callbacks->MemoryWriteUnsafe(dst_address, &dst_value, store_static->bit_width);
|
||||
break;
|
||||
}
|
||||
} else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) {
|
||||
@ -782,8 +786,7 @@ void CheatVirtualMachine::Execute(const CheatProcessMetadata& metadata) {
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
manager.ReadCheatProcessMemoryUnsafe(src_address, &src_value,
|
||||
begin_cond->bit_width);
|
||||
callbacks->MemoryReadUnsafe(src_address, &src_value, begin_cond->bit_width);
|
||||
break;
|
||||
}
|
||||
// Check against condition.
|
||||
@ -854,7 +857,7 @@ void CheatVirtualMachine::Execute(const CheatProcessMetadata& metadata) {
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
manager.ReadCheatProcessMemoryUnsafe(src_address, ®isters[ldr_memory->reg_index],
|
||||
callbacks->MemoryReadUnsafe(src_address, ®isters[ldr_memory->reg_index],
|
||||
ldr_memory->bit_width);
|
||||
break;
|
||||
}
|
||||
@ -871,8 +874,7 @@ void CheatVirtualMachine::Execute(const CheatProcessMetadata& metadata) {
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
manager.WriteCheatProcessMemoryUnsafe(dst_address, &dst_value,
|
||||
str_static->bit_width);
|
||||
callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_static->bit_width);
|
||||
break;
|
||||
}
|
||||
// Increment register if relevant.
|
||||
@ -1030,8 +1032,7 @@ void CheatVirtualMachine::Execute(const CheatProcessMetadata& metadata) {
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
manager.WriteCheatProcessMemoryUnsafe(dst_address, &dst_value,
|
||||
str_register->bit_width);
|
||||
callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_register->bit_width);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1110,7 +1111,7 @@ void CheatVirtualMachine::Execute(const CheatProcessMetadata& metadata) {
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
manager.ReadCheatProcessMemoryUnsafe(cond_address, &cond_value,
|
||||
callbacks->MemoryReadUnsafe(cond_address, &cond_value,
|
||||
begin_reg_cond->bit_width);
|
||||
break;
|
||||
}
|
||||
@ -1204,9 +1205,9 @@ void CheatVirtualMachine::Execute(const CheatProcessMetadata& metadata) {
|
||||
static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx];
|
||||
}
|
||||
} else if (std::holds_alternative<PauseProcessOpcode>(cur_opcode.opcode)) {
|
||||
manager.PauseCheatProcessUnsafe();
|
||||
callbacks->PauseProcess();
|
||||
} else if (std::holds_alternative<ResumeProcessOpcode>(cur_opcode.opcode)) {
|
||||
manager.ResumeCheatProcessUnsafe();
|
||||
callbacks->ResumeProcess();
|
||||
} else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) {
|
||||
// Read value from memory.
|
||||
u64 log_value = 0;
|
||||
@ -1253,8 +1254,7 @@ void CheatVirtualMachine::Execute(const CheatProcessMetadata& metadata) {
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
manager.ReadCheatProcessMemoryUnsafe(val_address, &log_value,
|
||||
debug_log->bit_width);
|
||||
callbacks->MemoryReadUnsafe(val_address, &log_value, debug_log->bit_width);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1265,4 +1265,4 @@ void CheatVirtualMachine::Execute(const CheatProcessMetadata& metadata) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::DMNT
|
||||
} // namespace Core::Memory
|
@ -3,14 +3,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <variant>
|
||||
|
||||
#include <vector>
|
||||
#include <fmt/printf.h>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/dmnt/dmnt_types.h"
|
||||
#include "core/memory/dmnt_cheat_types.h"
|
||||
|
||||
namespace Service::DMNT {
|
||||
class CheatProcessManager;
|
||||
namespace Core::Memory {
|
||||
|
||||
enum class CheatVmOpcodeType : u32 {
|
||||
StoreStatic = 0,
|
||||
@ -260,8 +259,25 @@ struct CheatVmOpcode {
|
||||
opcode{};
|
||||
};
|
||||
|
||||
class CheatVirtualMachine {
|
||||
class DmntCheatVm {
|
||||
public:
|
||||
/// Helper Type for DmntCheatVm <=> yuzu Interface
|
||||
class Callbacks {
|
||||
public:
|
||||
virtual ~Callbacks();
|
||||
|
||||
virtual void MemoryReadUnsafe(VAddr address, void* data, u64 size) = 0;
|
||||
virtual void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) = 0;
|
||||
|
||||
virtual u64 HidKeysDown() = 0;
|
||||
|
||||
virtual void PauseProcess() = 0;
|
||||
virtual void ResumeProcess() = 0;
|
||||
|
||||
virtual void DebugLog(u8 id, u64 value) = 0;
|
||||
virtual void CommandLog(std::string_view data) = 0;
|
||||
};
|
||||
|
||||
static constexpr std::size_t MaximumProgramOpcodeCount = 0x400;
|
||||
static constexpr std::size_t NumRegisters = 0x10;
|
||||
static constexpr std::size_t NumReadableStaticRegisters = 0x80;
|
||||
@ -269,43 +285,18 @@ public:
|
||||
static constexpr std::size_t NumStaticRegisters =
|
||||
NumReadableStaticRegisters + NumWritableStaticRegisters;
|
||||
|
||||
explicit CheatVirtualMachine(CheatProcessManager& cheat_manager);
|
||||
~CheatVirtualMachine();
|
||||
explicit DmntCheatVm(std::unique_ptr<Callbacks> callbacks_);
|
||||
~DmntCheatVm();
|
||||
|
||||
std::size_t GetProgramSize() const {
|
||||
return this->num_opcodes;
|
||||
}
|
||||
|
||||
bool LoadProgram(std::span<const CheatEntry> cheats);
|
||||
bool LoadProgram(const std::vector<CheatEntry>& cheats);
|
||||
void Execute(const CheatProcessMetadata& metadata);
|
||||
|
||||
u64 GetStaticRegister(std::size_t register_index) const {
|
||||
return static_registers[register_index];
|
||||
}
|
||||
|
||||
void SetStaticRegister(std::size_t register_index, u64 value) {
|
||||
static_registers[register_index] = value;
|
||||
}
|
||||
|
||||
void ResetStaticRegisters() {
|
||||
static_registers = {};
|
||||
}
|
||||
|
||||
private:
|
||||
bool DecodeNextOpcode(CheatVmOpcode& out);
|
||||
void SkipConditionalBlock(bool is_if);
|
||||
void ResetState();
|
||||
|
||||
// For implementing the DebugLog opcode.
|
||||
void DebugLog(u32 log_id, u64 value) const;
|
||||
|
||||
void LogOpcode(const CheatVmOpcode& opcode) const;
|
||||
|
||||
static u64 GetVmInt(VmInt value, u32 bit_width);
|
||||
static u64 GetCheatProcessAddress(const CheatProcessMetadata& metadata,
|
||||
MemoryAccessType mem_type, u64 rel_address);
|
||||
|
||||
CheatProcessManager& manager;
|
||||
std::unique_ptr<Callbacks> callbacks;
|
||||
|
||||
std::size_t num_opcodes = 0;
|
||||
std::size_t instruction_ptr = 0;
|
||||
@ -316,6 +307,19 @@ private:
|
||||
std::array<u64, NumRegisters> saved_values{};
|
||||
std::array<u64, NumStaticRegisters> static_registers{};
|
||||
std::array<std::size_t, NumRegisters> loop_tops{};
|
||||
|
||||
bool DecodeNextOpcode(CheatVmOpcode& out);
|
||||
void SkipConditionalBlock(bool is_if);
|
||||
void ResetState();
|
||||
|
||||
// For implementing the DebugLog opcode.
|
||||
void DebugLog(u32 log_id, u64 value);
|
||||
|
||||
void LogOpcode(const CheatVmOpcode& opcode);
|
||||
|
||||
static u64 GetVmInt(VmInt value, u32 bit_width);
|
||||
static u64 GetCheatProcessAddress(const CheatProcessMetadata& metadata,
|
||||
MemoryAccessType mem_type, u64 rel_address);
|
||||
};
|
||||
|
||||
}; // namespace Service::DMNT
|
||||
}; // namespace Core::Memory
|
@ -3,7 +3,6 @@
|
||||
|
||||
#include <set>
|
||||
#include <common/settings_input.h>
|
||||
#include <common/thread.h>
|
||||
#include <jni.h>
|
||||
#include "common/android/android_common.h"
|
||||
#include "common/android/id_cache.h"
|
||||
@ -11,18 +10,7 @@
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
Android::Android(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
vibration_thread = std::jthread([this](std::stop_token token) {
|
||||
Common::SetCurrentThreadName("Android_Vibration");
|
||||
auto env = Common::Android::GetEnvForThread();
|
||||
using namespace std::chrono_literals;
|
||||
while (!token.stop_requested()) {
|
||||
SendVibrations(env, token);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Android::~Android() = default;
|
||||
Android::Android(std::string input_engine_) : InputEngine(std::move(input_engine_)) {}
|
||||
|
||||
void Android::RegisterController(jobject j_input_device) {
|
||||
auto env = Common::Android::GetEnvForThread();
|
||||
@ -69,11 +57,17 @@ void Android::SetMotionState(std::string guid, size_t port, u64 delta_timestamp,
|
||||
Common::Input::DriverResult Android::SetVibration(
|
||||
[[maybe_unused]] const PadIdentifier& identifier,
|
||||
[[maybe_unused]] const Common::Input::VibrationStatus& vibration) {
|
||||
vibration_queue.Push(VibrationRequest{
|
||||
.identifier = identifier,
|
||||
.vibration = vibration,
|
||||
auto device = input_devices.find(identifier);
|
||||
if (device != input_devices.end()) {
|
||||
Common::Android::RunJNIOnFiber<void>([&](JNIEnv* env) {
|
||||
float average_intensity =
|
||||
static_cast<float>((vibration.high_amplitude + vibration.low_amplitude) / 2.0);
|
||||
env->CallVoidMethod(device->second, Common::Android::GetYuzuDeviceVibrate(),
|
||||
average_intensity);
|
||||
});
|
||||
return Common::Input::DriverResult::Success;
|
||||
}
|
||||
return Common::Input::DriverResult::NotSupported;
|
||||
}
|
||||
|
||||
bool Android::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) {
|
||||
@ -353,15 +347,4 @@ PadIdentifier Android::GetIdentifier(const std::string& guid, size_t port) const
|
||||
};
|
||||
}
|
||||
|
||||
void Android::SendVibrations(JNIEnv* env, std::stop_token token) {
|
||||
VibrationRequest request = vibration_queue.PopWait(token);
|
||||
auto device = input_devices.find(request.identifier);
|
||||
if (device != input_devices.end()) {
|
||||
float average_intensity = static_cast<float>(
|
||||
(request.vibration.high_amplitude + request.vibration.low_amplitude) / 2.0);
|
||||
env->CallVoidMethod(device->second, Common::Android::GetYuzuDeviceVibrate(),
|
||||
average_intensity);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
|
@ -4,7 +4,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
#include <common/threadsafe_queue.h>
|
||||
#include <jni.h>
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
@ -17,8 +16,6 @@ class Android final : public InputEngine {
|
||||
public:
|
||||
explicit Android(std::string input_engine_);
|
||||
|
||||
~Android() override;
|
||||
|
||||
/**
|
||||
* Registers controller number to accept new inputs.
|
||||
* @param j_input_device YuzuInputDevice object from the Android frontend to register.
|
||||
@ -92,9 +89,6 @@ private:
|
||||
/// Returns the correct identifier corresponding to the player index
|
||||
PadIdentifier GetIdentifier(const std::string& guid, size_t port) const;
|
||||
|
||||
/// Takes all vibrations from the queue and sends the command to the controller
|
||||
void SendVibrations(JNIEnv* env, std::stop_token token);
|
||||
|
||||
static constexpr s32 AXIS_X = 0;
|
||||
static constexpr s32 AXIS_Y = 1;
|
||||
static constexpr s32 AXIS_Z = 11;
|
||||
@ -139,10 +133,6 @@ private:
|
||||
redmagic_vid, backbone_labs_vid, xbox_vid};
|
||||
const std::vector<std::string> flipped_xy_vids{sony_vid, razer_vid, redmagic_vid,
|
||||
backbone_labs_vid, xbox_vid};
|
||||
|
||||
/// Queue of vibration request to controllers
|
||||
Common::SPSCQueue<VibrationRequest> vibration_queue;
|
||||
std::jthread vibration_thread;
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
|
@ -69,6 +69,11 @@ public:
|
||||
bool IsVibrationEnabled(const PadIdentifier& identifier) override;
|
||||
|
||||
private:
|
||||
struct VibrationRequest {
|
||||
PadIdentifier identifier;
|
||||
Common::Input::VibrationStatus vibration;
|
||||
};
|
||||
|
||||
void InitJoystick(int joystick_index);
|
||||
void CloseJoystick(SDL_Joystick* sdl_joystick);
|
||||
|
||||
|
@ -46,11 +46,6 @@ enum class EngineInputType {
|
||||
Nfc,
|
||||
};
|
||||
|
||||
struct VibrationRequest {
|
||||
PadIdentifier identifier;
|
||||
Common::Input::VibrationStatus vibration;
|
||||
};
|
||||
|
||||
namespace std {
|
||||
// Hash used to create lists from PadIdentifier data
|
||||
template <>
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/submission_package.h"
|
||||
#include "core/hle/service/glue/glue_manager.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "yuzu/compatibility_list.h"
|
||||
#include "yuzu/game_list.h"
|
||||
@ -326,6 +327,21 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
|
||||
const auto control = cache.GetEntry(game.title_id, ContentRecordType::Control);
|
||||
if (control != nullptr) {
|
||||
GetMetadataFromControlNCA(patch, *control, icon, name);
|
||||
|
||||
std::vector<u8> nacp_data;
|
||||
FileSys::NACP nacp;
|
||||
if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
|
||||
nacp_data = nacp.GetRawBytes();
|
||||
} else {
|
||||
nacp_data.resize(sizeof(FileSys::RawNACP));
|
||||
}
|
||||
Service::Glue::ApplicationLaunchProperty launch{};
|
||||
launch.title_id = program_id;
|
||||
launch.version = patch.GetGameVersion().value_or(0);
|
||||
launch.base_game_storage_id = FileSys::StorageId::NandUser;
|
||||
launch.update_storage_id = FileSys::StorageId::NandSystem;
|
||||
|
||||
system.GetARPManager().Register(launch.title_id, launch, std::move(nacp_data));
|
||||
}
|
||||
|
||||
auto entry = MakeGameListEntry(file->GetFullPath(), name, file->GetSize(), icon, *loader,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user