mirror of
https://github.com/PabloMK7/citra.git
synced 2025-09-11 13:20:04 +00:00
Add 3GX plugin loader (#6172)
* Initial plugin loader support * More plugin loader progress * Organize code and more plugin features * Fix clang-format * Fix compilation and add android gui * Fix clang-format * Fix macos build * Fix copy-paste bug and clang-format * More merge fixes * Make suggestions * Move global variable to static member * Fix typo * Apply suggestions * Proper initialization order * Allocate plugin memory from SYSTEM instead of APPLICATION * Do not mark free pages as RWX * Fix plugins in old 3DS mode. * Implement KernelSetState and notif 0x203 * Apply changes * Remove unused variable * Fix dynarmic commit * Sublicense files with MIT License * Remove non-ascii characters from license
This commit is contained in:
parent
48ee112ceb
commit
016ce6c286
38 changed files with 1911 additions and 42 deletions
|
@ -8,6 +8,8 @@
|
|||
#include "common/microprofile.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/plugin_3gx.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/shared_memory.h"
|
||||
#include "core/hle/kernel/shared_page.h"
|
||||
|
@ -69,6 +71,9 @@ static PAddr VirtualToPhysicalAddress(VAddr addr) {
|
|||
if (addr >= Memory::NEW_LINEAR_HEAP_VADDR && addr <= Memory::NEW_LINEAR_HEAP_VADDR_END) {
|
||||
return addr - Memory::NEW_LINEAR_HEAP_VADDR + Memory::FCRAM_PADDR;
|
||||
}
|
||||
if (addr >= Memory::PLUGIN_3GX_FB_VADDR && addr <= Memory::PLUGIN_3GX_FB_VADDR_END) {
|
||||
return addr - Memory::PLUGIN_3GX_FB_VADDR + Service::PLGLDR::PLG_LDR::GetPluginFBAddr();
|
||||
}
|
||||
|
||||
LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x{:08X}", addr);
|
||||
// To help with debugging, set bit on address so that it's obviously invalid.
|
||||
|
|
278
src/core/hle/service/plgldr/plgldr.cpp
Normal file
278
src/core/hle/service/plgldr/plgldr.cpp
Normal file
|
@ -0,0 +1,278 @@
|
|||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// Copyright 2022 The Pixellizer Group
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
// associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||
// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include <boost/serialization/weak_ptr.hpp>
|
||||
#include <fmt/format.h>
|
||||
#include "common/archives.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/plugin_3gx.h"
|
||||
#include "core/frontend/mic.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/shared_memory.h"
|
||||
#include "core/hle/service/plgldr/plgldr.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(Service::PLGLDR::PLG_LDR)
|
||||
|
||||
namespace Service::PLGLDR {
|
||||
|
||||
const Kernel::CoreVersion PLG_LDR::plgldr_version = Kernel::CoreVersion(1, 0, 0);
|
||||
PLG_LDR::PluginLoaderContext PLG_LDR::plgldr_context;
|
||||
bool PLG_LDR::allow_game_change = true;
|
||||
PAddr PLG_LDR::plugin_fb_addr = 0;
|
||||
|
||||
PLG_LDR::PLG_LDR() : ServiceFramework{"plg:ldr", 1} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{IPC::MakeHeader(1, 0, 2), nullptr, "LoadPlugin"},
|
||||
{IPC::MakeHeader(2, 0, 0), &PLG_LDR::IsEnabled, "IsEnabled"},
|
||||
{IPC::MakeHeader(3, 1, 0), &PLG_LDR::SetEnabled, "SetEnabled"},
|
||||
{IPC::MakeHeader(4, 2, 4), &PLG_LDR::SetLoadSettings, "SetLoadSettings"},
|
||||
{IPC::MakeHeader(5, 1, 8), nullptr, "DisplayMenu"},
|
||||
{IPC::MakeHeader(6, 0, 4), nullptr, "DisplayMessage"},
|
||||
{IPC::MakeHeader(7, 1, 4), &PLG_LDR::DisplayErrorMessage, "DisplayErrorMessage"},
|
||||
{IPC::MakeHeader(8, 0, 0), &PLG_LDR::GetPLGLDRVersion, "GetPLGLDRVersion"},
|
||||
{IPC::MakeHeader(9, 0, 0), &PLG_LDR::GetArbiter, "GetArbiter"},
|
||||
{IPC::MakeHeader(10, 0, 2), &PLG_LDR::GetPluginPath, "GetPluginPath"},
|
||||
{IPC::MakeHeader(11, 1, 0), nullptr, "SetRosalinaMenuBlock"},
|
||||
{IPC::MakeHeader(12, 2, 4), nullptr, "SetSwapParam"},
|
||||
{IPC::MakeHeader(13, 1, 2), nullptr, "SetLoadExeParam"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
plgldr_context.memory_changed_handle = 0;
|
||||
plgldr_context.plugin_loaded = false;
|
||||
}
|
||||
|
||||
void PLG_LDR::OnProcessRun(Kernel::Process& process, Kernel::KernelSystem& kernel) {
|
||||
if (!plgldr_context.is_enabled || plgldr_context.plugin_loaded) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
// Same check as original plugin loader, plugins are not supported in homebrew apps
|
||||
u32 value1, value2;
|
||||
kernel.memory.ReadBlock(process, process.codeset->CodeSegment().addr, &value1, 4);
|
||||
kernel.memory.ReadBlock(process, process.codeset->CodeSegment().addr + 32, &value2, 4);
|
||||
// Check for "B #0x20" and "MOV R4, LR" instructions
|
||||
bool is_homebrew = u32_le(value1) == 0xEA000006 && u32_le(value2) == 0xE1A0400E;
|
||||
if (is_homebrew) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
FileSys::Plugin3GXLoader plugin_loader;
|
||||
if (plgldr_context.use_user_load_parameters &&
|
||||
plgldr_context.user_load_parameters.low_title_Id ==
|
||||
static_cast<u32>(process.codeset->program_id) &&
|
||||
plgldr_context.user_load_parameters.path[0]) {
|
||||
std::string plugin_file = FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) +
|
||||
std::string(plgldr_context.user_load_parameters.path + 1);
|
||||
plgldr_context.is_default_path = false;
|
||||
plgldr_context.plugin_path = plugin_file;
|
||||
plugin_loader.Load(plgldr_context, process, kernel);
|
||||
} else {
|
||||
const std::string plugin_root =
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "luma/plugins/";
|
||||
const std::string plugin_tid =
|
||||
plugin_root + fmt::format("{:016X}", process.codeset->program_id);
|
||||
FileUtil::FSTEntry entry;
|
||||
FileUtil::ScanDirectoryTree(plugin_tid, entry);
|
||||
for (const auto child : entry.children) {
|
||||
if (!child.isDirectory && child.physicalName.ends_with(".3gx")) {
|
||||
plgldr_context.is_default_path = false;
|
||||
plgldr_context.plugin_path = child.physicalName;
|
||||
if (plugin_loader.Load(plgldr_context, process, kernel) ==
|
||||
Loader::ResultStatus::Success) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::string default_path = plugin_root + "default.3gx";
|
||||
if (FileUtil::Exists(default_path)) {
|
||||
plgldr_context.is_default_path = true;
|
||||
plgldr_context.plugin_path = default_path;
|
||||
plugin_loader.Load(plgldr_context, process, kernel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PLG_LDR::OnProcessExit(Kernel::Process& process, Kernel::KernelSystem& kernel) {
|
||||
if (plgldr_context.plugin_loaded) {
|
||||
u32 status = kernel.memory.Read32(FileSys::Plugin3GXLoader::_3GX_exe_load_addr - 0xC);
|
||||
if (status == 0) {
|
||||
LOG_CRITICAL(Service_PLGLDR, "Failed to launch {}: Checksum failed",
|
||||
plgldr_context.plugin_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ResultVal<Kernel::Handle> PLG_LDR::GetMemoryChangedHandle(Kernel::KernelSystem& kernel) {
|
||||
if (plgldr_context.memory_changed_handle)
|
||||
return MakeResult(plgldr_context.memory_changed_handle);
|
||||
|
||||
std::shared_ptr<Kernel::Event> evt = kernel.CreateEvent(
|
||||
Kernel::ResetType::OneShot,
|
||||
fmt::format("event-{:08x}", Core::System::GetInstance().GetRunningCore().GetReg(14)));
|
||||
CASCADE_RESULT(plgldr_context.memory_changed_handle,
|
||||
kernel.GetCurrentProcess()->handle_table.Create(std::move(evt)));
|
||||
|
||||
return MakeResult(plgldr_context.memory_changed_handle);
|
||||
}
|
||||
|
||||
void PLG_LDR::OnMemoryChanged(Kernel::Process& process, Kernel::KernelSystem& kernel) {
|
||||
if (!plgldr_context.plugin_loaded || !plgldr_context.memory_changed_handle)
|
||||
return;
|
||||
|
||||
std::shared_ptr<Kernel::Event> evt =
|
||||
kernel.GetCurrentProcess()->handle_table.Get<Kernel::Event>(
|
||||
plgldr_context.memory_changed_handle);
|
||||
if (evt == nullptr)
|
||||
return;
|
||||
|
||||
evt->Signal();
|
||||
}
|
||||
|
||||
void PLG_LDR::IsEnabled(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 2, 0, 0);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(plgldr_context.is_enabled);
|
||||
}
|
||||
|
||||
void PLG_LDR::SetEnabled(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 3, 1, 0);
|
||||
bool enabled = rp.Pop<u32>() == 1;
|
||||
|
||||
bool can_change = enabled == plgldr_context.is_enabled || allow_game_change;
|
||||
if (can_change) {
|
||||
plgldr_context.is_enabled = enabled;
|
||||
Settings::values.plugin_loader_enabled.SetValue(enabled);
|
||||
}
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push((can_change) ? RESULT_SUCCESS : Kernel::ERR_NOT_AUTHORIZED);
|
||||
}
|
||||
|
||||
void PLG_LDR::SetLoadSettings(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 4, 2, 4);
|
||||
|
||||
plgldr_context.use_user_load_parameters = true;
|
||||
plgldr_context.user_load_parameters.no_flash = rp.Pop<u32>() == 1;
|
||||
plgldr_context.user_load_parameters.low_title_Id = rp.Pop<u32>();
|
||||
|
||||
auto path = rp.PopMappedBuffer();
|
||||
|
||||
path.Read(
|
||||
plgldr_context.user_load_parameters.path, 0,
|
||||
std::min(sizeof(PluginLoaderContext::PluginLoadParameters::path) - 1, path.GetSize()));
|
||||
plgldr_context.user_load_parameters.path[std::min(
|
||||
sizeof(PluginLoaderContext::PluginLoadParameters::path) - 1, path.GetSize())] = '\0';
|
||||
|
||||
auto config = rp.PopMappedBuffer();
|
||||
config.Read(
|
||||
plgldr_context.user_load_parameters.config, 0,
|
||||
std::min(sizeof(PluginLoaderContext::PluginLoadParameters::config), config.GetSize()));
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void PLG_LDR::DisplayErrorMessage(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 7, 1, 4);
|
||||
u32 error_code = rp.Pop<u32>();
|
||||
auto title = rp.PopMappedBuffer();
|
||||
auto desc = rp.PopMappedBuffer();
|
||||
|
||||
std::vector<char> title_data(title.GetSize() + 1);
|
||||
std::vector<char> desc_data(desc.GetSize() + 1);
|
||||
|
||||
title.Read(title_data.data(), 0, title.GetSize());
|
||||
title_data[title.GetSize()] = '\0';
|
||||
|
||||
desc.Read(desc_data.data(), 0, desc.GetSize());
|
||||
desc_data[desc.GetSize()] = '\0';
|
||||
|
||||
LOG_ERROR(Service_PLGLDR, "Plugin error - Code: {} - Title: {} - Description: {}", error_code,
|
||||
std::string(title_data.data()), std::string(desc_data.data()));
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void PLG_LDR::GetPLGLDRVersion(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 8, 0, 0);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(plgldr_version.raw);
|
||||
}
|
||||
|
||||
void PLG_LDR::GetArbiter(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 9, 0, 0);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
// NOTE: It doesn't make sense to send an arbiter in HLE, as it's used to
|
||||
// signal the plgldr service thread when a event is ready. Instead we just send
|
||||
// an error and the 3GX plugin will take care of it.
|
||||
// (We never send any events anyways)
|
||||
rb.Push(Kernel::ERR_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
void PLG_LDR::GetPluginPath(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 10, 0, 2);
|
||||
auto path = rp.PopMappedBuffer();
|
||||
|
||||
// Same behaviour as strncpy
|
||||
std::string root_sd = FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir);
|
||||
std::string plugin_path = plgldr_context.plugin_path;
|
||||
auto it = plugin_path.find(root_sd);
|
||||
if (it != plugin_path.npos)
|
||||
plugin_path.erase(it, root_sd.size());
|
||||
|
||||
std::replace(plugin_path.begin(), plugin_path.end(), '\\', '/');
|
||||
if (plugin_path.empty() || plugin_path[0] != '/')
|
||||
plugin_path = "/" + plugin_path;
|
||||
|
||||
path.Write(plugin_path.c_str(), 0, std::min(path.GetSize(), plugin_path.length() + 1));
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushMappedBuffer(path);
|
||||
}
|
||||
|
||||
std::shared_ptr<PLG_LDR> GetService(Core::System& system) {
|
||||
if (!system.KernelRunning())
|
||||
return nullptr;
|
||||
auto it = system.Kernel().named_ports.find("plg:ldr");
|
||||
if (it != system.Kernel().named_ports.end())
|
||||
return std::static_pointer_cast<PLG_LDR>(it->second->GetServerPort()->hle_handler);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void InstallInterfaces(Core::System& system) {
|
||||
std::make_shared<PLG_LDR>()->InstallAsNamedPort(system.Kernel());
|
||||
}
|
||||
|
||||
} // namespace Service::PLGLDR
|
149
src/core/hle/service/plgldr/plgldr.h
Normal file
149
src/core/hle/service/plgldr/plgldr.h
Normal file
|
@ -0,0 +1,149 @@
|
|||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// Copyright 2022 The Pixellizer Group
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
// associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||
// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <boost/serialization/version.hpp>
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::PLGLDR {
|
||||
|
||||
class PLG_LDR final : public ServiceFramework<PLG_LDR> {
|
||||
public:
|
||||
struct PluginLoaderContext {
|
||||
struct PluginLoadParameters {
|
||||
u8 no_flash = 0;
|
||||
u8 no_IR_Patch = 0;
|
||||
u32_le low_title_Id = 0;
|
||||
char path[256] = {0};
|
||||
u32_le config[32] = {0};
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& no_flash;
|
||||
ar& no_IR_Patch;
|
||||
ar& low_title_Id;
|
||||
ar& path;
|
||||
ar& config;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
bool is_enabled = true;
|
||||
bool plugin_loaded = false;
|
||||
bool is_default_path = false;
|
||||
std::string plugin_path = "";
|
||||
|
||||
bool use_user_load_parameters = false;
|
||||
PluginLoadParameters user_load_parameters;
|
||||
|
||||
VAddr plg_event = 0;
|
||||
VAddr plg_reply = 0;
|
||||
Kernel::Handle memory_changed_handle = 0;
|
||||
|
||||
bool is_exe_load_function_set = false;
|
||||
u32 exe_load_checksum = 0;
|
||||
|
||||
std::vector<u32> load_exe_func;
|
||||
u32_le load_exe_args[4] = {0};
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& is_enabled;
|
||||
ar& plugin_loaded;
|
||||
ar& is_default_path;
|
||||
ar& plugin_path;
|
||||
ar& use_user_load_parameters;
|
||||
ar& user_load_parameters;
|
||||
ar& plg_event;
|
||||
ar& plg_reply;
|
||||
ar& memory_changed_handle;
|
||||
ar& is_exe_load_function_set;
|
||||
ar& exe_load_checksum;
|
||||
ar& load_exe_func;
|
||||
ar& load_exe_args;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
PLG_LDR();
|
||||
~PLG_LDR() {}
|
||||
|
||||
void OnProcessRun(Kernel::Process& process, Kernel::KernelSystem& kernel);
|
||||
void OnProcessExit(Kernel::Process& process, Kernel::KernelSystem& kernel);
|
||||
ResultVal<Kernel::Handle> GetMemoryChangedHandle(Kernel::KernelSystem& kernel);
|
||||
void OnMemoryChanged(Kernel::Process& process, Kernel::KernelSystem& kernel);
|
||||
|
||||
static void SetEnabled(bool enabled) {
|
||||
plgldr_context.is_enabled = enabled;
|
||||
}
|
||||
static bool GetEnabled() {
|
||||
return plgldr_context.is_enabled;
|
||||
}
|
||||
static void SetAllowGameChangeState(bool allow) {
|
||||
allow_game_change = allow;
|
||||
}
|
||||
static bool GetAllowGameChangeState() {
|
||||
return allow_game_change;
|
||||
}
|
||||
static void SetPluginFBAddr(PAddr addr) {
|
||||
plugin_fb_addr = addr;
|
||||
}
|
||||
static PAddr GetPluginFBAddr() {
|
||||
return plugin_fb_addr;
|
||||
}
|
||||
|
||||
private:
|
||||
static const Kernel::CoreVersion plgldr_version;
|
||||
|
||||
static PluginLoaderContext plgldr_context;
|
||||
static PAddr plugin_fb_addr;
|
||||
static bool allow_game_change;
|
||||
|
||||
void IsEnabled(Kernel::HLERequestContext& ctx);
|
||||
void SetEnabled(Kernel::HLERequestContext& ctx);
|
||||
void SetLoadSettings(Kernel::HLERequestContext& ctx);
|
||||
void DisplayErrorMessage(Kernel::HLERequestContext& ctx);
|
||||
void GetPLGLDRVersion(Kernel::HLERequestContext& ctx);
|
||||
void GetArbiter(Kernel::HLERequestContext& ctx);
|
||||
void GetPluginPath(Kernel::HLERequestContext& ctx);
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<Kernel::SessionRequestHandler>(*this);
|
||||
ar& plgldr_context;
|
||||
ar& plugin_fb_addr;
|
||||
ar& allow_game_change;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
std::shared_ptr<PLG_LDR> GetService(Core::System& system);
|
||||
|
||||
void InstallInterfaces(Core::System& system);
|
||||
|
||||
} // namespace Service::PLGLDR
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(Service::PLGLDR::PLG_LDR)
|
|
@ -41,6 +41,7 @@
|
|||
#include "core/hle/service/nfc/nfc.h"
|
||||
#include "core/hle/service/nim/nim.h"
|
||||
#include "core/hle/service/nwm/nwm.h"
|
||||
#include "core/hle/service/plgldr/plgldr.h"
|
||||
#include "core/hle/service/pm/pm.h"
|
||||
#include "core/hle/service/ps/ps_ps.h"
|
||||
#include "core/hle/service/ptm/ptm.h"
|
||||
|
@ -55,7 +56,7 @@
|
|||
|
||||
namespace Service {
|
||||
|
||||
const std::array<ServiceModuleInfo, 40> service_module_map{
|
||||
const std::array<ServiceModuleInfo, 41> service_module_map{
|
||||
{{"FS", 0x00040130'00001102, FS::InstallInterfaces},
|
||||
{"PM", 0x00040130'00001202, PM::InstallInterfaces},
|
||||
{"LDR", 0x00040130'00003702, LDR::InstallInterfaces},
|
||||
|
@ -94,6 +95,7 @@ const std::array<ServiceModuleInfo, 40> service_module_map{
|
|||
{"SOC", 0x00040130'00002E02, SOC::InstallInterfaces},
|
||||
{"SSL", 0x00040130'00002F02, SSL::InstallInterfaces},
|
||||
{"PS", 0x00040130'00003102, PS::InstallInterfaces},
|
||||
{"PLGLDR", 0x00040130'00006902, PLGLDR::InstallInterfaces},
|
||||
// no HLE implementation
|
||||
{"CDC", 0x00040130'00001802, nullptr},
|
||||
{"GPIO", 0x00040130'00001B02, nullptr},
|
||||
|
|
|
@ -194,7 +194,7 @@ struct ServiceModuleInfo {
|
|||
std::function<void(Core::System&)> init_function;
|
||||
};
|
||||
|
||||
extern const std::array<ServiceModuleInfo, 40> service_module_map;
|
||||
extern const std::array<ServiceModuleInfo, 41> service_module_map;
|
||||
|
||||
} // namespace Service
|
||||
|
||||
|
|
|
@ -243,10 +243,18 @@ void SRV::PublishToSubscriber(Kernel::HLERequestContext& ctx) {
|
|||
u32 notification_id = rp.Pop<u32>();
|
||||
u8 flags = rp.Pop<u8>();
|
||||
|
||||
// Handle notification 0x203 in HLE, as this one may be used by homebrew to power off the
|
||||
// console. Normally, this is handled by NS. If notification handling is properly implemented,
|
||||
// this piece of code should be removed, and handled by subscribing from NS instead.
|
||||
if (notification_id == 0x203) {
|
||||
Core::System::GetInstance().RequestShutdown();
|
||||
} else {
|
||||
LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x{:X}, flags={}",
|
||||
notification_id, flags);
|
||||
}
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x{:X}, flags={}", notification_id,
|
||||
flags);
|
||||
}
|
||||
|
||||
void SRV::RegisterService(Kernel::HLERequestContext& ctx) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue