mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-05 20:04:13 +00:00
[nce, fs] tico support, fix FS bug that would nuke entire 'switch' folder (#4086)
Contains the minimal set of functionalities to allow tico installer succeed, and tico work normally EXCEPT for game launching (which me or someone else will investigate later) First three commits are from PR 4012. The other six, kinda dizzy to explain each one. All were implemented based on tico source, switchbrew and libnx. Hopefully the commit messages will do. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4086 Reviewed-by: Lizzie <lizzie@eden-emu.dev> Reviewed-by: Maufeat <sahyno1996@gmail.com>
This commit is contained in:
@@ -43,6 +43,16 @@ static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
|
||||
return base->GetDirectoryRelative(dir_name);
|
||||
}
|
||||
|
||||
static std::string_view GetGuestParentPath(std::string_view path) {
|
||||
const auto name_index = path.find_last_of("\\/");
|
||||
return name_index == std::string_view::npos ? std::string_view{} : path.substr(0, name_index);
|
||||
}
|
||||
|
||||
static std::string_view GetGuestFilename(std::string_view path) {
|
||||
const auto name_index = path.find_last_of("\\/");
|
||||
return name_index == std::string_view::npos ? path : path.substr(name_index + 1);
|
||||
}
|
||||
|
||||
VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_)
|
||||
: backing(std::move(backing_)) {}
|
||||
|
||||
@@ -83,11 +93,12 @@ Result VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
|
||||
if (dir == nullptr || dir->GetFile(Common::FS::GetFilename(path)) == nullptr) {
|
||||
const auto filename = GetGuestFilename(path);
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, GetGuestParentPath(path));
|
||||
if (filename.empty() || dir == nullptr || dir->GetFile(filename) == nullptr) {
|
||||
return FileSys::ResultPathNotFound;
|
||||
}
|
||||
if (!dir->DeleteFile(Common::FS::GetFilename(path))) {
|
||||
if (!dir->DeleteFile(filename)) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultUnknown;
|
||||
}
|
||||
@@ -117,8 +128,19 @@ Result VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) con
|
||||
|
||||
Result VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
|
||||
std::string path(Common::FS::SanitizePath(path_));
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
|
||||
if (!dir->DeleteSubdirectory(Common::FS::GetFilename(path))) {
|
||||
const auto dirname = GetGuestFilename(path);
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, GetGuestParentPath(path));
|
||||
FileSys::VirtualDir target{};
|
||||
if (!dirname.empty() && dir != nullptr) {
|
||||
target = dir->GetSubdirectory(dirname);
|
||||
}
|
||||
if (target == nullptr) {
|
||||
return FileSys::ResultPathNotFound;
|
||||
}
|
||||
if (!target->GetFiles().empty() || !target->GetSubdirectories().empty()) {
|
||||
return ResultUnknown;
|
||||
}
|
||||
if (!dir->DeleteSubdirectory(dirname)) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultUnknown;
|
||||
}
|
||||
@@ -127,8 +149,12 @@ Result VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) con
|
||||
|
||||
Result VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
|
||||
std::string path(Common::FS::SanitizePath(path_));
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
|
||||
if (!dir->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path))) {
|
||||
const auto dirname = GetGuestFilename(path);
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, GetGuestParentPath(path));
|
||||
if (dirname.empty() || dir == nullptr || dir->GetSubdirectory(dirname) == nullptr) {
|
||||
return FileSys::ResultPathNotFound;
|
||||
}
|
||||
if (!dir->DeleteSubdirectoryRecursive(dirname)) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultUnknown;
|
||||
}
|
||||
@@ -137,9 +163,13 @@ Result VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string&
|
||||
|
||||
Result VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const {
|
||||
const std::string sanitized_path(Common::FS::SanitizePath(path));
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(sanitized_path));
|
||||
const auto dirname = GetGuestFilename(sanitized_path);
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, GetGuestParentPath(sanitized_path));
|
||||
|
||||
if (!dir->CleanSubdirectoryRecursive(Common::FS::GetFilename(sanitized_path))) {
|
||||
if (dirname.empty() || dir == nullptr || dir->GetSubdirectory(dirname) == nullptr) {
|
||||
return FileSys::ResultPathNotFound;
|
||||
}
|
||||
if (!dir->CleanSubdirectoryRecursive(dirname)) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultUnknown;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
// 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"
|
||||
@@ -88,6 +98,268 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
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"} {
|
||||
@@ -113,8 +385,8 @@ public:
|
||||
{1, nullptr, "CreateContentMetaDatabase"},
|
||||
{2, nullptr, "VerifyContentStorage"},
|
||||
{3, nullptr, "VerifyContentMetaDatabase"},
|
||||
{4, nullptr, "OpenContentStorage"},
|
||||
{5, nullptr, "OpenContentMetaDatabase"},
|
||||
{4, &NCM::OpenContentStorage, "OpenContentStorage"},
|
||||
{5, &NCM::OpenContentMetaDatabase, "OpenContentMetaDatabase"},
|
||||
{6, nullptr, "CloseContentStorageForcibly"},
|
||||
{7, nullptr, "CloseContentMetaDatabaseForcibly"},
|
||||
{8, nullptr, "CleanupContentMetaDatabase"},
|
||||
@@ -130,6 +402,29 @@ public:
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/ns/application_manager_interface.h"
|
||||
|
||||
#include "core/file_sys/content_archive.h"
|
||||
@@ -19,6 +20,7 @@
|
||||
#include "core/launch_timestamp_cache.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
namespace Service::NS {
|
||||
@@ -36,14 +38,14 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||
{1, nullptr, "GenerateApplicationRecordCount"},
|
||||
{2, D<&IApplicationManagerInterface::GetApplicationRecordUpdateSystemEvent>, "GetApplicationRecordUpdateSystemEvent"},
|
||||
{3, nullptr, "GetApplicationViewDeprecated"},
|
||||
{4, nullptr, "DeleteApplicationEntity"},
|
||||
{5, nullptr, "DeleteApplicationCompletely"},
|
||||
{4, D<&IApplicationManagerInterface::DeleteApplicationEntity>, "DeleteApplicationEntity"},
|
||||
{5, D<&IApplicationManagerInterface::DeleteApplicationCompletely>, "DeleteApplicationCompletely"},
|
||||
{6, nullptr, "IsAnyApplicationEntityRedundant"},
|
||||
{7, nullptr, "DeleteRedundantApplicationEntity"},
|
||||
{8, nullptr, "IsApplicationEntityMovable"},
|
||||
{9, nullptr, "MoveApplicationEntity"},
|
||||
{11, nullptr, "CalculateApplicationOccupiedSize"},
|
||||
{16, nullptr, "PushApplicationRecord"},
|
||||
{16, &IApplicationManagerInterface::PushApplicationRecord, "PushApplicationRecord"},
|
||||
{17, nullptr, "ListApplicationRecordContentMeta"},
|
||||
{19, nullptr, "LaunchApplicationOld"},
|
||||
{21, nullptr, "GetApplicationContentPath"},
|
||||
@@ -643,6 +645,27 @@ Result IApplicationManagerInterface::IsAnyApplicationEntityInstalled(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::DeleteApplicationEntity(u64 application_id) {
|
||||
LOG_DEBUG(Service_NS, "called, application_id={:016X}", application_id);
|
||||
|
||||
auto& fsc = system.GetFileSystemController();
|
||||
if (auto* const user_cache = fsc.GetUserNANDContents(); user_cache != nullptr) {
|
||||
user_cache->RemoveExistingEntry(application_id);
|
||||
user_cache->Refresh();
|
||||
}
|
||||
if (auto* const sdmc_cache = fsc.GetSDMCContents(); sdmc_cache != nullptr) {
|
||||
sdmc_cache->RemoveExistingEntry(application_id);
|
||||
sdmc_cache->Refresh();
|
||||
}
|
||||
|
||||
record_update_system_event.Signal(system.Kernel());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::DeleteApplicationCompletely(u64 application_id) {
|
||||
R_RETURN(DeleteApplicationEntity(application_id));
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::GetApplicationViewDeprecated(
|
||||
OutArray<ApplicationViewV19, BufferAttr_HipcMapAlias> out_application_views,
|
||||
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
|
||||
@@ -843,6 +866,29 @@ Result IApplicationManagerInterface::Unknown4053() {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void IApplicationManagerInterface::PushApplicationRecord(HLERequestContext& ctx) {
|
||||
const auto record = ctx.ReadBuffer();
|
||||
u64 application_id{};
|
||||
if (record.size() >= sizeof(application_id)) {
|
||||
std::memcpy(&application_id, record.data(), sizeof(application_id));
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_NS, "called, application_id={:016X}, size={}", application_id, record.size());
|
||||
|
||||
auto& fsc = system.GetFileSystemController();
|
||||
if (auto* const user_cache = fsc.GetUserNANDContents(); user_cache != nullptr) {
|
||||
user_cache->Refresh();
|
||||
}
|
||||
if (auto* const sdmc_cache = fsc.GetSDMCContents(); sdmc_cache != nullptr) {
|
||||
sdmc_cache->Refresh();
|
||||
}
|
||||
|
||||
record_update_system_event.Signal(system.Kernel());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IApplicationManagerInterface::ListApplicationTitle(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
IReadOnlyApplicationControlDataInterface(system).ListApplicationTitle(ctx);
|
||||
|
||||
@@ -56,6 +56,8 @@ public:
|
||||
Result ResumeAll();
|
||||
Result IsQualificationTransitionSupportedByProcessId(Out<bool> out_is_supported,
|
||||
u64 process_id);
|
||||
Result DeleteApplicationEntity(u64 application_id);
|
||||
Result DeleteApplicationCompletely(u64 application_id);
|
||||
Result GetStorageSize(Out<s64> out_total_space_size, Out<s64> out_free_space_size,
|
||||
FileSys::StorageId storage_id);
|
||||
Result TouchApplication(u64 application_id);
|
||||
@@ -74,6 +76,7 @@ public:
|
||||
Result RequestDownloadApplicationControlDataInBackground(u64 control_source,
|
||||
u64 application_id);
|
||||
|
||||
void PushApplicationRecord(HLERequestContext& ctx);
|
||||
void ListApplicationTitle(HLERequestContext& ctx);
|
||||
|
||||
private:
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// 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
|
||||
|
||||
@@ -28,9 +31,9 @@ SPL_MIG::SPL_MIG(Core::System& system_, std::shared_ptr<Module> module_)
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &SPL::GetConfig, "GetConfig"},
|
||||
{1, &SPL::ModularExponentiate, "ModularExponentiate"},
|
||||
{2, nullptr, "GenerateAesKek"},
|
||||
{2, &SPL::GenerateAesKek, "GenerateAesKek"},
|
||||
{3, nullptr, "LoadAesKey"},
|
||||
{4, nullptr, "GenerateAesKey"},
|
||||
{4, &SPL::GenerateAesKey, "GenerateAesKey"},
|
||||
{5, &SPL::SetConfig, "SetConfig"},
|
||||
{7, &SPL::GenerateRandomBytes, "GenerateRandomBytes"},
|
||||
{11, &SPL::IsDevelopment, "IsDevelopment"},
|
||||
|
||||
@@ -59,6 +59,36 @@ void Module::Interface::ModularExponentiate(HLERequestContext& ctx) {
|
||||
rb.Push(ResultSecureMonitorNotImplemented);
|
||||
}
|
||||
|
||||
void Module::Interface::GenerateAesKek(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
[[maybe_unused]] const auto key_source = rp.PopRaw<KeySource>();
|
||||
const auto generation = rp.Pop<u32>();
|
||||
const auto option = rp.Pop<u32>();
|
||||
|
||||
LOG_WARNING(Service_SPL, "(STUBBED) called, generation={:#x}, option={:#x}", generation,
|
||||
option);
|
||||
|
||||
AccessKey access_key{};
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(access_key);
|
||||
}
|
||||
|
||||
void Module::Interface::GenerateAesKey(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
[[maybe_unused]] const auto access_key = rp.PopRaw<AccessKey>();
|
||||
[[maybe_unused]] const auto key_source = rp.PopRaw<KeySource>();
|
||||
|
||||
LOG_WARNING(Service_SPL, "(STUBBED) called");
|
||||
|
||||
AesKey aes_key{};
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(aes_key);
|
||||
}
|
||||
|
||||
void Module::Interface::SetConfig(HLERequestContext& ctx) {
|
||||
UNIMPLEMENTED_MSG("SetConfig is not implemented!");
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// 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
|
||||
|
||||
@@ -25,6 +28,8 @@ public:
|
||||
// General
|
||||
void GetConfig(HLERequestContext& ctx);
|
||||
void ModularExponentiate(HLERequestContext& ctx);
|
||||
void GenerateAesKek(HLERequestContext& ctx);
|
||||
void GenerateAesKey(HLERequestContext& ctx);
|
||||
void SetConfig(HLERequestContext& ctx);
|
||||
void GenerateRandomBytes(HLERequestContext& ctx);
|
||||
void IsDevelopment(HLERequestContext& ctx);
|
||||
|
||||
Reference in New Issue
Block a user