Compare commits

...

3 commits

26 changed files with 494 additions and 12 deletions

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@ -17,6 +17,7 @@
#define CONFIG_DIR "config"
#define CRASH_DUMPS_DIR "crash_dumps"
#define DUMP_DIR "dump"
#define HOST_DIR "host"
#define KEYS_DIR "keys"
#define LOAD_DIR "load"
#define LOG_DIR "log"

View file

@ -155,6 +155,7 @@ public:
GenerateEdenPath(EdenPath::ConfigDir, eden_path_config);
GenerateEdenPath(EdenPath::CrashDumpsDir, eden_path / CRASH_DUMPS_DIR);
GenerateEdenPath(EdenPath::DumpDir, eden_path / DUMP_DIR);
GenerateEdenPath(EdenPath::HostDir, eden_path / HOST_DIR);
GenerateEdenPath(EdenPath::KeysDir, eden_path / KEYS_DIR);
GenerateEdenPath(EdenPath::LoadDir, eden_path / LOAD_DIR);
GenerateEdenPath(EdenPath::LogDir, eden_path / LOG_DIR);

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@ -20,6 +20,7 @@ enum class EdenPath {
ConfigDir, // Where config files are stored.
CrashDumpsDir, // Where crash dumps are stored.
DumpDir, // Where dumped data is stored.
HostDir, // Where the game has access to the host system.
KeysDir, // Where key files are stored.
LoadDir, // Where cheat/mod files are stored.
LogDir, // Where log files are stored.

View file

@ -25,6 +25,7 @@ SUB(Service, AM)
SUB(Service, AOC)
SUB(Service, APM)
SUB(Service, ARP)
SUB(Service, Banana)
SUB(Service, BCAT)
SUB(Service, BPC)
SUB(Service, BGTC)
@ -40,6 +41,8 @@ SUB(Service, Friend)
SUB(Service, FS)
SUB(Service, GRC)
SUB(Service, HID)
SUB(Service, HTC)
SUB(Service, HTCS)
SUB(Service, IRS)
SUB(Service, JIT)
SUB(Service, LBL)
@ -63,6 +66,7 @@ SUB(Service, NVDRV)
SUB(Service, Nvnflinger)
SUB(Service, OLSC)
SUB(Service, PCIE)
SUB(Service, PCM)
SUB(Service, PCTL)
SUB(Service, PCV)
SUB(Service, PM)

View file

@ -114,6 +114,8 @@ add_library(core STATIC
file_sys/fssystem/fssystem_switch_storage.h
file_sys/fssystem/fssystem_utility.cpp
file_sys/fssystem/fssystem_utility.h
file_sys/host_factory.cpp
file_sys/host_factory.h
file_sys/ips_layer.cpp
file_sys/ips_layer.h
file_sys/kernel_executable.cpp
@ -543,6 +545,8 @@ add_library(core STATIC
hle/service/audio/hardware_opus_decoder_manager.h
hle/service/audio/hardware_opus_decoder.cpp
hle/service/audio/hardware_opus_decoder.h
hle/service/banana/banana.cpp
hle/service/banana/banana.h
hle/service/bcat/backend/backend.cpp
hle/service/bcat/backend/backend.h
hle/service/bcat/bcat.cpp
@ -714,6 +718,10 @@ add_library(core STATIC
hle/service/hid/xcd.h
hle/service/hle_ipc.cpp
hle/service/hle_ipc.h
hle/service/htc/htc.cpp
hle/service/htc/htc.h
hle/service/htcs/htcs.cpp
hle/service/htcs/htcs.h
hle/service/ipc_helpers.h
hle/service/kernel_helpers.cpp
hle/service/kernel_helpers.h
@ -952,6 +960,8 @@ add_library(core STATIC
hle/service/os/process.h
hle/service/pcie/pcie.cpp
hle/service/pcie/pcie.h
hle/service/pcm/pcm.cpp
hle/service/pcm/pcm.h
hle/service/pctl/parental_control_service_factory.cpp
hle/service/pctl/parental_control_service_factory.h
hle/service/pctl/parental_control_service.cpp

View file

@ -0,0 +1,18 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/file_sys/host_factory.h"
#include "core/file_sys/vfs/vfs.h"
namespace FileSys {
HostFactory::HostFactory(VirtualDir host_dir_)
: host_dir(std::move(host_dir_)) {}
HostFactory::~HostFactory() = default;
VirtualDir HostFactory::Open() const {
return host_dir;
}
} // namespace FileSys

View file

@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/file_sys/vfs/vfs_types.h"
namespace FileSys {
class HostFactory {
public:
explicit HostFactory(VirtualDir host_dir_);
~HostFactory();
VirtualDir Open() const;
private:
VirtualDir host_dir;
};
} // namespace FileSys

View file

@ -25,6 +25,30 @@
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;
}
}
} // Anonymous namespace
IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet)
: ServiceFramework{system_, "IApplicationFunctions"}, m_applet{std::move(applet)} {
// clang-format off
@ -40,7 +64,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_
{21, D<&IApplicationFunctions::GetDesiredLanguage>, "GetDesiredLanguage"},
{22, D<&IApplicationFunctions::SetTerminateResult>, "SetTerminateResult"},
{23, D<&IApplicationFunctions::GetDisplayVersion>, "GetDisplayVersion"},
{24, nullptr, "GetLaunchStorageInfoForDebug"},
{24, D<&IApplicationFunctions::GetLaunchStorageInfoForDebug>, "GetLaunchStorageInfoForDebug"},
{25, D<&IApplicationFunctions::ExtendSaveData>, "ExtendSaveData"},
{26, D<&IApplicationFunctions::GetSaveDataSize>, "GetSaveDataSize"},
{27, D<&IApplicationFunctions::CreateCacheStorage>, "CreateCacheStorage"},
@ -231,6 +255,18 @@ Result IApplicationFunctions::GetDisplayVersion(Out<DisplayVersion> out_display_
R_SUCCEED();
}
Result IApplicationFunctions::GetLaunchStorageInfoForDebug(Out<u8> out_app_storage,
Out<u8> out_app_storage_update) {
LOG_DEBUG(Service_AM, "called");
auto& storage = system.GetContentProviderUnion();
*out_app_storage = static_cast<u8>(GetStorageIdForFrontendSlot(
storage.GetSlotForEntry(m_applet->program_id, FileSys::ContentRecordType::Program)));
*out_app_storage_update = static_cast<u8>(GetStorageIdForFrontendSlot(storage.GetSlotForEntry(
FileSys::GetUpdateTitleID(m_applet->program_id), FileSys::ContentRecordType::Program)));
R_SUCCEED();
}
Result IApplicationFunctions::ExtendSaveData(Out<u64> out_required_size, FileSys::SaveDataType type,
Common::UUID user_id, u64 normal_size,
u64 journal_size) {

View file

@ -36,6 +36,7 @@ private:
Result GetDesiredLanguage(Out<u64> out_language_code);
Result SetTerminateResult(Result terminate_result);
Result GetDisplayVersion(Out<DisplayVersion> out_display_version);
Result GetLaunchStorageInfoForDebug(Out<u8> out_app_storage, Out<u8> out_app_storage_update);
Result ExtendSaveData(Out<u64> out_required_size, FileSys::SaveDataType type,
Common::UUID user_id, u64 normal_size, u64 journal_size);
Result GetSaveDataSize(Out<u64> out_normal_size, Out<u64> out_journal_size,

View file

@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "common/logging.h"
#include "core/hle/service/banana/banana.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
namespace Service::Banana {
class IProfiler final : public ServiceFramework<IProfiler> {
public:
explicit IProfiler(Core::System& system_) : ServiceFramework{system_, "banana"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetSystemEvent"},
{1, &IProfiler::StartSignalingEvent, "StartSignalingEvent"},
{2, nullptr, "StopSignalingEvent"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void StartSignalingEvent(HLERequestContext& ctx) {
LOG_WARNING(Service_Banana, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
};
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("banana", std::make_shared<IProfiler>(system));
ServerManager::RunServer(std::move(server_manager));
}
} // namespace Service::Banana

View file

@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
namespace Core {
class System;
}
namespace Service::Banana {
void LoopProcess(Core::System& system);
} // namespace Service::Banana

View file

@ -16,6 +16,7 @@
#include "core/file_sys/card_image.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/host_factory.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs_factory.h"
@ -360,6 +361,22 @@ std::shared_ptr<FileSys::SaveDataFactory> FileSystemController::CreateSaveDataFa
std::move(save_directory));
}
Result FileSystemController::OpenHost(FileSys::VirtualDir* out_host) const {
LOG_TRACE(Service_FS, "Opening host");
if (host_factory == nullptr) {
return FileSys::ResultTargetNotFound;
}
auto host = host_factory->Open();
if (host == nullptr) {
return FileSys::ResultTargetNotFound;
}
*out_host = host;
return ResultSuccess;
}
Result FileSystemController::OpenSDMC(FileSys::VirtualDir* out_sdmc) const {
LOG_TRACE(Service_FS, "Opening SDMC");
@ -697,6 +714,8 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
const auto sdmc_load_dir_path = sdmc_dir_path / "atmosphere/contents";
const auto rw_mode = FileSys::OpenMode::ReadWrite;
auto host_directory =
vfs.OpenDirectory(Common::FS::GetEdenPathString(EdenPath::HostDir), rw_mode);
auto nand_directory =
vfs.OpenDirectory(Common::FS::GetEdenPathString(EdenPath::NANDDir), rw_mode);
auto sd_directory = vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_dir_path), rw_mode);
@ -716,6 +735,10 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
bis_factory->GetUserNANDContents());
}
if (host_factory == nullptr) {
host_factory = std::make_unique<FileSys::HostFactory>(host_directory);
}
if (sdmc_factory == nullptr) {
sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory),
std::move(sd_load_directory));

View file

@ -21,6 +21,7 @@ class System;
namespace FileSys {
class BISFactory;
class ExternalContentProvider;
class HostFactory;
class NCA;
class RegisteredCache;
class RegisteredCacheUnion;
@ -80,6 +81,7 @@ public:
std::shared_ptr<SaveDataController> OpenSaveDataController();
Result OpenHost(FileSys::VirtualDir* out_host) const;
Result OpenSDMC(FileSys::VirtualDir* out_sdmc) const;
Result OpenBISPartition(FileSys::VirtualDir* out_bis_partition,
FileSys::BisPartitionId id) const;
@ -141,6 +143,7 @@ private:
std::mutex registration_lock;
std::map<ProcessId, Registration> registrations;
std::unique_ptr<FileSys::HostFactory> host_factory;
std::unique_ptr<FileSys::SDMCFactory> sdmc_factory;
std::unique_ptr<FileSys::BISFactory> bis_factory;

View file

@ -61,7 +61,7 @@ FSP_SRV::FSP_SRV(Core::System& system_)
{11, nullptr, "OpenBisFileSystem"},
{12, nullptr, "OpenBisStorage"},
{13, nullptr, "InvalidateBisCache"},
{17, nullptr, "OpenHostFileSystem"},
{17, D<&FSP_SRV::OpenHostFileSystem>, "OpenHostFileSystem"},
{18, D<&FSP_SRV::OpenSdCardFileSystem>, "OpenSdCardFileSystem"},
{19, nullptr, "FormatSdCardFileSystem"},
{21, nullptr, "DeleteSaveDataFileSystem"},
@ -155,7 +155,7 @@ FSP_SRV::FSP_SRV(Core::System& system_)
{810, nullptr, "RegisterProgramIndexMapInfo"},
{1000, nullptr, "SetBisRootForHost"},
{1001, nullptr, "SetSaveDataSize"},
{1002, nullptr, "SetSaveDataRootPath"},
{1002, D<&FSP_SRV::SetSaveDataRootPath>, "SetSaveDataRootPath"},
{1003, D<&FSP_SRV::DisableAutoSaveDataCreation>, "DisableAutoSaveDataCreation"},
{1004, D<&FSP_SRV::SetGlobalAccessLogMode>, "SetGlobalAccessLogMode"},
{1005, D<&FSP_SRV::GetGlobalAccessLogMode>, "GetGlobalAccessLogMode"},
@ -217,6 +217,18 @@ Result FSP_SRV::OpenFileSystemWithPatch(OutInterface<IFileSystem> out_interface,
R_SUCCEED();
}
Result FSP_SRV::OpenHostFileSystem(OutInterface<IFileSystem> out_interface) {
LOG_DEBUG(Service_FS, "called");
FileSys::VirtualDir host_dir{};
fsc.OpenHost(&host_dir);
*out_interface = std::make_shared<IFileSystem>(system, host_dir,
SizeGetter::FromStorageId(fsc, FileSys::StorageId::Host));
R_SUCCEED();
}
Result FSP_SRV::OpenSdCardFileSystem(OutInterface<IFileSystem> out_interface) {
LOG_DEBUG(Service_FS, "called");
@ -532,6 +544,12 @@ Result FSP_SRV::IsSdCardAccessible(Out<bool> out_is_accessible) {
R_SUCCEED();
}
Result FSP_SRV::SetSaveDataRootPath(InBuffer<BufferAttr_HipcMapAlias> path) {
LOG_WARNING(Service_FS, "(STUBBED) called, path_size={}", path.size());
R_SUCCEED();
}
Result FSP_SRV::DisableAutoSaveDataCreation() {
LOG_DEBUG(Service_FS, "called");

View file

@ -53,6 +53,7 @@ private:
Result SetCurrentProcess(ClientProcessId pid);
Result OpenFileSystemWithPatch(OutInterface<IFileSystem> out_interface,
FileSystemProxyType type, u64 open_program_id);
Result OpenHostFileSystem(OutInterface<IFileSystem> out_interface);
Result OpenSdCardFileSystem(OutInterface<IFileSystem> out_interface);
Result CreateSaveDataFileSystem(FileSys::SaveDataCreationInfo save_create_struct,
FileSys::SaveDataAttribute save_struct, u128 uid);
@ -102,6 +103,7 @@ private:
FileSys::StorageId storage_id, u64 title_id);
Result OpenDataStorageWithProgramIndex(OutInterface<IStorage> out_interface, u8 program_index);
Result IsSdCardAccessible(Out<bool> out_is_accessible);
Result SetSaveDataRootPath(InBuffer<BufferAttr_HipcMapAlias> path);
Result DisableAutoSaveDataCreation();
Result SetGlobalAccessLogMode(AccessLogMode access_log_mode_);
Result GetGlobalAccessLogMode(Out<AccessLogMode> out_access_log_mode);

View file

@ -0,0 +1,95 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "common/logging.h"
#include "core/hle/service/htc/htc.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
namespace Service::HTC {
class IHtcManager final : public ServiceFramework<IHtcManager> {
public:
explicit IHtcManager(Core::System& system_) : ServiceFramework{system_, "htc"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IHtcManager::GetEnvironmentVariable, "GetEnvironmentVariable"},
{1, &IHtcManager::GetEnvironmentVariableLength, "GetEnvironmentVariableLength"},
{2, nullptr, "BindHostConnectionEvent"},
{3, nullptr, "BindHostDisconnectionEvent"},
{4, nullptr, "BindHostConnectionEventForSystem"},
{5, nullptr, "BindHostDisconnectionEventForSystem"},
{6, nullptr, "GetBridgeIpAddress"}, // 3.0.0+
{7, nullptr, "GetBridgePort"}, // 3.0.0+
{8, nullptr, "SetCradleAttached"}, // 3.0.0+
{9, nullptr, "GetBridgeSubnetMask"}, // 4.0.0+
{10, nullptr, "GetBridgeMacAddress"}, // 4.0.0+
{11, nullptr, "GetWorkingDirectoryPath"}, // 6.0.0+
{12, nullptr, "GetWorkingDirectoryPathSize"}, // 6.0.0+
{13, nullptr, "RunOnHostStart"}, // 6.0.0+
{14, nullptr, "RunOnHostResults"}, // 9.0.0+
{15, nullptr, "SetBridgeIpAddress"}, // 20.0.0+
{16, nullptr, "SetBridgeSubnetMask"}, // 20.0.0+
{17, nullptr, "SetBridgePort"}, // 20.0.0+
{18, nullptr, "GetBridgeSerialNumber"}, // 20.0.0+
{19, nullptr, "GetBridgeFwVersion"}, // 20.0.0+
{20, nullptr, "ResetBridgeSettings"}, // 20.0.0+
{21, nullptr, "BeginUpdateBridge"}, // 19.0.0+
{22, nullptr, "ContinueUpdateBridge"}, // 19.0.0+
{23, nullptr, "EndUpdateBridge"}, // 19.0.0+
{24, nullptr, "GetBridgeType"}, // 19.0.0+
{25, nullptr, "GetBridgeDefaultGateway"}, // 20.0.0+
{26, nullptr, "SetBridgeDefaultGateway"}, // 20.0.0+
{27, nullptr, "GetBridgeDhcp"}, // 20.0.0+
{28, nullptr, "SetBridgeDhcp"}, // 20.0.0+
{29, nullptr, "GetBridgeProgramVersion"}, // 21.0.0+
};
// clang-format on
RegisterHandlers(functions);
}
private:
void GetEnvironmentVariable(HLERequestContext& ctx) {
LOG_WARNING(Service_HTC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.Push(0);
}
void GetEnvironmentVariableLength(HLERequestContext& ctx) {
LOG_WARNING(Service_HTC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.Push(0);
}
};
class IServiceManager : public ServiceFramework<IServiceManager> {
public:
explicit IServiceManager(Core::System& system_) : ServiceFramework{system_, "htc:tenv"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IServiceManager::GetServiceInterface, "GetServiceInterface"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void GetServiceInterface(HLERequestContext& ctx) {
LOG_WARNING(Service_HTC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
};
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("htc:tenv", std::make_shared<IServiceManager>(system));
server_manager->RegisterNamedService("htc", std::make_shared<IHtcManager>(system));
ServerManager::RunServer(std::move(server_manager));
}
} // namespace Service::HTC

View file

@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
namespace Core {
class System;
}
namespace Service::HTC {
void LoopProcess(Core::System& system);
} // namespace Service::HTC

View file

@ -0,0 +1,72 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "common/logging.h"
#include "core/hle/service/htcs/htcs.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
namespace Service::HTCS {
class IHtcsManager final : public ServiceFramework<IHtcsManager> {
public:
explicit IHtcsManager(Core::System& system_) : ServiceFramework{system_, "htcs"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Socket"},
{1, nullptr, "Close"},
{2, nullptr, "Connect"},
{3, nullptr, "Bind"},
{4, nullptr, "Listen"},
{5, nullptr, "Accept"},
{6, nullptr, "Recv"},
{7, nullptr, "Send"},
{8, nullptr, "Shutdown"},
{9, nullptr, "Fcntl"},
{10, &IHtcsManager::GetPeerName, "GetPeerNameAny"},
{11, nullptr, "GetDefaultHostName"},
{12, nullptr, "CreateSocketOld"},
{13, &IHtcsManager::CreateSocket, "CreateSocket"},
{100, &IHtcsManager::RegisterProcessId, "RegisterProcessId"},
{101, &IHtcsManager::MonitorManager, "MonitorManager"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void GetPeerName(HLERequestContext& ctx) {
LOG_WARNING(Service_HTCS, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void CreateSocket(HLERequestContext& ctx) {
// debug because this gets spammed a LOT
LOG_DEBUG(Service_HTCS, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void RegisterProcessId(HLERequestContext& ctx) {
LOG_WARNING(Service_HTCS, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void MonitorManager(HLERequestContext& ctx) {
LOG_WARNING(Service_HTCS, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
};
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("htcs", std::make_shared<IHtcsManager>(system));
ServerManager::RunServer(std::move(server_manager));
}
} // namespace Service::HTCS

View file

@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
namespace Core {
class System;
}
namespace Service::HTCS {
void LoopProcess(Core::System& system);
} // namespace Service::HTCS

View file

@ -0,0 +1,42 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "common/logging.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/pcm/pcm.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
namespace Service::PCM {
class IManager final : public ServiceFramework<IManager> {
public:
explicit IManager(Core::System& system_) : ServiceFramework{system_, "pcm"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "IsSupported"},
{1, &IManager::ReadCurrentPower, "ReadCurrentPower"},
{2, nullptr, "IsServiceEnabled"}, // 4.0.0+
{3, nullptr, "ReadCurrentVoltage"}, // 4.0.0+
};
// clang-format on
RegisterHandlers(functions);
}
private:
void ReadCurrentPower(HLERequestContext& ctx) {
LOG_DEBUG(Service_PCM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.Push(0);
}
};
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("pcm", std::make_shared<IManager>(system));
ServerManager::RunServer(std::move(server_manager));
}
} // namespace Service::PCM

View file

@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
namespace Core {
class System;
}
namespace Service::PCM {
void LoopProcess(Core::System& system);
} // namespace Service::PCM

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -381,11 +384,16 @@ Result ServerManager::CompleteSyncRequest(Session* session) {
R_SUCCEED();
}
if (service_res == IPC::ResultSessionClosed) {
this->DestroySession(session);
R_SUCCEED();
}
// Send the reply.
res = server_session->SendReplyHLE();
// If the session has been closed, we're done.
if (res == Kernel::ResultSessionClosed || service_res == IPC::ResultSessionClosed) {
if (res == Kernel::ResultSessionClosed) {
this->DestroySession(session);
R_SUCCEED();
}

View file

@ -105,8 +105,6 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
switch (ctx.GetCommandType()) {
case IPC::CommandType::Close:
case IPC::CommandType::TIPC_Close: {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
result = IPC::ResultSessionClosed;
break;
}
@ -132,7 +130,7 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
// If emulation was shutdown, we are closing service threads, do not write the response back to
// memory that may be shutting down as well.
if (system.IsPoweredOn()) {
if (system.IsPoweredOn() && result != IPC::ResultSessionClosed) {
ctx.WriteToOutgoingCommandBuffer();
}

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@ -11,6 +11,7 @@
#include "core/hle/service/aoc/addon_content_manager.h"
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/audio/audio.h"
#include "core/hle/service/banana/banana.h"
#include "core/hle/service/bcat/bcat.h"
#include "core/hle/service/bpc/bpc.h"
#include "core/hle/service/btdrv/btdrv.h"
@ -26,6 +27,8 @@
#include "core/hle/service/glue/glue.h"
#include "core/hle/service/grc/grc.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/htc/htc.h"
#include "core/hle/service/htcs/htcs.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/jit/jit.h"
#include "core/hle/service/lbl/lbl.h"
@ -49,6 +52,7 @@
#include "core/hle/service/olsc/olsc.h"
#include "core/hle/service/omm/omm.h"
#include "core/hle/service/pcie/pcie.h"
#include "core/hle/service/pcm/pcm.h"
#include "core/hle/service/pctl/pctl.h"
#include "core/hle/service/pcv/pcv.h"
#include "core/hle/service/pm/pm.h"
@ -102,6 +106,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
{"am", &AM::LoopProcess},
{"aoc", &AOC::LoopProcess},
{"apm", &APM::LoopProcess},
{"banana", &Banana::LoopProcess},
{"bcat", &BCAT::LoopProcess},
{"bpc", &BPC::LoopProcess},
{"btdrv", &BtDrv::LoopProcess},
@ -118,6 +123,8 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
{"glue", &Glue::LoopProcess},
{"grc", &GRC::LoopProcess},
{"hid", &HID::LoopProcess},
{"htc", &HTC::LoopProcess},
{"htcs", &HTCS::LoopProcess},
{"lbl", &LBL::LoopProcess},
{"LogManager.Prod", &LM::LoopProcess},
{"mig", &Migration::LoopProcess},
@ -136,6 +143,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
{"olsc", &OLSC::LoopProcess},
{"omm", &OMM::LoopProcess},
{"pcie", &PCIe::LoopProcess},
{"pcm", &PCM::LoopProcess},
{"pctl", &PCTL::LoopProcess},
{"pcv", &PCV::LoopProcess},
{"prepo", &PlayReport::LoopProcess},

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@ -42,7 +42,7 @@ IApplicationDisplayService::IApplicationDisplayService(Core::System& system_,
{2451, nullptr, "GetIndirectLayerImageCropMap"},
{2460, C<&IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo>, "GetIndirectLayerImageRequiredMemoryInfo"},
{5202, C<&IApplicationDisplayService::GetDisplayVsyncEvent>, "GetDisplayVsyncEvent"},
{5203, nullptr, "GetDisplayVsyncEventForDebug"},
{5203, C<&IApplicationDisplayService::GetDisplayVsyncEventForDebug>, "GetDisplayVsyncEventForDebug"},
};
// clang-format on
@ -53,6 +53,9 @@ IApplicationDisplayService::~IApplicationDisplayService() {
for (auto& [display_id, event] : m_display_vsync_events) {
m_container->UnlinkVsyncEvent(display_id, &event);
}
for (auto& [display_id, event] : m_display_vsync_events_debug) {
m_container->UnlinkVsyncEvent(display_id, &event);
}
for (const auto layer_id : m_open_layer_ids) {
m_container->CloseLayer(layer_id);
}
@ -263,6 +266,21 @@ Result IApplicationDisplayService::GetDisplayVsyncEvent(
R_SUCCEED();
}
Result IApplicationDisplayService::GetDisplayVsyncEventForDebug(
OutCopyHandle<Kernel::KReadableEvent> out_vsync_event, u64 display_id) {
LOG_DEBUG(Service_VI, "called. display_id={}", display_id);
std::scoped_lock lk{m_lock};
auto [it, created] = m_display_vsync_events_debug.emplace(display_id, m_context);
R_UNLESS(created, VI::ResultPermissionDenied);
m_container->LinkVsyncEvent(display_id, &it->second);
*out_vsync_event = it->second.GetHandle();
R_SUCCEED();
}
Result IApplicationDisplayService::ConvertScalingMode(Out<ConvertedScaleMode> out_scaling_mode,
NintendoScaleMode mode) {
LOG_DEBUG(Service_VI, "called mode={}", mode);

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -58,6 +61,8 @@ public:
Result DestroyStrayLayer(u64 layer_id);
Result GetDisplayVsyncEvent(OutCopyHandle<Kernel::KReadableEvent> out_vsync_event,
u64 display_id);
Result GetDisplayVsyncEventForDebug(OutCopyHandle<Kernel::KReadableEvent> out_vsync_event,
u64 display_id);
Result ConvertScalingMode(Out<ConvertedScaleMode> out_scaling_mode, NintendoScaleMode mode);
Result GetIndirectLayerImageMap(
Out<u64> out_size, Out<u64> out_stride,
@ -75,6 +80,7 @@ private:
std::set<u64> m_open_layer_ids{};
std::set<u64> m_stray_layer_ids{};
std::map<u64, Event> m_display_vsync_events{};
std::map<u64, Event> m_display_vsync_events_debug{};
bool m_vsync_event_fetched{false};
};