mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-23 07:56:52 +00:00
a41a98028a
Signed-off-by: lizzie <lizzie@eden-emu.dev> - [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions). - [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md) - [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability. ------------------- this PR adds various missing services (acc:e, acc:e:u1) to expand homebrew compatibility notably this also fixes NPad related crashes due to nullref Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4264 Reviewed-by: Maufeat <sahyno1996@gmail.com> Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
454 lines
17 KiB
C++
454 lines
17 KiB
C++
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
#include "core/core.h"
|
|
#include "core/file_sys/registered_cache.h"
|
|
#include "core/file_sys/romfs_factory.h"
|
|
#include "core/hle/api_version.h"
|
|
#include "core/hle/service/filesystem/filesystem.h"
|
|
#include "core/hle/service/ipc_helpers.h"
|
|
#include "core/hle/service/ncm/ncm.h"
|
|
#include "core/hle/service/server_manager.h"
|
|
#include "core/hle/service/service.h"
|
|
|
|
namespace Service::NCM {
|
|
|
|
class ILocationResolver final : public ServiceFramework<ILocationResolver> {
|
|
public:
|
|
explicit ILocationResolver(Core::System& system_, FileSys::StorageId id)
|
|
: ServiceFramework{system_, "ILocationResolver"}, storage{id} {
|
|
// clang-format off
|
|
static const FunctionInfo functions[] = {
|
|
{0, nullptr, "ResolveProgramPath"},
|
|
{1, nullptr, "RedirectProgramPath"},
|
|
{2, nullptr, "ResolveApplicationControlPath"},
|
|
{3, nullptr, "ResolveApplicationHtmlDocumentPath"},
|
|
{4, nullptr, "ResolveDataPath"},
|
|
{5, nullptr, "RedirectApplicationControlPath"},
|
|
{6, nullptr, "RedirectApplicationHtmlDocumentPath"},
|
|
{7, nullptr, "ResolveApplicationLegalInformationPath"},
|
|
{8, nullptr, "RedirectApplicationLegalInformationPath"},
|
|
{9, nullptr, "Refresh"},
|
|
{10, nullptr, "RedirectApplicationProgramPath"},
|
|
{11, nullptr, "ClearApplicationRedirection"},
|
|
{12, nullptr, "EraseProgramRedirection"},
|
|
{13, nullptr, "EraseApplicationControlRedirection"},
|
|
{14, nullptr, "EraseApplicationHtmlDocumentRedirection"},
|
|
{15, nullptr, "EraseApplicationLegalInformationRedirection"},
|
|
{16, nullptr, "ResolveProgramPathForDebug"},
|
|
{17, nullptr, "RedirectProgramPathForDebug"},
|
|
{18, nullptr, "RedirectApplicationProgramPathForDebug"},
|
|
{19, nullptr, "EraseProgramRedirectionForDebug"},
|
|
};
|
|
// clang-format on
|
|
|
|
RegisterHandlers(functions);
|
|
}
|
|
|
|
private:
|
|
[[maybe_unused]] FileSys::StorageId storage;
|
|
};
|
|
|
|
class IRegisteredLocationResolver final : public ServiceFramework<IRegisteredLocationResolver> {
|
|
public:
|
|
explicit IRegisteredLocationResolver(Core::System& system_)
|
|
: ServiceFramework{system_, "IRegisteredLocationResolver"} {
|
|
// clang-format off
|
|
static const FunctionInfo functions[] = {
|
|
{0, nullptr, "ResolveProgramPath"},
|
|
{1, nullptr, "RegisterProgramPath"},
|
|
{2, nullptr, "UnregisterProgramPath"},
|
|
{3, nullptr, "RedirectProgramPath"},
|
|
{4, nullptr, "ResolveHtmlDocumentPath"},
|
|
{5, nullptr, "RegisterHtmlDocumentPath"},
|
|
{6, nullptr, "UnregisterHtmlDocumentPath"},
|
|
{7, nullptr, "RedirectHtmlDocumentPath"},
|
|
{8, nullptr, "Refresh"},
|
|
{9, nullptr, "RefreshExcluding"},
|
|
};
|
|
// clang-format on
|
|
|
|
RegisterHandlers(functions);
|
|
}
|
|
};
|
|
|
|
class IAddOnContentLocationResolver final : public ServiceFramework<IAddOnContentLocationResolver> {
|
|
public:
|
|
explicit IAddOnContentLocationResolver(Core::System& system_)
|
|
: ServiceFramework{system_, "IAddOnContentLocationResolver"} {
|
|
// clang-format off
|
|
static const FunctionInfo functions[] = {
|
|
{0, nullptr, "ResolveAddOnContentPath"},
|
|
{1, nullptr, "RegisterAddOnContentStorage"},
|
|
{2, nullptr, "UnregisterAllAddOnContentPath"},
|
|
{3, nullptr, "RefreshApplicationAddOnContent"},
|
|
{4, nullptr, "UnregisterApplicationAddOnContent"},
|
|
};
|
|
// clang-format on
|
|
|
|
RegisterHandlers(functions);
|
|
}
|
|
};
|
|
|
|
class IContentStorage final : public ServiceFramework<IContentStorage> {
|
|
public:
|
|
explicit IContentStorage(Core::System& system_, FileSys::StorageId id)
|
|
: ServiceFramework{system_, "IContentStorage"}, storage{id} {
|
|
// clang-format off
|
|
static const FunctionInfo functions[] = {
|
|
{0, &IContentStorage::GeneratePlaceHolderId, "GeneratePlaceHolderId"},
|
|
{1, &IContentStorage::CreatePlaceHolder, "CreatePlaceHolder"},
|
|
{2, &IContentStorage::DeletePlaceHolder, "DeletePlaceHolder"},
|
|
{4, &IContentStorage::WritePlaceHolder, "WritePlaceHolder"},
|
|
{5, &IContentStorage::Register, "Register"},
|
|
{6, &IContentStorage::Delete, "Delete"},
|
|
};
|
|
// clang-format on
|
|
|
|
RegisterHandlers(functions);
|
|
}
|
|
|
|
private:
|
|
void GeneratePlaceHolderId(HLERequestContext& ctx) {
|
|
LOG_DEBUG(Service_NCM, "called");
|
|
|
|
IPC::ResponseBuilder rb{ctx, 6};
|
|
rb.Push(ResultSuccess);
|
|
rb.PushRaw(FileSys::PlaceholderCache::Generate());
|
|
}
|
|
|
|
void CreatePlaceHolder(HLERequestContext& ctx) {
|
|
IPC::RequestParser rp{ctx};
|
|
[[maybe_unused]] FileSys::NcaID content_id{};
|
|
FileSys::NcaID placeholder_id{};
|
|
if constexpr (HLE::ApiVersion::HOS_VERSION_MAJOR >= 16) {
|
|
placeholder_id = rp.PopRaw<FileSys::NcaID>();
|
|
content_id = rp.PopRaw<FileSys::NcaID>();
|
|
} else {
|
|
content_id = rp.PopRaw<FileSys::NcaID>();
|
|
placeholder_id = rp.PopRaw<FileSys::NcaID>();
|
|
}
|
|
const auto size = rp.Pop<s64>();
|
|
|
|
auto* const placeholder_cache =
|
|
system.GetFileSystemController().GetPlaceholderCacheForStorage(storage);
|
|
const bool succeeded =
|
|
placeholder_cache != nullptr && size >= 0 &&
|
|
(placeholder_cache->Exists(placeholder_id) ||
|
|
placeholder_cache->Create(placeholder_id, static_cast<u64>(size)));
|
|
|
|
if (succeeded) {
|
|
LOG_DEBUG(Service_NCM, "called, storage_id={}, size={}", static_cast<u32>(storage),
|
|
size);
|
|
} else {
|
|
LOG_WARNING(Service_NCM, "failed, storage_id={}, size={}", static_cast<u32>(storage),
|
|
size);
|
|
}
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
|
|
}
|
|
|
|
void DeletePlaceHolder(HLERequestContext& ctx) {
|
|
IPC::RequestParser rp{ctx};
|
|
const auto placeholder_id = rp.PopRaw<FileSys::NcaID>();
|
|
|
|
auto* const placeholder_cache =
|
|
system.GetFileSystemController().GetPlaceholderCacheForStorage(storage);
|
|
const bool succeeded =
|
|
placeholder_cache != nullptr &&
|
|
(!placeholder_cache->Exists(placeholder_id) || placeholder_cache->Delete(placeholder_id));
|
|
|
|
if (succeeded) {
|
|
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage));
|
|
} else {
|
|
LOG_WARNING(Service_NCM, "failed, storage_id={}", static_cast<u32>(storage));
|
|
}
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
|
|
}
|
|
|
|
void WritePlaceHolder(HLERequestContext& ctx) {
|
|
IPC::RequestParser rp{ctx};
|
|
const auto placeholder_id = rp.PopRaw<FileSys::NcaID>();
|
|
const auto offset = rp.Pop<u64>();
|
|
const auto data = ctx.ReadBuffer();
|
|
|
|
auto* const placeholder_cache =
|
|
system.GetFileSystemController().GetPlaceholderCacheForStorage(storage);
|
|
const std::vector<u8> write_data{data.begin(), data.end()};
|
|
const bool succeeded =
|
|
placeholder_cache != nullptr &&
|
|
placeholder_cache->Write(placeholder_id, offset, write_data);
|
|
|
|
if (succeeded) {
|
|
LOG_DEBUG(Service_NCM, "called, storage_id={}, offset={}, size={}",
|
|
static_cast<u32>(storage), offset, data.size());
|
|
} else {
|
|
LOG_WARNING(Service_NCM, "failed, storage_id={}, offset={}, size={}",
|
|
static_cast<u32>(storage), offset, data.size());
|
|
}
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
|
|
}
|
|
|
|
void Register(HLERequestContext& ctx) {
|
|
IPC::RequestParser rp{ctx};
|
|
FileSys::NcaID content_id{};
|
|
FileSys::NcaID placeholder_id{};
|
|
if constexpr (HLE::ApiVersion::HOS_VERSION_MAJOR >= 16) {
|
|
placeholder_id = rp.PopRaw<FileSys::NcaID>();
|
|
content_id = rp.PopRaw<FileSys::NcaID>();
|
|
} else {
|
|
content_id = rp.PopRaw<FileSys::NcaID>();
|
|
placeholder_id = rp.PopRaw<FileSys::NcaID>();
|
|
}
|
|
|
|
auto& fsc = system.GetFileSystemController();
|
|
auto* const placeholder_cache = fsc.GetPlaceholderCacheForStorage(storage);
|
|
auto* const registered_cache = fsc.GetRegisteredCacheForStorage(storage);
|
|
const bool succeeded =
|
|
placeholder_cache != nullptr && registered_cache != nullptr &&
|
|
placeholder_cache->Register(registered_cache, placeholder_id, content_id);
|
|
|
|
if (succeeded) {
|
|
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage));
|
|
} else {
|
|
LOG_WARNING(Service_NCM, "failed, storage_id={}", static_cast<u32>(storage));
|
|
}
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
|
|
}
|
|
|
|
void Delete(HLERequestContext& ctx) {
|
|
IPC::RequestParser rp{ctx};
|
|
const auto content_id = rp.PopRaw<FileSys::NcaID>();
|
|
|
|
auto* const registered_cache =
|
|
system.GetFileSystemController().GetRegisteredCacheForStorage(storage);
|
|
const bool succeeded = registered_cache != nullptr && registered_cache->Delete(content_id);
|
|
if (succeeded) {
|
|
registered_cache->Refresh();
|
|
}
|
|
|
|
if (succeeded) {
|
|
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage));
|
|
} else {
|
|
LOG_WARNING(Service_NCM, "failed, storage_id={}", static_cast<u32>(storage));
|
|
}
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
|
|
}
|
|
|
|
FileSys::StorageId storage;
|
|
};
|
|
|
|
class IContentMetaDatabase final : public ServiceFramework<IContentMetaDatabase> {
|
|
public:
|
|
explicit IContentMetaDatabase(Core::System& system_, FileSys::StorageId id)
|
|
: ServiceFramework{system_, "IContentMetaDatabase"}, storage{id} {
|
|
// clang-format off
|
|
static const FunctionInfo functions[] = {
|
|
{0, &IContentMetaDatabase::Set, "Set"},
|
|
{2, &IContentMetaDatabase::Remove, "Remove"},
|
|
{8, &IContentMetaDatabase::Has, "Has"},
|
|
{15, &IContentMetaDatabase::Commit, "Commit"},
|
|
};
|
|
// clang-format on
|
|
|
|
RegisterHandlers(functions);
|
|
}
|
|
|
|
private:
|
|
struct ContentMetaKey {
|
|
u64 id;
|
|
u32 version;
|
|
FileSys::TitleType type;
|
|
u8 install_type;
|
|
std::array<u8, 2> padding;
|
|
};
|
|
static_assert(sizeof(ContentMetaKey) == 0x10);
|
|
|
|
void Set(HLERequestContext& ctx) {
|
|
IPC::RequestParser rp{ctx};
|
|
const auto key = rp.PopRaw<ContentMetaKey>();
|
|
|
|
const auto entry_matches = [&key](const ContentMetaKey& entry) {
|
|
return entry.id == key.id && entry.version == key.version && entry.type == key.type &&
|
|
entry.install_type == key.install_type;
|
|
};
|
|
if (std::find_if(entries.begin(), entries.end(), entry_matches) == entries.end()) {
|
|
entries.push_back(key);
|
|
}
|
|
|
|
LOG_DEBUG(Service_NCM,
|
|
"called, storage_id={}, title_id={:016X}, version={}, type={}, size={}",
|
|
static_cast<u32>(storage), key.id, key.version, static_cast<u8>(key.type),
|
|
ctx.GetReadBufferSize());
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
rb.Push(ResultSuccess);
|
|
}
|
|
|
|
void Remove(HLERequestContext& ctx) {
|
|
IPC::RequestParser rp{ctx};
|
|
const auto key = rp.PopRaw<ContentMetaKey>();
|
|
|
|
std::erase_if(entries, [&key](const ContentMetaKey& entry) {
|
|
return entry.id == key.id && entry.version == key.version && entry.type == key.type &&
|
|
entry.install_type == key.install_type;
|
|
});
|
|
|
|
LOG_DEBUG(Service_NCM, "called, storage_id={}, title_id={:016X}, version={}, type={}",
|
|
static_cast<u32>(storage), key.id, key.version, static_cast<u8>(key.type));
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
rb.Push(ResultSuccess);
|
|
}
|
|
|
|
void Has(HLERequestContext& ctx) {
|
|
IPC::RequestParser rp{ctx};
|
|
const auto key = rp.PopRaw<ContentMetaKey>();
|
|
|
|
const bool has_pending =
|
|
std::find_if(entries.begin(), entries.end(), [&key](const ContentMetaKey& entry) {
|
|
return entry.id == key.id && entry.version == key.version &&
|
|
entry.type == key.type && entry.install_type == key.install_type;
|
|
}) != entries.end();
|
|
|
|
auto* const registered_cache =
|
|
system.GetFileSystemController().GetRegisteredCacheForStorage(storage);
|
|
const bool has_registered =
|
|
registered_cache != nullptr &&
|
|
registered_cache->HasEntry(key.id, FileSys::ContentRecordType::Meta);
|
|
|
|
LOG_DEBUG(Service_NCM, "called, storage_id={}, title_id={:016X}, version={}, type={}, has={}",
|
|
static_cast<u32>(storage), key.id, key.version, static_cast<u8>(key.type),
|
|
has_pending || has_registered);
|
|
|
|
IPC::ResponseBuilder rb{ctx, 3};
|
|
rb.Push(ResultSuccess);
|
|
rb.Push(has_pending || has_registered);
|
|
}
|
|
|
|
void Commit(HLERequestContext& ctx) {
|
|
auto* const registered_cache =
|
|
system.GetFileSystemController().GetRegisteredCacheForStorage(storage);
|
|
if (registered_cache != nullptr) {
|
|
registered_cache->Refresh();
|
|
}
|
|
|
|
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage));
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
rb.Push(ResultSuccess);
|
|
}
|
|
|
|
FileSys::StorageId storage;
|
|
std::vector<ContentMetaKey> entries;
|
|
};
|
|
|
|
class LR final : public ServiceFramework<LR> {
|
|
public:
|
|
explicit LR(Core::System& system_) : ServiceFramework{system_, "lr"} {
|
|
// clang-format off
|
|
static const FunctionInfo functions[] = {
|
|
{0, nullptr, "OpenLocationResolver"},
|
|
{1, nullptr, "OpenRegisteredLocationResolver"},
|
|
{2, nullptr, "RefreshLocationResolver"},
|
|
{3, nullptr, "OpenAddOnContentLocationResolver"},
|
|
};
|
|
// clang-format on
|
|
|
|
RegisterHandlers(functions);
|
|
}
|
|
};
|
|
|
|
class NCM final : public ServiceFramework<NCM> {
|
|
public:
|
|
explicit NCM(Core::System& system_) : ServiceFramework{system_, "ncm"} {
|
|
// clang-format off
|
|
static const FunctionInfo functions[] = {
|
|
{0, nullptr, "CreateContentStorage"},
|
|
{1, nullptr, "CreateContentMetaDatabase"},
|
|
{2, nullptr, "VerifyContentStorage"},
|
|
{3, nullptr, "VerifyContentMetaDatabase"},
|
|
{4, &NCM::OpenContentStorage, "OpenContentStorage"},
|
|
{5, &NCM::OpenContentMetaDatabase, "OpenContentMetaDatabase"},
|
|
{6, nullptr, "CloseContentStorageForcibly"},
|
|
{7, nullptr, "CloseContentMetaDatabaseForcibly"},
|
|
{8, nullptr, "CleanupContentMetaDatabase"},
|
|
{9, nullptr, "ActivateContentStorage"},
|
|
{10, nullptr, "InactivateContentStorage"},
|
|
{11, nullptr, "ActivateContentMetaDatabase"},
|
|
{12, nullptr, "InactivateContentMetaDatabase"},
|
|
{13, nullptr, "InvalidateRightsIdCache"},
|
|
{14, nullptr, "GetMemoryReport"},
|
|
{15, nullptr, "ActivateFsContentStorage"},
|
|
};
|
|
// clang-format on
|
|
|
|
RegisterHandlers(functions);
|
|
}
|
|
|
|
private:
|
|
void OpenContentStorage(HLERequestContext& ctx) {
|
|
IPC::RequestParser rp{ctx};
|
|
const auto storage_id = rp.PopEnum<FileSys::StorageId>();
|
|
|
|
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage_id));
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
|
rb.Push(ResultSuccess);
|
|
rb.PushIpcInterface<IContentStorage>(ctx, system, storage_id);
|
|
}
|
|
|
|
void OpenContentMetaDatabase(HLERequestContext& ctx) {
|
|
IPC::RequestParser rp{ctx};
|
|
const auto storage_id = rp.PopEnum<FileSys::StorageId>();
|
|
|
|
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage_id));
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
|
rb.Push(ResultSuccess);
|
|
rb.PushIpcInterface<IContentMetaDatabase>(ctx, system, storage_id);
|
|
}
|
|
};
|
|
|
|
class NCM_V final : public ServiceFramework<NCM_V> {
|
|
public:
|
|
explicit NCM_V(Core::System& system_)
|
|
: ServiceFramework{system_, "ncm:v"}
|
|
{
|
|
static const FunctionInfo functions[] = {
|
|
{0, nullptr, "GetSystemVersion"},
|
|
};
|
|
RegisterHandlers(functions);
|
|
}
|
|
};
|
|
|
|
void LoopProcess(Core::System& system) {
|
|
auto server_manager = std::make_unique<ServerManager>(system);
|
|
|
|
server_manager->RegisterNamedService("lr", std::make_shared<LR>(system));
|
|
server_manager->RegisterNamedService("ncm", std::make_shared<NCM>(system));
|
|
if (1 /* not retail */) {
|
|
server_manager->RegisterNamedService("ncm:v", std::make_shared<NCM_V>(system));
|
|
}
|
|
ServerManager::RunServer(std::move(server_manager));
|
|
}
|
|
|
|
} // namespace Service::NCM
|