| |
| |
|
|
| #include <array> |
| #include <atomic> |
| #include <exception> |
| #include <memory> |
| #include <utility> |
|
|
| #include "audio_core/audio_core.h" |
| #include "common/fs/fs.h" |
| #include "common/logging/log.h" |
| #include "common/microprofile.h" |
| #include "common/settings.h" |
| #include "common/settings_enums.h" |
| #include "common/string_util.h" |
| #include "core/arm/exclusive_monitor.h" |
| #include "core/core.h" |
| #include "core/core_timing.h" |
| #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" |
| #include "core/file_sys/romfs_factory.h" |
| #include "core/file_sys/savedata_factory.h" |
| #include "core/file_sys/vfs/vfs_concat.h" |
| #include "core/file_sys/vfs/vfs_real.h" |
| #include "core/gpu_dirty_memory_manager.h" |
| #include "core/hle/kernel/k_memory_manager.h" |
| #include "core/hle/kernel/k_process.h" |
| #include "core/hle/kernel/k_resource_limit.h" |
| #include "core/hle/kernel/k_scheduler.h" |
| #include "core/hle/kernel/kernel.h" |
| #include "core/hle/kernel/physical_core.h" |
| #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/apm/apm_controller.h" |
| #include "core/hle/service/filesystem/filesystem.h" |
| #include "core/hle/service/glue/glue_manager.h" |
| #include "core/hle/service/glue/time/static.h" |
| #include "core/hle/service/psc/time/static.h" |
| #include "core/hle/service/psc/time/steady_clock.h" |
| #include "core/hle/service/psc/time/system_clock.h" |
| #include "core/hle/service/psc/time/time_zone_service.h" |
| #include "core/hle/service/service.h" |
| #include "core/hle/service/services.h" |
| #include "core/hle/service/set/system_settings_server.h" |
| #include "core/hle/service/sm/sm.h" |
| #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" |
| #include "core/tools/freezer.h" |
| #include "core/tools/renderdoc.h" |
| #include "hid_core/hid_core.h" |
| #include "network/network.h" |
| #include "video_core/host1x/host1x.h" |
| #include "video_core/renderer_base.h" |
| #include "video_core/video_core.h" |
|
|
| MICROPROFILE_DEFINE(ARM_CPU0, "ARM", "CPU 0", MP_RGB(255, 64, 64)); |
| MICROPROFILE_DEFINE(ARM_CPU1, "ARM", "CPU 1", MP_RGB(255, 64, 64)); |
| MICROPROFILE_DEFINE(ARM_CPU2, "ARM", "CPU 2", MP_RGB(255, 64, 64)); |
| 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; |
| } |
| } |
|
|
| } |
|
|
| FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, |
| const std::string& path) { |
| |
| std::string dir_name; |
| std::string filename; |
| Common::SplitPath(path, &dir_name, &filename, nullptr); |
|
|
| if (filename == "00") { |
| const auto dir = vfs->OpenDirectory(dir_name, FileSys::OpenMode::Read); |
| std::vector<FileSys::VirtualFile> concat; |
|
|
| for (u32 i = 0; i < 0x10; ++i) { |
| const auto file_name = fmt::format("{:02X}", i); |
| auto next = dir->GetFile(file_name); |
|
|
| if (next != nullptr) { |
| concat.push_back(std::move(next)); |
| } else { |
| next = dir->GetFile(file_name); |
|
|
| if (next == nullptr) { |
| break; |
| } |
|
|
| concat.push_back(std::move(next)); |
| } |
| } |
|
|
| return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(dir->GetName(), |
| std::move(concat)); |
| } |
|
|
| if (Common::FS::IsDir(path)) { |
| return vfs->OpenFile(path + "/main", FileSys::OpenMode::Read); |
| } |
|
|
| return vfs->OpenFile(path, FileSys::OpenMode::Read); |
| } |
|
|
| struct System::Impl { |
| explicit Impl(System& system) |
| : kernel{system}, fs_controller{system}, hid_core{}, room_network{}, cpu_manager{system}, |
| reporter{system}, applet_manager{system}, frontend_applets{system}, profile_manager{} {} |
|
|
| void Initialize(System& system) { |
| device_memory = std::make_unique<Core::DeviceMemory>(); |
|
|
| is_multicore = Settings::values.use_multi_core.GetValue(); |
| extended_memory_layout = |
| Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb; |
|
|
| core_timing.SetMulticore(is_multicore); |
| core_timing.Initialize([&system]() { system.RegisterHostThread(); }); |
|
|
| |
| if (virtual_filesystem == nullptr) { |
| virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); |
| } |
| if (content_provider == nullptr) { |
| content_provider = std::make_unique<FileSys::ContentProviderUnion>(); |
| } |
|
|
| |
| frontend_applets.SetDefaultAppletsIfMissing(); |
|
|
| is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue(); |
|
|
| kernel.SetMulticore(is_multicore); |
| cpu_manager.SetMulticore(is_multicore); |
| cpu_manager.SetAsyncGpu(is_async_gpu); |
| } |
|
|
| void ReinitializeIfNecessary(System& system) { |
| const bool must_reinitialize = |
| is_multicore != Settings::values.use_multi_core.GetValue() || |
| extended_memory_layout != (Settings::values.memory_layout_mode.GetValue() != |
| Settings::MemoryLayout::Memory_4Gb); |
|
|
| if (!must_reinitialize) { |
| return; |
| } |
|
|
| LOG_DEBUG(Kernel, "Re-initializing"); |
|
|
| is_multicore = Settings::values.use_multi_core.GetValue(); |
| extended_memory_layout = |
| Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb; |
|
|
| Initialize(system); |
| } |
|
|
| void RefreshTime(System& system) { |
| if (!system.IsPoweredOn()) { |
| return; |
| } |
|
|
| auto settings_service = |
| system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", |
| true); |
| auto static_service_a = |
| system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:a", true); |
|
|
| auto static_service_s = |
| system.ServiceManager().GetService<Service::PSC::Time::StaticService>("time:s", true); |
|
|
| std::shared_ptr<Service::PSC::Time::SystemClock> user_clock; |
| static_service_a->GetStandardUserSystemClock(&user_clock); |
|
|
| std::shared_ptr<Service::PSC::Time::SystemClock> local_clock; |
| static_service_a->GetStandardLocalSystemClock(&local_clock); |
|
|
| std::shared_ptr<Service::PSC::Time::SystemClock> network_clock; |
| static_service_s->GetStandardNetworkSystemClock(&network_clock); |
|
|
| std::shared_ptr<Service::Glue::Time::TimeZoneService> timezone_service; |
| static_service_a->GetTimeZoneService(&timezone_service); |
|
|
| Service::PSC::Time::LocationName name{}; |
| auto new_name = Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue()); |
| std::memcpy(name.data(), new_name.data(), std::min(name.size(), new_name.size())); |
|
|
| timezone_service->SetDeviceLocationName(name); |
|
|
| u64 time_offset = 0; |
| if (Settings::values.custom_rtc_enabled) { |
| time_offset = Settings::values.custom_rtc_offset.GetValue(); |
| } |
|
|
| const auto posix_time = std::chrono::system_clock::now().time_since_epoch(); |
| const u64 current_time = |
| +std::chrono::duration_cast<std::chrono::seconds>(posix_time).count(); |
| const u64 new_time = current_time + time_offset; |
|
|
| Service::PSC::Time::SystemClockContext context{}; |
| settings_service->SetUserSystemClockContext(context); |
| user_clock->SetCurrentTime(new_time); |
|
|
| local_clock->SetCurrentTime(new_time); |
|
|
| network_clock->GetSystemClockContext(&context); |
| settings_service->SetNetworkSystemClockContext(context); |
| network_clock->SetCurrentTime(new_time); |
| } |
|
|
| void Run() { |
| std::unique_lock<std::mutex> lk(suspend_guard); |
|
|
| kernel.SuspendEmulation(false); |
| core_timing.SyncPause(false); |
| is_paused.store(false, std::memory_order_relaxed); |
| } |
|
|
| void Pause() { |
| std::unique_lock<std::mutex> lk(suspend_guard); |
|
|
| core_timing.SyncPause(true); |
| kernel.SuspendEmulation(true); |
| is_paused.store(true, std::memory_order_relaxed); |
| } |
|
|
| bool IsPaused() const { |
| return is_paused.load(std::memory_order_relaxed); |
| } |
|
|
| std::unique_lock<std::mutex> StallApplication() { |
| std::unique_lock<std::mutex> lk(suspend_guard); |
| kernel.SuspendEmulation(true); |
| core_timing.SyncPause(true); |
| return lk; |
| } |
|
|
| void UnstallApplication() { |
| if (!IsPaused()) { |
| core_timing.SyncPause(false); |
| kernel.SuspendEmulation(false); |
| } |
| } |
|
|
| void SetNVDECActive(bool is_nvdec_active) { |
| nvdec_active = is_nvdec_active; |
| } |
|
|
| bool GetNVDECActive() { |
| return nvdec_active; |
| } |
|
|
| void InitializeDebugger(System& system, u16 port) { |
| debugger = std::make_unique<Debugger>(system, port); |
| } |
|
|
| void InitializeKernel(System& system) { |
| LOG_DEBUG(Core, "initialized OK"); |
|
|
| |
| ReinitializeIfNecessary(system); |
|
|
| kernel.Initialize(); |
| cpu_manager.Initialize(); |
| } |
|
|
| SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) { |
| |
| arp_manager.ResetAll(); |
|
|
| telemetry_session = std::make_unique<Core::TelemetrySession>(); |
|
|
| host1x_core = std::make_unique<Tegra::Host1x::Host1x>(system); |
| gpu_core = VideoCore::CreateGPU(emu_window, system); |
| if (!gpu_core) { |
| return SystemResultStatus::ErrorVideoCore; |
| } |
|
|
| audio_core = std::make_unique<AudioCore::AudioCore>(system); |
|
|
| service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); |
| services = |
| std::make_unique<Service::Services>(service_manager, system, stop_event.get_token()); |
|
|
| is_powered_on = true; |
| exit_locked = false; |
| exit_requested = false; |
|
|
| microprofile_cpu[0] = MICROPROFILE_TOKEN(ARM_CPU0); |
| microprofile_cpu[1] = MICROPROFILE_TOKEN(ARM_CPU1); |
| microprofile_cpu[2] = MICROPROFILE_TOKEN(ARM_CPU2); |
| microprofile_cpu[3] = MICROPROFILE_TOKEN(ARM_CPU3); |
|
|
| if (Settings::values.enable_renderdoc_hotkey) { |
| renderdoc_api = std::make_unique<Tools::RenderdocAPI>(); |
| } |
|
|
| LOG_DEBUG(Core, "Initialized OK"); |
|
|
| return SystemResultStatus::Success; |
| } |
|
|
| 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); |
|
|
| |
| 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 [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(); |
|
|
| return static_cast<SystemResultStatus>( |
| static_cast<u32>(SystemResultStatus::ErrorLoader) + static_cast<u32>(load_result)); |
| } |
|
|
| |
| SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)}; |
| if (init_result != SystemResultStatus::Success) { |
| LOG_CRITICAL(Core, "Failed to initialize system (Error {})!", |
| static_cast<int>(init_result)); |
| ShutdownMainProcess(); |
| return init_result; |
| } |
|
|
| AddGlueRegistrationForProcess(*app_loader, *main_process); |
| telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider); |
|
|
| |
| if (cheat_engine) { |
| cheat_engine->Initialize(); |
| } |
|
|
| |
| applet_manager.CreateAndInsertByFrontendAppletParameters(main_process->GetProcessId(), |
| params); |
|
|
| |
| main_process->Run(load_parameters->main_thread_priority, |
| load_parameters->main_thread_stack_size); |
| main_process->Close(); |
|
|
| if (Settings::values.gamecard_inserted) { |
| if (Settings::values.gamecard_current_game) { |
| fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, filepath)); |
| } else if (!Settings::values.gamecard_path.GetValue().empty()) { |
| const auto& gamecard_path = Settings::values.gamecard_path.GetValue(); |
| fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, gamecard_path)); |
| } |
| } |
|
|
| perf_stats = std::make_unique<PerfStats>(params.program_id); |
| |
| GetAndResetPerfStats(); |
| perf_stats->BeginSystemFrame(); |
|
|
| std::string title_version; |
| const FileSys::PatchManager pm(params.program_id, system.GetFileSystemController(), |
| system.GetContentProvider()); |
| const auto metadata = pm.GetControlMetadata(); |
| if (metadata.first != nullptr) { |
| title_version = metadata.first->GetVersionString(); |
| } |
| if (auto room_member = room_network.GetRoomMember().lock()) { |
| Network::GameInfo game_info; |
| game_info.name = name; |
| game_info.id = params.program_id; |
| game_info.version = title_version; |
| room_member->SendGameInfo(game_info); |
| } |
|
|
| status = SystemResultStatus::Success; |
| return status; |
| } |
|
|
| void ShutdownMainProcess() { |
| SetShuttingDown(true); |
|
|
| |
| if (perf_stats) { |
| const auto perf_results = GetAndResetPerfStats(); |
| constexpr auto performance = Common::Telemetry::FieldType::Performance; |
|
|
| telemetry_session->AddField(performance, "Shutdown_EmulationSpeed", |
| perf_results.emulation_speed * 100.0); |
| telemetry_session->AddField(performance, "Shutdown_Framerate", |
| perf_results.average_game_fps); |
| telemetry_session->AddField(performance, "Shutdown_Frametime", |
| perf_results.frametime * 1000.0); |
| telemetry_session->AddField(performance, "Mean_Frametime_MS", |
| perf_stats->GetMeanFrametime()); |
| } |
|
|
| is_powered_on = false; |
| exit_locked = false; |
| exit_requested = false; |
|
|
| if (gpu_core != nullptr) { |
| gpu_core->NotifyShutdown(); |
| } |
|
|
| stop_event.request_stop(); |
| core_timing.SyncPause(false); |
| Network::CancelPendingSocketOperations(); |
| kernel.SuspendEmulation(true); |
| kernel.CloseServices(); |
| kernel.ShutdownCores(); |
| applet_manager.Reset(); |
| services.reset(); |
| service_manager.reset(); |
| fs_controller.Reset(); |
| cheat_engine.reset(); |
| telemetry_session.reset(); |
| core_timing.ClearPendingEvents(); |
| app_loader.reset(); |
| audio_core.reset(); |
| gpu_core.reset(); |
| host1x_core.reset(); |
| perf_stats.reset(); |
| cpu_manager.Shutdown(); |
| debugger.reset(); |
| kernel.Shutdown(); |
| stop_event = {}; |
| Network::RestartSocketOperations(); |
|
|
| if (auto room_member = room_network.GetRoomMember().lock()) { |
| Network::GameInfo game_info{}; |
| room_member->SendGameInfo(game_info); |
| } |
|
|
| LOG_DEBUG(Core, "Shutdown OK"); |
| } |
|
|
| bool IsShuttingDown() const { |
| return is_shutting_down; |
| } |
|
|
| void SetShuttingDown(bool shutting_down) { |
| is_shutting_down = shutting_down; |
| } |
|
|
| Loader::ResultStatus GetGameName(std::string& out) const { |
| if (app_loader == nullptr) |
| return Loader::ResultStatus::ErrorNotInitialized; |
| 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); |
|
|
| |
| |
| 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) { |
| status_details = details; |
| } |
| } |
|
|
| PerfStatsResults GetAndResetPerfStats() { |
| return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs()); |
| } |
|
|
| mutable std::mutex suspend_guard; |
| std::atomic_bool is_paused{}; |
| std::atomic<bool> is_shutting_down{}; |
|
|
| Timing::CoreTiming core_timing; |
| Kernel::KernelCore kernel; |
| |
| FileSys::VirtualFilesystem virtual_filesystem; |
| |
| std::unique_ptr<FileSys::ContentProviderUnion> content_provider; |
| Service::FileSystem::FileSystemController fs_controller; |
| |
| std::unique_ptr<Loader::AppLoader> app_loader; |
| std::unique_ptr<Tegra::GPU> gpu_core; |
| std::unique_ptr<Tegra::Host1x::Host1x> host1x_core; |
| std::unique_ptr<Core::DeviceMemory> device_memory; |
| std::unique_ptr<AudioCore::AudioCore> audio_core; |
| Core::HID::HIDCore hid_core; |
| Network::RoomNetwork room_network; |
|
|
| CpuManager cpu_manager; |
| std::atomic_bool is_powered_on{}; |
| bool exit_locked = false; |
| bool exit_requested = false; |
|
|
| bool nvdec_active{}; |
|
|
| Reporter reporter; |
| std::unique_ptr<Memory::CheatEngine> cheat_engine; |
| std::unique_ptr<Tools::Freezer> memory_freezer; |
| std::array<u8, 0x20> build_id{}; |
|
|
| std::unique_ptr<Tools::RenderdocAPI> renderdoc_api; |
|
|
| |
| Service::AM::AppletManager applet_manager; |
| Service::AM::Frontend::FrontendAppletHolder frontend_applets; |
|
|
| |
| Service::APM::Controller apm_controller{core_timing}; |
|
|
| |
| Service::Glue::ARPManager arp_manager; |
| Service::Account::ProfileManager profile_manager; |
|
|
| |
| std::shared_ptr<Service::SM::ServiceManager> service_manager; |
|
|
| |
| std::unique_ptr<Service::Services> services; |
|
|
| |
| std::unique_ptr<Core::TelemetrySession> telemetry_session; |
|
|
| |
| Network::NetworkInstance network_instance; |
|
|
| |
| std::unique_ptr<Core::Debugger> debugger; |
|
|
| SystemResultStatus status = SystemResultStatus::Success; |
| std::string status_details = ""; |
|
|
| std::unique_ptr<Core::PerfStats> perf_stats; |
| Core::SpeedLimiter speed_limiter; |
|
|
| bool is_multicore{}; |
| bool is_async_gpu{}; |
| bool extended_memory_layout{}; |
|
|
| ExecuteProgramCallback execute_program_callback; |
| ExitCallback exit_callback; |
| std::stop_source stop_event; |
|
|
| std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; |
| std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{}; |
|
|
| std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> |
| gpu_dirty_memory_managers; |
|
|
| std::deque<std::vector<u8>> user_channel; |
| }; |
|
|
| System::System() : impl{std::make_unique<Impl>(*this)} {} |
|
|
| System::~System() = default; |
|
|
| CpuManager& System::GetCpuManager() { |
| return impl->cpu_manager; |
| } |
|
|
| const CpuManager& System::GetCpuManager() const { |
| return impl->cpu_manager; |
| } |
|
|
| void System::Initialize() { |
| impl->Initialize(*this); |
| } |
|
|
| void System::Run() { |
| impl->Run(); |
| } |
|
|
| void System::Pause() { |
| impl->Pause(); |
| } |
|
|
| bool System::IsPaused() const { |
| return impl->IsPaused(); |
| } |
|
|
| void System::ShutdownMainProcess() { |
| impl->ShutdownMainProcess(); |
| } |
|
|
| bool System::IsShuttingDown() const { |
| return impl->IsShuttingDown(); |
| } |
|
|
| void System::SetShuttingDown(bool shutting_down) { |
| impl->SetShuttingDown(shutting_down); |
| } |
|
|
| void System::DetachDebugger() { |
| if (impl->debugger) { |
| impl->debugger->NotifyShutdown(); |
| } |
| } |
|
|
| std::unique_lock<std::mutex> System::StallApplication() { |
| return impl->StallApplication(); |
| } |
|
|
| void System::UnstallApplication() { |
| impl->UnstallApplication(); |
| } |
|
|
| void System::SetNVDECActive(bool is_nvdec_active) { |
| impl->SetNVDECActive(is_nvdec_active); |
| } |
|
|
| bool System::GetNVDECActive() { |
| return impl->GetNVDECActive(); |
| } |
|
|
| void System::InitializeDebugger() { |
| impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue()); |
| } |
|
|
| SystemResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath, |
| Service::AM::FrontendAppletParameters& params) { |
| return impl->Load(*this, emu_window, filepath, params); |
| } |
|
|
| bool System::IsPoweredOn() const { |
| return impl->is_powered_on.load(std::memory_order::relaxed); |
| } |
|
|
| void System::PrepareReschedule(const u32 core_index) { |
| impl->kernel.PrepareReschedule(core_index); |
| } |
|
|
| size_t System::GetCurrentHostThreadID() const { |
| return impl->kernel.GetCurrentHostThreadID(); |
| } |
|
|
| std::span<GPUDirtyMemoryManager> System::GetGPUDirtyMemoryManager() { |
| return impl->gpu_dirty_memory_managers; |
| } |
|
|
| void System::GatherGPUDirtyMemory(std::function<void(PAddr, size_t)>& callback) { |
| for (auto& manager : impl->gpu_dirty_memory_managers) { |
| manager.Gather(callback); |
| } |
| } |
|
|
| PerfStatsResults System::GetAndResetPerfStats() { |
| return impl->GetAndResetPerfStats(); |
| } |
|
|
| TelemetrySession& System::TelemetrySession() { |
| return *impl->telemetry_session; |
| } |
|
|
| const TelemetrySession& System::TelemetrySession() const { |
| return *impl->telemetry_session; |
| } |
|
|
| Kernel::PhysicalCore& System::CurrentPhysicalCore() { |
| return impl->kernel.CurrentPhysicalCore(); |
| } |
|
|
| const Kernel::PhysicalCore& System::CurrentPhysicalCore() const { |
| return impl->kernel.CurrentPhysicalCore(); |
| } |
|
|
| |
| Kernel::GlobalSchedulerContext& System::GlobalSchedulerContext() { |
| return impl->kernel.GlobalSchedulerContext(); |
| } |
|
|
| |
| const Kernel::GlobalSchedulerContext& System::GlobalSchedulerContext() const { |
| return impl->kernel.GlobalSchedulerContext(); |
| } |
|
|
| Kernel::KProcess* System::ApplicationProcess() { |
| return impl->kernel.ApplicationProcess(); |
| } |
|
|
| Core::DeviceMemory& System::DeviceMemory() { |
| return *impl->device_memory; |
| } |
|
|
| const Core::DeviceMemory& System::DeviceMemory() const { |
| return *impl->device_memory; |
| } |
|
|
| const Kernel::KProcess* System::ApplicationProcess() const { |
| return impl->kernel.ApplicationProcess(); |
| } |
|
|
| Memory::Memory& System::ApplicationMemory() { |
| return impl->kernel.ApplicationProcess()->GetMemory(); |
| } |
|
|
| const Core::Memory::Memory& System::ApplicationMemory() const { |
| return impl->kernel.ApplicationProcess()->GetMemory(); |
| } |
|
|
| Tegra::GPU& System::GPU() { |
| return *impl->gpu_core; |
| } |
|
|
| const Tegra::GPU& System::GPU() const { |
| return *impl->gpu_core; |
| } |
|
|
| Tegra::Host1x::Host1x& System::Host1x() { |
| return *impl->host1x_core; |
| } |
|
|
| const Tegra::Host1x::Host1x& System::Host1x() const { |
| return *impl->host1x_core; |
| } |
|
|
| VideoCore::RendererBase& System::Renderer() { |
| return impl->gpu_core->Renderer(); |
| } |
|
|
| const VideoCore::RendererBase& System::Renderer() const { |
| return impl->gpu_core->Renderer(); |
| } |
|
|
| Kernel::KernelCore& System::Kernel() { |
| return impl->kernel; |
| } |
|
|
| const Kernel::KernelCore& System::Kernel() const { |
| return impl->kernel; |
| } |
|
|
| HID::HIDCore& System::HIDCore() { |
| return impl->hid_core; |
| } |
|
|
| const HID::HIDCore& System::HIDCore() const { |
| return impl->hid_core; |
| } |
|
|
| AudioCore::AudioCore& System::AudioCore() { |
| return *impl->audio_core; |
| } |
|
|
| const AudioCore::AudioCore& System::AudioCore() const { |
| return *impl->audio_core; |
| } |
|
|
| Timing::CoreTiming& System::CoreTiming() { |
| return impl->core_timing; |
| } |
|
|
| const Timing::CoreTiming& System::CoreTiming() const { |
| return impl->core_timing; |
| } |
|
|
| Core::PerfStats& System::GetPerfStats() { |
| return *impl->perf_stats; |
| } |
|
|
| const Core::PerfStats& System::GetPerfStats() const { |
| return *impl->perf_stats; |
| } |
|
|
| Core::SpeedLimiter& System::SpeedLimiter() { |
| return impl->speed_limiter; |
| } |
|
|
| const Core::SpeedLimiter& System::SpeedLimiter() const { |
| return impl->speed_limiter; |
| } |
|
|
| u64 System::GetApplicationProcessProgramID() const { |
| return impl->kernel.ApplicationProcess()->GetProgramId(); |
| } |
|
|
| Loader::ResultStatus System::GetGameName(std::string& out) const { |
| return impl->GetGameName(out); |
| } |
|
|
| void System::SetStatus(SystemResultStatus new_status, const char* details) { |
| impl->SetStatus(new_status, details); |
| } |
|
|
| const std::string& System::GetStatusDetails() const { |
| return impl->status_details; |
| } |
|
|
| Loader::AppLoader& System::GetAppLoader() { |
| return *impl->app_loader; |
| } |
|
|
| const Loader::AppLoader& System::GetAppLoader() const { |
| return *impl->app_loader; |
| } |
|
|
| void System::SetFilesystem(FileSys::VirtualFilesystem vfs) { |
| impl->virtual_filesystem = std::move(vfs); |
| } |
|
|
| FileSys::VirtualFilesystem System::GetFilesystem() const { |
| return impl->virtual_filesystem; |
| } |
|
|
| 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_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) { |
| impl->frontend_applets.SetFrontendAppletSet(std::move(set)); |
| } |
|
|
| Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() { |
| return impl->frontend_applets; |
| } |
|
|
| const Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() const { |
| return impl->frontend_applets; |
| } |
|
|
| Service::AM::AppletManager& System::GetAppletManager() { |
| return impl->applet_manager; |
| } |
|
|
| void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) { |
| impl->content_provider = std::move(provider); |
| } |
|
|
| FileSys::ContentProvider& System::GetContentProvider() { |
| return *impl->content_provider; |
| } |
|
|
| const FileSys::ContentProvider& System::GetContentProvider() const { |
| return *impl->content_provider; |
| } |
|
|
| FileSys::ContentProviderUnion& System::GetContentProviderUnion() { |
| return *impl->content_provider; |
| } |
|
|
| const FileSys::ContentProviderUnion& System::GetContentProviderUnion() const { |
| return *impl->content_provider; |
| } |
|
|
| Service::FileSystem::FileSystemController& System::GetFileSystemController() { |
| return impl->fs_controller; |
| } |
|
|
| const Service::FileSystem::FileSystemController& System::GetFileSystemController() const { |
| return impl->fs_controller; |
| } |
|
|
| void System::RegisterContentProvider(FileSys::ContentProviderUnionSlot slot, |
| FileSys::ContentProvider* provider) { |
| impl->content_provider->SetSlot(slot, provider); |
| } |
|
|
| void System::ClearContentProvider(FileSys::ContentProviderUnionSlot slot) { |
| impl->content_provider->ClearSlot(slot); |
| } |
|
|
| const Reporter& System::GetReporter() const { |
| return impl->reporter; |
| } |
|
|
| Service::Glue::ARPManager& System::GetARPManager() { |
| return impl->arp_manager; |
| } |
|
|
| const Service::Glue::ARPManager& System::GetARPManager() const { |
| return impl->arp_manager; |
| } |
|
|
| Service::APM::Controller& System::GetAPMController() { |
| return impl->apm_controller; |
| } |
|
|
| const Service::APM::Controller& System::GetAPMController() const { |
| return impl->apm_controller; |
| } |
|
|
| Service::Account::ProfileManager& System::GetProfileManager() { |
| return impl->profile_manager; |
| } |
|
|
| const Service::Account::ProfileManager& System::GetProfileManager() const { |
| return impl->profile_manager; |
| } |
|
|
| void System::SetExitLocked(bool locked) { |
| impl->exit_locked = locked; |
| } |
|
|
| bool System::GetExitLocked() const { |
| return impl->exit_locked; |
| } |
|
|
| void System::SetExitRequested(bool requested) { |
| impl->exit_requested = requested; |
| } |
|
|
| bool System::GetExitRequested() const { |
| return impl->exit_requested; |
| } |
|
|
| void System::SetApplicationProcessBuildID(const CurrentBuildProcessID& id) { |
| impl->build_id = id; |
| } |
|
|
| const System::CurrentBuildProcessID& System::GetApplicationProcessBuildID() const { |
| return impl->build_id; |
| } |
|
|
| Service::SM::ServiceManager& System::ServiceManager() { |
| return *impl->service_manager; |
| } |
|
|
| const Service::SM::ServiceManager& System::ServiceManager() const { |
| return *impl->service_manager; |
| } |
|
|
| void System::RegisterCoreThread(std::size_t id) { |
| impl->kernel.RegisterCoreThread(id); |
| } |
|
|
| void System::RegisterHostThread() { |
| impl->kernel.RegisterHostThread(); |
| } |
|
|
| void System::EnterCPUProfile() { |
| std::size_t core = impl->kernel.GetCurrentHostThreadID(); |
| impl->dynarmic_ticks[core] = MicroProfileEnter(impl->microprofile_cpu[core]); |
| } |
|
|
| void System::ExitCPUProfile() { |
| std::size_t core = impl->kernel.GetCurrentHostThreadID(); |
| MicroProfileLeave(impl->microprofile_cpu[core], impl->dynarmic_ticks[core]); |
| } |
|
|
| bool System::IsMulticore() const { |
| return impl->is_multicore; |
| } |
|
|
| bool System::DebuggerEnabled() const { |
| return Settings::values.use_gdbstub.GetValue(); |
| } |
|
|
| Core::Debugger& System::GetDebugger() { |
| return *impl->debugger; |
| } |
|
|
| const Core::Debugger& System::GetDebugger() const { |
| return *impl->debugger; |
| } |
|
|
| Network::RoomNetwork& System::GetRoomNetwork() { |
| return impl->room_network; |
| } |
|
|
| const Network::RoomNetwork& System::GetRoomNetwork() const { |
| return impl->room_network; |
| } |
|
|
| Tools::RenderdocAPI& System::GetRenderdocAPI() { |
| return *impl->renderdoc_api; |
| } |
|
|
| void System::RunServer(std::unique_ptr<Service::ServerManager>&& server_manager) { |
| return impl->kernel.RunServer(std::move(server_manager)); |
| } |
|
|
| void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) { |
| impl->execute_program_callback = std::move(callback); |
| } |
|
|
| void System::ExecuteProgram(std::size_t program_index) { |
| if (impl->execute_program_callback) { |
| impl->execute_program_callback(program_index); |
| } else { |
| LOG_CRITICAL(Core, "execute_program_callback must be initialized by the frontend"); |
| } |
| } |
|
|
| std::deque<std::vector<u8>>& System::GetUserChannel() { |
| return impl->user_channel; |
| } |
|
|
| void System::RegisterExitCallback(ExitCallback&& callback) { |
| impl->exit_callback = std::move(callback); |
| } |
|
|
| void System::Exit() { |
| if (impl->exit_callback) { |
| impl->exit_callback(); |
| } else { |
| LOG_CRITICAL(Core, "exit_callback must be initialized by the frontend"); |
| } |
| } |
|
|
| void System::ApplySettings() { |
| impl->RefreshTime(*this); |
|
|
| if (IsPoweredOn()) { |
| Renderer().RefreshBaseSettings(); |
| } |
| } |
|
|
| } |
|
|