early-access version 4088

This commit is contained in:
pineappleEA
2024-01-26 01:18:56 +01:00
parent 06b2c0cf0c
commit ef251c7289
160 changed files with 7677 additions and 839 deletions
+32 -6
View File
@@ -189,14 +189,14 @@ enum class ErrorModule : u32 {
union Result {
u32 raw;
BitField<0, 9, ErrorModule> module;
BitField<9, 13, u32> description;
using Module = BitField<0, 9, ErrorModule>;
using Description = BitField<9, 13, u32>;
Result() = default;
constexpr explicit Result(u32 raw_) : raw(raw_) {}
constexpr Result(ErrorModule module_, u32 description_)
: raw(module.FormatValue(module_) | description.FormatValue(description_)) {}
: raw(Module::FormatValue(module_) | Description::FormatValue(description_)) {}
[[nodiscard]] constexpr bool IsSuccess() const {
return raw == 0;
@@ -211,7 +211,15 @@ union Result {
}
[[nodiscard]] constexpr u32 GetInnerValue() const {
return static_cast<u32>(module.Value()) | (description << module.bits);
return raw;
}
[[nodiscard]] constexpr ErrorModule GetModule() const {
return Module::ExtractValue(raw);
}
[[nodiscard]] constexpr u32 GetDescription() const {
return Description::ExtractValue(raw);
}
[[nodiscard]] constexpr bool Includes(Result result) const {
@@ -274,8 +282,9 @@ public:
}
[[nodiscard]] constexpr bool Includes(Result other) const {
return code.module == other.module && code.description <= other.description &&
other.description <= description_end;
return code.GetModule() == other.GetModule() &&
code.GetDescription() <= other.GetDescription() &&
other.GetDescription() <= description_end;
}
private:
@@ -330,6 +339,16 @@ constexpr bool EvaluateResultFailure(const Result& r) {
return R_FAILED(r);
}
template <auto... R>
constexpr bool EvaluateAnyResultIncludes(const Result& r) {
return ((r == R) || ...);
}
template <auto... R>
constexpr bool EvaluateResultNotIncluded(const Result& r) {
return !EvaluateAnyResultIncludes<R...>(r);
}
template <typename T>
constexpr void UpdateCurrentResultReference(T result_reference, Result result) = delete;
// Intentionally not defined
@@ -371,6 +390,13 @@ constexpr void UpdateCurrentResultReference<const Result>(Result result_referenc
DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__); \
ON_RESULT_SUCCESS_2
#define ON_RESULT_INCLUDED_2(...) \
ON_RESULT_RETURN_IMPL(ResultImpl::EvaluateAnyResultIncludes<__VA_ARGS__>)
#define ON_RESULT_INCLUDED(...) \
DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__); \
ON_RESULT_INCLUDED_2(__VA_ARGS__)
constexpr inline Result __TmpCurrentResultReference = ResultSuccess;
/// Returns a result.
@@ -27,8 +27,8 @@ struct ErrorCode {
static constexpr ErrorCode FromResult(Result result) {
return {
.error_category{2000 + static_cast<u32>(result.module.Value())},
.error_number{result.description.Value()},
.error_category{2000 + static_cast<u32>(result.GetModule())},
.error_number{result.GetDescription()},
};
}
@@ -9,13 +9,13 @@
#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/system_archive/system_archive.h"
#include "core/file_sys/vfs_vector.h"
#include "core/file_sys/vfs/vfs_vector.h"
#include "core/frontend/applets/web_browser.h"
#include "core/hle/result.h"
#include "core/hle/service/am/am.h"
@@ -213,7 +213,7 @@ void ExtractSharedFonts(Core::System& system) {
std::move(decrypted_data), DECRYPTED_SHARED_FONTS[i]);
const auto temp_dir = system.GetFilesystem()->CreateDirectory(
Common::FS::PathToUTF8String(fonts_dir), FileSys::Mode::ReadWrite);
Common::FS::PathToUTF8String(fonts_dir), FileSys::OpenMode::ReadWrite);
const auto out_file = temp_dir->CreateFile(DECRYPTED_SHARED_FONTS[i]);
@@ -333,7 +333,7 @@ void WebBrowser::ExtractOfflineRomFS() {
const auto extracted_romfs_dir = FileSys::ExtractRomFS(offline_romfs);
const auto temp_dir = system.GetFilesystem()->CreateDirectory(
Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite);
Common::FS::PathToUTF8String(offline_cache_dir), FileSys::OpenMode::ReadWrite);
FileSys::VfsRawCopyD(extracted_romfs_dir, temp_dir);
}
@@ -7,7 +7,7 @@
#include <optional>
#include "common/common_types.h"
#include "core/file_sys/vfs_types.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/result.h"
#include "core/hle/service/am/applets/applet_web_browser_types.h"
#include "core/hle/service/am/applets/applets.h"
+2 -1
View File
@@ -139,7 +139,8 @@ private:
ctx.WriteBufferC(performance_buffer.data(), performance_buffer.size(), 1);
}
} else {
LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description);
LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!",
result.GetDescription());
}
IPC::ResponseBuilder rb{ctx, 2};
+1 -1
View File
@@ -8,7 +8,7 @@
#include <string>
#include "common/common_types.h"
#include "core/file_sys/vfs_types.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/result.h"
#include "core/hle/service/kernel_helpers.h"
+1 -1
View File
@@ -8,7 +8,7 @@
#include "common/settings.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/bcat/backend/backend.h"
#include "core/hle/service/bcat/bcat.h"
+7 -6
View File
@@ -202,14 +202,14 @@ Result IAlbumAccessorService::TranslateResult(Result in_result) {
}
if ((in_result.raw & 0x3801ff) == ResultUnknown1024.raw) {
if (in_result.description - 0x514 < 100) {
if (in_result.GetDescription() - 0x514 < 100) {
return ResultInvalidFileData;
}
if (in_result.description - 0x5dc < 100) {
if (in_result.GetDescription() - 0x5dc < 100) {
return ResultInvalidFileData;
}
if (in_result.description - 0x578 < 100) {
if (in_result.GetDescription() - 0x578 < 100) {
if (in_result == ResultFileCountLimit) {
return ResultUnknown22;
}
@@ -244,9 +244,10 @@ Result IAlbumAccessorService::TranslateResult(Result in_result) {
return ResultUnknown1024;
}
if (in_result.module == ErrorModule::FS) {
if ((in_result.description >> 0xc < 0x7d) || (in_result.description - 1000 < 2000) ||
(((in_result.description - 3000) >> 3) < 0x271)) {
if (in_result.GetModule() == ErrorModule::FS) {
if ((in_result.GetDescription() >> 0xc < 0x7d) ||
(in_result.GetDescription() - 1000 < 2000) ||
(((in_result.GetDescription() - 3000) >> 3) < 0x271)) {
// TODO: Translate FS error
return in_result;
}
+337
View File
@@ -0,0 +1,337 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/div_ceil.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/service.h"
namespace Service {
// clang-format off
struct RequestLayout {
u32 copy_handle_count;
u32 move_handle_count;
u32 cmif_raw_data_size;
u32 domain_interface_count;
};
template <ArgumentType Type1, ArgumentType Type2, typename MethodArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t ArgIndex = 0>
constexpr u32 GetArgumentRawDataSize() {
if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) {
return static_cast<u32>(DataOffset);
} else {
using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
if constexpr (ArgumentTraits<ArgType>::Type == Type1 || ArgumentTraits<ArgType>::Type == Type2) {
constexpr size_t ArgAlign = alignof(ArgType);
constexpr size_t ArgSize = sizeof(ArgType);
static_assert(PrevAlign <= ArgAlign, "Input argument is not ordered by alignment");
constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
constexpr size_t ArgEnd = ArgOffset + ArgSize;
return GetArgumentRawDataSize<Type1, Type2, MethodArguments, ArgAlign, ArgEnd, ArgIndex + 1>();
} else {
return GetArgumentRawDataSize<Type1, Type2, MethodArguments, PrevAlign, DataOffset, ArgIndex + 1>();
}
}
}
template <ArgumentType DataType, typename MethodArguments, size_t ArgCount = 0, size_t ArgIndex = 0>
constexpr u32 GetArgumentTypeCount() {
if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) {
return static_cast<u32>(ArgCount);
} else {
using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
if constexpr (ArgumentTraits<ArgType>::Type == DataType) {
return GetArgumentTypeCount<DataType, MethodArguments, ArgCount + 1, ArgIndex + 1>();
} else {
return GetArgumentTypeCount<DataType, MethodArguments, ArgCount, ArgIndex + 1>();
}
}
}
template <typename MethodArguments>
constexpr RequestLayout GetNonDomainReplyInLayout() {
return RequestLayout{
.copy_handle_count = GetArgumentTypeCount<ArgumentType::InCopyHandle, MethodArguments>(),
.move_handle_count = 0,
.cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::InData, ArgumentType::InProcessId, MethodArguments>(),
.domain_interface_count = 0,
};
}
template <typename MethodArguments>
constexpr RequestLayout GetDomainReplyInLayout() {
return RequestLayout{
.copy_handle_count = GetArgumentTypeCount<ArgumentType::InCopyHandle, MethodArguments>(),
.move_handle_count = 0,
.cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::InData, ArgumentType::InProcessId, MethodArguments>(),
.domain_interface_count = GetArgumentTypeCount<ArgumentType::InInterface, MethodArguments>(),
};
}
template <typename MethodArguments>
constexpr RequestLayout GetNonDomainReplyOutLayout() {
return RequestLayout{
.copy_handle_count = GetArgumentTypeCount<ArgumentType::OutCopyHandle, MethodArguments>(),
.move_handle_count = GetArgumentTypeCount<ArgumentType::OutMoveHandle, MethodArguments>() + GetArgumentTypeCount<ArgumentType::OutInterface, MethodArguments>(),
.cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::OutData, ArgumentType::OutData, MethodArguments>(),
.domain_interface_count = 0,
};
}
template <typename MethodArguments>
constexpr RequestLayout GetDomainReplyOutLayout() {
return RequestLayout{
.copy_handle_count = GetArgumentTypeCount<ArgumentType::OutCopyHandle, MethodArguments>(),
.move_handle_count = GetArgumentTypeCount<ArgumentType::OutMoveHandle, MethodArguments>(),
.cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::OutData, ArgumentType::OutData, MethodArguments>(),
.domain_interface_count = GetArgumentTypeCount<ArgumentType::OutInterface, MethodArguments>(),
};
}
template <bool Domain, typename MethodArguments>
constexpr RequestLayout GetReplyInLayout() {
return Domain ? GetDomainReplyInLayout<MethodArguments>() : GetNonDomainReplyInLayout<MethodArguments>();
}
template <bool Domain, typename MethodArguments>
constexpr RequestLayout GetReplyOutLayout() {
return Domain ? GetDomainReplyOutLayout<MethodArguments>() : GetNonDomainReplyOutLayout<MethodArguments>();
}
using OutTemporaryBuffers = std::array<Common::ScratchBuffer<u8>, 3>;
template <bool Domain, typename MethodArguments, typename CallArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t HandleIndex = 0, size_t InBufferIndex = 0, size_t OutBufferIndex = 0, bool RawDataFinished = false, size_t ArgIndex = 0>
void ReadInArgument(CallArguments& args, const u8* raw_data, HLERequestContext& ctx, OutTemporaryBuffers& temp) {
if constexpr (ArgIndex >= std::tuple_size_v<CallArguments>) {
return;
} else {
using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InData || ArgumentTraits<ArgType>::Type == ArgumentType::InProcessId) {
constexpr size_t ArgAlign = alignof(ArgType);
constexpr size_t ArgSize = sizeof(ArgType);
static_assert(PrevAlign <= ArgAlign, "Input argument is not ordered by alignment");
static_assert(!RawDataFinished, "All input interface arguments must appear after raw data");
constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
constexpr size_t ArgEnd = ArgOffset + ArgSize;
if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InProcessId) {
// TODO: abort parsing if PID is not provided?
// TODO: validate against raw data value?
std::get<ArgIndex>(args).pid = ctx.GetPID();
} else {
std::memcpy(&std::get<ArgIndex>(args), raw_data + ArgOffset, ArgSize);
}
return ReadInArgument<Domain, MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, false, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InInterface) {
constexpr size_t ArgAlign = alignof(u32);
constexpr size_t ArgSize = sizeof(u32);
constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
constexpr size_t ArgEnd = ArgOffset + ArgSize;
static_assert(Domain);
ASSERT(ctx.GetDomainMessageHeader().input_object_count > 0);
u32 value{};
std::memcpy(&value, raw_data + ArgOffset, ArgSize);
std::get<ArgIndex>(args) = ctx.GetDomainHandler<ArgType::Type>(value - 1);
return ReadInArgument<Domain, MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, true, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InCopyHandle) {
std::get<ArgIndex>(args) = std::move(ctx.GetObjectFromHandle<typename ArgType::Type>(ctx.GetCopyHandle(HandleIndex)));
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex + 1, InBufferIndex, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InLargeData) {
constexpr size_t BufferSize = sizeof(ArgType);
// Clear the existing data.
std::memset(&std::get<ArgIndex>(args), 0, BufferSize);
std::span<const u8> buffer{};
ASSERT(ctx.CanReadBuffer(InBufferIndex));
if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
buffer = ctx.ReadBuffer(InBufferIndex);
} else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
buffer = ctx.ReadBufferA(InBufferIndex);
} else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
buffer = ctx.ReadBufferX(InBufferIndex);
}
std::memcpy(&std::get<ArgIndex>(args), buffer.data(), std::min(BufferSize, buffer.size()));
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex + 1, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InBuffer) {
using ElementType = typename ArgType::Type;
std::span<const u8> buffer{};
if (ctx.CanReadBuffer(InBufferIndex)) {
if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
buffer = ctx.ReadBuffer(InBufferIndex);
} else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
buffer = ctx.ReadBufferA(InBufferIndex);
} else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
buffer = ctx.ReadBufferX(InBufferIndex);
}
}
ElementType* ptr = (ElementType*) buffer.data();
size_t size = buffer.size() / sizeof(ElementType);
std::get<ArgIndex>(args) = std::span(ptr, size);
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex + 1, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutLargeData) {
constexpr size_t BufferSize = sizeof(ArgType);
// Clear the existing data.
std::memset(&std::get<ArgIndex>(args), 0, BufferSize);
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutBuffer) {
using ElementType = typename ArgType::Type;
// Set up scratch buffer.
auto& buffer = temp[OutBufferIndex];
if (ctx.CanWriteBuffer(OutBufferIndex)) {
buffer.resize_destructive(ctx.GetWriteBufferSize(OutBufferIndex));
} else {
buffer.resize_destructive(0);
}
ElementType* ptr = (ElementType*) buffer.data();
size_t size = buffer.size() / sizeof(ElementType);
std::get<ArgIndex>(args) = std::span(ptr, size);
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else {
return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
}
}
}
template <bool Domain, typename MethodArguments, typename CallArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t OutBufferIndex = 0, bool RawDataFinished = false, size_t ArgIndex = 0>
void WriteOutArgument(CallArguments& args, u8* raw_data, HLERequestContext& ctx, OutTemporaryBuffers& temp) {
if constexpr (ArgIndex >= std::tuple_size_v<CallArguments>) {
return;
} else {
using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;
if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutData) {
constexpr size_t ArgAlign = alignof(ArgType);
constexpr size_t ArgSize = sizeof(ArgType);
static_assert(PrevAlign <= ArgAlign, "Output argument is not ordered by alignment");
static_assert(!RawDataFinished, "All output interface arguments must appear after raw data");
constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
constexpr size_t ArgEnd = ArgOffset + ArgSize;
std::memcpy(raw_data + ArgOffset, &std::get<ArgIndex>(args), ArgSize);
return WriteOutArgument<Domain, MethodArguments, CallArguments, ArgAlign, ArgEnd, OutBufferIndex, false, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutInterface) {
if constexpr (Domain) {
ctx.AddDomainObject(std::get<ArgIndex>(args));
} else {
ctx.AddMoveInterface(std::get<ArgIndex>(args));
}
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, true, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutCopyHandle) {
ctx.AddCopyObject(std::get<ArgIndex>(args).GetPointerUnsafe());
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutMoveHandle) {
ctx.AddMoveObject(std::get<ArgIndex>(args).GetPointerUnsafe());
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutLargeData) {
constexpr size_t BufferSize = sizeof(ArgType);
ASSERT(ctx.CanWriteBuffer(OutBufferIndex));
if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
ctx.WriteBuffer(std::get<ArgIndex>(args), OutBufferIndex);
} else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
ctx.WriteBufferB(&std::get<ArgIndex>(args), BufferSize, OutBufferIndex);
} else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
ctx.WriteBufferC(&std::get<ArgIndex>(args), BufferSize, OutBufferIndex);
}
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
} else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutBuffer) {
auto& buffer = temp[OutBufferIndex];
const size_t size = buffer.size();
if (ctx.CanWriteBuffer(OutBufferIndex)) {
if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
ctx.WriteBuffer(buffer.data(), size, OutBufferIndex);
} else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
ctx.WriteBufferB(buffer.data(), size, OutBufferIndex);
} else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
ctx.WriteBufferC(buffer.data(), size, OutBufferIndex);
}
}
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>( args, raw_data, ctx, temp);
} else {
return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp);
}
}
}
template <bool Domain, typename T, typename... A>
void CmifReplyWrapImpl(HLERequestContext& ctx, T& t, Result (T::*f)(A...)) {
// Verify domain state.
if constexpr (Domain) {
ASSERT_MSG(ctx.GetManager()->IsDomain(), "Domain reply used on non-domain session");
} else {
ASSERT_MSG(!ctx.GetManager()->IsDomain(), "Non-domain reply used on domain session");
}
using MethodArguments = std::tuple<std::remove_reference_t<A>...>;
OutTemporaryBuffers buffers{};
auto call_arguments = std::tuple<typename RemoveOut<A>::Type...>();
// Read inputs.
const size_t offset_plus_command_id = ctx.GetDataPayloadOffset() + 2;
ReadInArgument<Domain, MethodArguments>(call_arguments, reinterpret_cast<u8*>(ctx.CommandBuffer() + offset_plus_command_id), ctx, buffers);
// Call.
const auto Callable = [&]<typename... CallArgs>(CallArgs&... args) {
return (t.*f)(args...);
};
const Result res = std::apply(Callable, call_arguments);
// Write result.
constexpr RequestLayout layout = GetReplyOutLayout<Domain, MethodArguments>();
IPC::ResponseBuilder rb{ctx, 2 + Common::DivCeil(layout.cmif_raw_data_size, sizeof(u32)), layout.copy_handle_count, layout.move_handle_count + layout.domain_interface_count};
rb.Push(res);
// Write out arguments.
WriteOutArgument<Domain, MethodArguments>(call_arguments, reinterpret_cast<u8*>(ctx.CommandBuffer() + rb.GetCurrentOffset()), ctx, buffers);
}
// clang-format on
template <typename Self>
template <bool Domain, auto F>
inline void ServiceFramework<Self>::CmifReplyWrap(HLERequestContext& ctx) {
return CmifReplyWrapImpl<Domain>(ctx, *static_cast<Self*>(this), F);
}
} // namespace Service
+234
View File
@@ -0,0 +1,234 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/service/hle_ipc.h"
namespace Service {
// clang-format off
template <typename T>
class Out {
public:
/* implicit */ Out(T& t) : raw(&t) {}
~Out() = default;
T* Get() const {
return raw;
}
T& operator*() {
return *raw;
}
private:
T* raw;
};
template <typename T>
using SharedPointer = std::shared_ptr<T>;
struct ClientProcessId {
explicit operator bool() const {
return pid != 0;
}
const u64& operator*() const {
return pid;
}
u64 pid;
};
using ClientAppletResourceUserId = ClientProcessId;
template <typename T>
class InCopyHandle : public Kernel::KScopedAutoObject<T> {
public:
using Type = T;
template <typename... Args>
/* implicit */ InCopyHandle(Args&&... args) : Kernel::KScopedAutoObject<T>(std::forward<Args...>(args)...) {}
~InCopyHandle() = default;
InCopyHandle& operator=(InCopyHandle&& rhs) {
Kernel::KScopedAutoObject<T>::operator=(std::move(rhs));
return *this;
}
};
template <typename T>
class OutCopyHandle : public Kernel::KScopedAutoObject<T> {
public:
using Type = T;
template <typename... Args>
/* implicit */ OutCopyHandle(Args&&... args) : Kernel::KScopedAutoObject<T>(std::forward<Args...>(args)...) {}
~OutCopyHandle() = default;
OutCopyHandle& operator=(OutCopyHandle&& rhs) {
Kernel::KScopedAutoObject<T>::operator=(std::move(rhs));
return *this;
}
};
template <typename T>
class OutMoveHandle : public Kernel::KScopedAutoObject<T> {
public:
using Type = T;
template <typename... Args>
/* implicit */ OutMoveHandle(Args&&... args) : Kernel::KScopedAutoObject<T>(std::forward<Args...>(args)...) {}
~OutMoveHandle() = default;
OutMoveHandle& operator=(OutMoveHandle&& rhs) {
Kernel::KScopedAutoObject<T>::operator=(std::move(rhs));
return *this;
}
};
enum BufferAttr : int {
BufferAttr_In = (1U << 0),
BufferAttr_Out = (1U << 1),
BufferAttr_HipcMapAlias = (1U << 2),
BufferAttr_HipcPointer = (1U << 3),
BufferAttr_FixedSize = (1U << 4),
BufferAttr_HipcAutoSelect = (1U << 5),
BufferAttr_HipcMapTransferAllowsNonSecure = (1U << 6),
BufferAttr_HipcMapTransferAllowsNonDevice = (1U << 7),
};
template <typename T, int A>
struct Buffer : public std::span<T> {
static_assert(std::is_trivial_v<T>, "Buffer type must be trivial");
static_assert((A & BufferAttr_FixedSize) == 0, "Buffer attr must not contain FixedSize");
static_assert(((A & BufferAttr_In) == 0) ^ ((A & BufferAttr_Out) == 0), "Buffer attr must be In or Out");
static constexpr BufferAttr Attr = static_cast<BufferAttr>(A);
using Type = T;
Buffer& operator=(const std::span<T>& rhs) {
std::span<T>::operator=(rhs);
return *this;
}
T& operator*() const {
return *this->data();
}
explicit operator bool() const {
return this->size() > 0;
}
};
template <BufferAttr A>
using InBuffer = Buffer<const u8, BufferAttr_In | A>;
template <typename T, BufferAttr A>
using InArray = Buffer<T, BufferAttr_In | A>;
template <BufferAttr A>
using OutBuffer = Buffer<u8, BufferAttr_Out | A>;
template <typename T, BufferAttr A>
using OutArray = Buffer<T, BufferAttr_Out | A>;
template <typename T, int A>
struct LargeData : public T {
static_assert(std::is_trivial_v<T>, "LargeData type must be trivial");
static_assert((A & BufferAttr_FixedSize) != 0, "LargeData attr must contain FixedSize");
static_assert(((A & BufferAttr_In) == 0) ^ ((A & BufferAttr_Out) == 0), "LargeData attr must be In or Out");
static constexpr BufferAttr Attr = static_cast<BufferAttr>(A);
using Type = T;
};
template <typename T, BufferAttr A>
using InLargeData = LargeData<T, BufferAttr_FixedSize | BufferAttr_In | A>;
template <typename T, BufferAttr A>
using OutLargeData = LargeData<T, BufferAttr_FixedSize | BufferAttr_Out | A>;
template <typename T>
struct RemoveOut {
using Type = std::remove_reference_t<T>;
};
template <typename T>
struct RemoveOut<Out<T>> {
using Type = T;
};
enum class ArgumentType {
InProcessId,
InData,
InInterface,
InCopyHandle,
OutData,
OutInterface,
OutCopyHandle,
OutMoveHandle,
InBuffer,
InLargeData,
OutBuffer,
OutLargeData,
};
template <typename T>
struct ArgumentTraits;
template <>
struct ArgumentTraits<ClientProcessId> {
static constexpr ArgumentType Type = ArgumentType::InProcessId;
};
template <typename T>
struct ArgumentTraits<SharedPointer<T>> {
static constexpr ArgumentType Type = ArgumentType::InInterface;
};
template <typename T>
struct ArgumentTraits<InCopyHandle<T>> {
static constexpr ArgumentType Type = ArgumentType::InCopyHandle;
};
template <typename T>
struct ArgumentTraits<Out<SharedPointer<T>>> {
static constexpr ArgumentType Type = ArgumentType::OutInterface;
};
template <typename T>
struct ArgumentTraits<Out<T>> {
static constexpr ArgumentType Type = ArgumentType::OutData;
};
template <typename T>
struct ArgumentTraits<OutCopyHandle<T>> {
static constexpr ArgumentType Type = ArgumentType::OutCopyHandle;
};
template <typename T>
struct ArgumentTraits<OutMoveHandle<T>> {
static constexpr ArgumentType Type = ArgumentType::OutMoveHandle;
};
template <typename T, int A>
struct ArgumentTraits<Buffer<T, A>> {
static constexpr ArgumentType Type = (A & BufferAttr_In) == 0 ? ArgumentType::OutBuffer : ArgumentType::InBuffer;
};
template <typename T, int A>
struct ArgumentTraits<LargeData<T, A>> {
static constexpr ArgumentType Type = (A & BufferAttr_In) == 0 ? ArgumentType::OutLargeData : ArgumentType::InLargeData;
};
template <typename T>
struct ArgumentTraits {
static constexpr ArgumentType Type = ArgumentType::InData;
};
// clang-format on
} // namespace Service
+2 -2
View File
@@ -73,8 +73,8 @@ static void GenerateErrorReport(Core::System& system, Result error_code, const F
"Program entry point: 0x{:16X}\n"
"\n",
Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw,
2000 + static_cast<u32>(error_code.module.Value()),
static_cast<u32>(error_code.description.Value()), info.set_flags, info.program_entry_point);
2000 + static_cast<u32>(error_code.GetModule()),
static_cast<u32>(error_code.GetDescription()), info.set_flags, info.program_entry_point);
if (info.backtrace_size != 0x0) {
crash_report += "Registers:\n";
for (size_t i = 0; i < info.registers.size(); i++) {
+39 -39
View File
@@ -12,18 +12,17 @@
#include "core/file_sys/card_image.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/sdmc_factory.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_offset.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/file_sys/vfs/vfs_offset.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/fsp_ldr.h"
#include "core/hle/service/filesystem/fsp_pr.h"
#include "core/hle/service/filesystem/fsp_srv.h"
#include "core/hle/service/filesystem/fsp/fsp_ldr.h"
#include "core/hle/service/filesystem/fsp/fsp_pr.h"
#include "core/hle/service/filesystem/fsp/fsp_srv.h"
#include "core/hle/service/filesystem/romfs_controller.h"
#include "core/hle/service/filesystem/save_data_controller.h"
#include "core/hle/service/server_manager.h"
@@ -53,12 +52,12 @@ Result VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size
std::string path(Common::FS::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (dir == nullptr) {
return FileSys::ERROR_PATH_NOT_FOUND;
return FileSys::ResultPathNotFound;
}
FileSys::EntryType entry_type{};
FileSys::DirectoryEntryType entry_type{};
if (GetEntryType(&entry_type, path) == ResultSuccess) {
return FileSys::ERROR_PATH_ALREADY_EXISTS;
return FileSys::ResultPathAlreadyExists;
}
auto file = dir->CreateFile(Common::FS::GetFilename(path));
@@ -82,7 +81,7 @@ Result VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (dir == nullptr || dir->GetFile(Common::FS::GetFilename(path)) == nullptr) {
return FileSys::ERROR_PATH_NOT_FOUND;
return FileSys::ResultPathNotFound;
}
if (!dir->DeleteFile(Common::FS::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
@@ -153,12 +152,12 @@ Result VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
// Use more-optimized vfs implementation rename.
if (src == nullptr) {
return FileSys::ERROR_PATH_NOT_FOUND;
return FileSys::ResultPathNotFound;
}
if (dst && Common::FS::Exists(dst->GetFullPath())) {
LOG_ERROR(Service_FS, "File at new_path={} already exists", dst->GetFullPath());
return FileSys::ERROR_PATH_ALREADY_EXISTS;
return FileSys::ResultPathAlreadyExists;
}
if (!src->Rename(Common::FS::GetFilename(dest_path))) {
@@ -195,7 +194,7 @@ Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
// Use more-optimized vfs implementation rename.
if (src == nullptr)
return FileSys::ERROR_PATH_NOT_FOUND;
return FileSys::ResultPathNotFound;
if (!src->Rename(Common::FS::GetFilename(dest_path))) {
// TODO(DarkLordZach): Find a better error code for this
return ResultUnknown;
@@ -214,7 +213,8 @@ Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
}
Result VfsDirectoryServiceWrapper::OpenFile(FileSys::VirtualFile* out_file,
const std::string& path_, FileSys::Mode mode) const {
const std::string& path_,
FileSys::OpenMode mode) const {
const std::string path(Common::FS::SanitizePath(path_));
std::string_view npath = path;
while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) {
@@ -223,10 +223,10 @@ Result VfsDirectoryServiceWrapper::OpenFile(FileSys::VirtualFile* out_file,
auto file = backing->GetFileRelative(npath);
if (file == nullptr) {
return FileSys::ERROR_PATH_NOT_FOUND;
return FileSys::ResultPathNotFound;
}
if (mode == FileSys::Mode::Append) {
if (mode == FileSys::OpenMode::AllowAppend) {
*out_file = std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize());
} else {
*out_file = file;
@@ -241,50 +241,50 @@ Result VfsDirectoryServiceWrapper::OpenDirectory(FileSys::VirtualDir* out_direct
auto dir = GetDirectoryRelativeWrapped(backing, path);
if (dir == nullptr) {
// TODO(DarkLordZach): Find a better error code for this
return FileSys::ERROR_PATH_NOT_FOUND;
return FileSys::ResultPathNotFound;
}
*out_directory = dir;
return ResultSuccess;
}
Result VfsDirectoryServiceWrapper::GetEntryType(FileSys::EntryType* out_entry_type,
Result VfsDirectoryServiceWrapper::GetEntryType(FileSys::DirectoryEntryType* out_entry_type,
const std::string& path_) const {
std::string path(Common::FS::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (dir == nullptr) {
return FileSys::ERROR_PATH_NOT_FOUND;
return FileSys::ResultPathNotFound;
}
auto filename = Common::FS::GetFilename(path);
// TODO(Subv): Some games use the '/' path, find out what this means.
if (filename.empty()) {
*out_entry_type = FileSys::EntryType::Directory;
*out_entry_type = FileSys::DirectoryEntryType::Directory;
return ResultSuccess;
}
if (dir->GetFile(filename) != nullptr) {
*out_entry_type = FileSys::EntryType::File;
*out_entry_type = FileSys::DirectoryEntryType::File;
return ResultSuccess;
}
if (dir->GetSubdirectory(filename) != nullptr) {
*out_entry_type = FileSys::EntryType::Directory;
*out_entry_type = FileSys::DirectoryEntryType::Directory;
return ResultSuccess;
}
return FileSys::ERROR_PATH_NOT_FOUND;
return FileSys::ResultPathNotFound;
}
Result VfsDirectoryServiceWrapper::GetFileTimeStampRaw(
FileSys::FileTimeStampRaw* out_file_time_stamp_raw, const std::string& path) const {
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (dir == nullptr) {
return FileSys::ERROR_PATH_NOT_FOUND;
return FileSys::ResultPathNotFound;
}
FileSys::EntryType entry_type;
FileSys::DirectoryEntryType entry_type;
if (GetEntryType(&entry_type, path) != ResultSuccess) {
return FileSys::ERROR_PATH_NOT_FOUND;
return FileSys::ResultPathNotFound;
}
*out_file_time_stamp_raw = dir->GetFileTimeStamp(Common::FS::GetFilename(path));
@@ -317,7 +317,7 @@ Result FileSystemController::OpenProcess(
const auto it = registrations.find(process_id);
if (it == registrations.end()) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
return FileSys::ResultTargetNotFound;
}
*out_program_id = it->second.program_id;
@@ -347,7 +347,7 @@ std::shared_ptr<SaveDataController> FileSystemController::OpenSaveDataController
std::shared_ptr<FileSys::SaveDataFactory> FileSystemController::CreateSaveDataFactory(
ProgramId program_id) {
using YuzuPath = Common::FS::YuzuPath;
const auto rw_mode = FileSys::Mode::ReadWrite;
const auto rw_mode = FileSys::OpenMode::ReadWrite;
auto vfs = system.GetFilesystem();
const auto nand_directory =
@@ -360,12 +360,12 @@ Result FileSystemController::OpenSDMC(FileSys::VirtualDir* out_sdmc) const {
LOG_TRACE(Service_FS, "Opening SDMC");
if (sdmc_factory == nullptr) {
return FileSys::ERROR_SD_CARD_NOT_FOUND;
return FileSys::ResultPortSdCardNoDevice;
}
auto sdmc = sdmc_factory->Open();
if (sdmc == nullptr) {
return FileSys::ERROR_SD_CARD_NOT_FOUND;
return FileSys::ResultPortSdCardNoDevice;
}
*out_sdmc = sdmc;
@@ -377,12 +377,12 @@ Result FileSystemController::OpenBISPartition(FileSys::VirtualDir* out_bis_parti
LOG_TRACE(Service_FS, "Opening BIS Partition with id={:08X}", id);
if (bis_factory == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
return FileSys::ResultTargetNotFound;
}
auto part = bis_factory->OpenPartition(id);
if (part == nullptr) {
return FileSys::ERROR_INVALID_ARGUMENT;
return FileSys::ResultInvalidArgument;
}
*out_bis_partition = part;
@@ -394,12 +394,12 @@ Result FileSystemController::OpenBISPartitionStorage(
LOG_TRACE(Service_FS, "Opening BIS Partition Storage with id={:08X}", id);
if (bis_factory == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
return FileSys::ResultTargetNotFound;
}
auto part = bis_factory->OpenPartitionStorage(id, system.GetFilesystem());
if (part == nullptr) {
return FileSys::ERROR_INVALID_ARGUMENT;
return FileSys::ResultInvalidArgument;
}
*out_bis_partition_storage = part;
@@ -686,15 +686,15 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
using YuzuPath = Common::FS::YuzuPath;
const auto sdmc_dir_path = Common::FS::GetYuzuPath(YuzuPath::SDMCDir);
const auto sdmc_load_dir_path = sdmc_dir_path / "atmosphere/contents";
const auto rw_mode = FileSys::Mode::ReadWrite;
const auto rw_mode = FileSys::OpenMode::ReadWrite;
auto nand_directory =
vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::NANDDir), rw_mode);
auto sd_directory = vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_dir_path), rw_mode);
auto load_directory =
vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::LoadDir), FileSys::Mode::Read);
auto sd_load_directory =
vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_load_dir_path), FileSys::Mode::Read);
auto load_directory = vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::LoadDir),
FileSys::OpenMode::Read);
auto sd_load_directory = vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_load_dir_path),
FileSys::OpenMode::Read);
auto dump_directory =
vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::DumpDir), rw_mode);
+6 -12
View File
@@ -4,9 +4,11 @@
#pragma once
#include <memory>
#include <mutex>
#include "common/common_types.h"
#include "core/file_sys/directory.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/fs_directory.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/result.h"
namespace Core {
@@ -26,7 +28,6 @@ class XCI;
enum class BisPartitionId : u32;
enum class ContentRecordType : u8;
enum class Mode : u32;
enum class SaveDataSpaceId : u8;
enum class SaveDataType : u8;
enum class StorageId : u8;
@@ -57,13 +58,6 @@ enum class ImageDirectoryId : u32 {
SdCard,
};
enum class OpenDirectoryMode : u64 {
Directory = (1 << 0),
File = (1 << 1),
All = Directory | File
};
DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode);
using ProcessId = u64;
using ProgramId = u64;
@@ -237,7 +231,7 @@ public:
* @return Opened file, or error code
*/
Result OpenFile(FileSys::VirtualFile* out_file, const std::string& path,
FileSys::Mode mode) const;
FileSys::OpenMode mode) const;
/**
* Open a directory specified by its path
@@ -250,7 +244,7 @@ public:
* Get the type of the specified path
* @return The type of the specified path or error code
*/
Result GetEntryType(FileSys::EntryType* out_entry_type, const std::string& path) const;
Result GetEntryType(FileSys::DirectoryEntryType* out_entry_type, const std::string& path) const;
/**
* Get the timestamp of the specified path
+84
View File
@@ -0,0 +1,84 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/savedata_factory.h"
#include "core/hle/service/filesystem/fsp/fs_i_directory.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::FileSystem {
template <typename T>
static void BuildEntryIndex(std::vector<FileSys::DirectoryEntry>& entries,
const std::vector<T>& new_data, FileSys::DirectoryEntryType type) {
entries.reserve(entries.size() + new_data.size());
for (const auto& new_entry : new_data) {
auto name = new_entry->GetName();
if (type == FileSys::DirectoryEntryType::File &&
name == FileSys::GetSaveDataSizeFileName()) {
continue;
}
entries.emplace_back(name, static_cast<s8>(type),
type == FileSys::DirectoryEntryType::Directory ? 0
: new_entry->GetSize());
}
}
IDirectory::IDirectory(Core::System& system_, FileSys::VirtualDir backend_,
FileSys::OpenDirectoryMode mode)
: ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) {
static const FunctionInfo functions[] = {
{0, &IDirectory::Read, "Read"},
{1, &IDirectory::GetEntryCount, "GetEntryCount"},
};
RegisterHandlers(functions);
// TODO(DarkLordZach): Verify that this is the correct behavior.
// Build entry index now to save time later.
if (True(mode & FileSys::OpenDirectoryMode::Directory)) {
BuildEntryIndex(entries, backend->GetSubdirectories(),
FileSys::DirectoryEntryType::Directory);
}
if (True(mode & FileSys::OpenDirectoryMode::File)) {
BuildEntryIndex(entries, backend->GetFiles(), FileSys::DirectoryEntryType::File);
}
}
void IDirectory::Read(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called.");
// Calculate how many entries we can fit in the output buffer
const u64 count_entries = ctx.GetWriteBufferNumElements<FileSys::DirectoryEntry>();
// Cap at total number of entries.
const u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index);
// Determine data start and end
const auto* begin = reinterpret_cast<u8*>(entries.data() + next_entry_index);
const auto* end = reinterpret_cast<u8*>(entries.data() + next_entry_index + actual_entries);
const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
next_entry_index += actual_entries;
// Write the data to memory
ctx.WriteBuffer(begin, range_size);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(actual_entries);
}
void IDirectory::GetEntryCount(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
u64 count = entries.size() - next_entry_index;
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(count);
}
} // namespace Service::FileSystem
+30
View File
@@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/service.h"
namespace FileSys {
struct DirectoryEntry;
}
namespace Service::FileSystem {
class IDirectory final : public ServiceFramework<IDirectory> {
public:
explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_,
FileSys::OpenDirectoryMode mode);
private:
FileSys::VirtualDir backend;
std::vector<FileSys::DirectoryEntry> entries;
u64 next_entry_index = 0;
void Read(HLERequestContext& ctx);
void GetEntryCount(HLERequestContext& ctx);
};
} // namespace Service::FileSystem
+127
View File
@@ -0,0 +1,127 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/file_sys/errors.h"
#include "core/hle/service/filesystem/fsp/fs_i_file.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::FileSystem {
IFile::IFile(Core::System& system_, FileSys::VirtualFile backend_)
: ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) {
static const FunctionInfo functions[] = {
{0, &IFile::Read, "Read"},
{1, &IFile::Write, "Write"},
{2, &IFile::Flush, "Flush"},
{3, &IFile::SetSize, "SetSize"},
{4, &IFile::GetSize, "GetSize"},
{5, nullptr, "OperateRange"},
{6, nullptr, "OperateRangeWithBuffer"},
};
RegisterHandlers(functions);
}
void IFile::Read(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 option = rp.Pop<u64>();
const s64 offset = rp.Pop<s64>();
const s64 length = rp.Pop<s64>();
LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, length);
// Error checking
if (length < 0) {
LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(FileSys::ResultInvalidSize);
return;
}
if (offset < 0) {
LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(FileSys::ResultInvalidOffset);
return;
}
// Read the data from the Storage backend
std::vector<u8> output = backend->ReadBytes(length, offset);
// Write the data to memory
ctx.WriteBuffer(output);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(static_cast<u64>(output.size()));
}
void IFile::Write(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 option = rp.Pop<u64>();
const s64 offset = rp.Pop<s64>();
const s64 length = rp.Pop<s64>();
LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, length);
// Error checking
if (length < 0) {
LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(FileSys::ResultInvalidSize);
return;
}
if (offset < 0) {
LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(FileSys::ResultInvalidOffset);
return;
}
const auto data = ctx.ReadBuffer();
ASSERT_MSG(static_cast<s64>(data.size()) <= length,
"Attempting to write more data than requested (requested={:016X}, actual={:016X}).",
length, data.size());
// Write the data to the Storage backend
const auto write_size =
static_cast<std::size_t>(std::distance(data.begin(), data.begin() + length));
const std::size_t written = backend->Write(data.data(), write_size, offset);
ASSERT_MSG(static_cast<s64>(written) == length,
"Could not write all bytes to file (requested={:016X}, actual={:016X}).", length,
written);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IFile::Flush(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
// Exists for SDK compatibiltity -- No need to flush file.
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IFile::SetSize(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 size = rp.Pop<u64>();
LOG_DEBUG(Service_FS, "called, size={}", size);
backend->Resize(size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IFile::GetSize(HLERequestContext& ctx) {
const u64 size = backend->GetSize();
LOG_DEBUG(Service_FS, "called, size={}", size);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u64>(size);
}
} // namespace Service::FileSystem
+25
View File
@@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/service.h"
namespace Service::FileSystem {
class IFile final : public ServiceFramework<IFile> {
public:
explicit IFile(Core::System& system_, FileSys::VirtualFile backend_);
private:
FileSys::VirtualFile backend;
void Read(HLERequestContext& ctx);
void Write(HLERequestContext& ctx);
void Flush(HLERequestContext& ctx);
void SetSize(HLERequestContext& ctx);
void GetSize(HLERequestContext& ctx);
};
} // namespace Service::FileSystem
+262
View File
@@ -0,0 +1,262 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/string_util.h"
#include "core/hle/service/filesystem/fsp/fs_i_directory.h"
#include "core/hle/service/filesystem/fsp/fs_i_file.h"
#include "core/hle/service/filesystem/fsp/fs_i_filesystem.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::FileSystem {
IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_)
: ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move(
size_)} {
static const FunctionInfo functions[] = {
{0, &IFileSystem::CreateFile, "CreateFile"},
{1, &IFileSystem::DeleteFile, "DeleteFile"},
{2, &IFileSystem::CreateDirectory, "CreateDirectory"},
{3, &IFileSystem::DeleteDirectory, "DeleteDirectory"},
{4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"},
{5, &IFileSystem::RenameFile, "RenameFile"},
{6, nullptr, "RenameDirectory"},
{7, &IFileSystem::GetEntryType, "GetEntryType"},
{8, &IFileSystem::OpenFile, "OpenFile"},
{9, &IFileSystem::OpenDirectory, "OpenDirectory"},
{10, &IFileSystem::Commit, "Commit"},
{11, &IFileSystem::GetFreeSpaceSize, "GetFreeSpaceSize"},
{12, &IFileSystem::GetTotalSpaceSize, "GetTotalSpaceSize"},
{13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"},
{14, &IFileSystem::GetFileTimeStampRaw, "GetFileTimeStampRaw"},
{15, nullptr, "QueryEntry"},
{16, &IFileSystem::GetFileSystemAttribute, "GetFileSystemAttribute"},
};
RegisterHandlers(functions);
}
void IFileSystem::CreateFile(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
const u64 file_mode = rp.Pop<u64>();
const u32 file_size = rp.Pop<u32>();
LOG_DEBUG(Service_FS, "called. file={}, mode=0x{:X}, size=0x{:08X}", name, file_mode,
file_size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.CreateFile(name, file_size));
}
void IFileSystem::DeleteFile(HLERequestContext& ctx) {
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
LOG_DEBUG(Service_FS, "called. file={}", name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.DeleteFile(name));
}
void IFileSystem::CreateDirectory(HLERequestContext& ctx) {
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
LOG_DEBUG(Service_FS, "called. directory={}", name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.CreateDirectory(name));
}
void IFileSystem::DeleteDirectory(HLERequestContext& ctx) {
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
LOG_DEBUG(Service_FS, "called. directory={}", name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.DeleteDirectory(name));
}
void IFileSystem::DeleteDirectoryRecursively(HLERequestContext& ctx) {
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
LOG_DEBUG(Service_FS, "called. directory={}", name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.DeleteDirectoryRecursively(name));
}
void IFileSystem::CleanDirectoryRecursively(HLERequestContext& ctx) {
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
LOG_DEBUG(Service_FS, "called. Directory: {}", name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.CleanDirectoryRecursively(name));
}
void IFileSystem::RenameFile(HLERequestContext& ctx) {
const std::string src_name = Common::StringFromBuffer(ctx.ReadBuffer(0));
const std::string dst_name = Common::StringFromBuffer(ctx.ReadBuffer(1));
LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.RenameFile(src_name, dst_name));
}
void IFileSystem::OpenFile(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
const auto mode = static_cast<FileSys::OpenMode>(rp.Pop<u32>());
LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, mode);
FileSys::VirtualFile vfs_file{};
auto result = backend.OpenFile(&vfs_file, name, mode);
if (result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
auto file = std::make_shared<IFile>(system, vfs_file);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IFile>(std::move(file));
}
void IFileSystem::OpenDirectory(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
const auto mode = rp.PopRaw<FileSys::OpenDirectoryMode>();
LOG_DEBUG(Service_FS, "called. directory={}, mode={}", name, mode);
FileSys::VirtualDir vfs_dir{};
auto result = backend.OpenDirectory(&vfs_dir, name);
if (result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
auto directory = std::make_shared<IDirectory>(system, vfs_dir, mode);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDirectory>(std::move(directory));
}
void IFileSystem::GetEntryType(HLERequestContext& ctx) {
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
LOG_DEBUG(Service_FS, "called. file={}", name);
FileSys::DirectoryEntryType vfs_entry_type{};
auto result = backend.GetEntryType(&vfs_entry_type, name);
if (result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(static_cast<u32>(vfs_entry_type));
}
void IFileSystem::Commit(HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IFileSystem::GetFreeSpaceSize(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(size.get_free_size());
}
void IFileSystem::GetTotalSpaceSize(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(size.get_total_size());
}
void IFileSystem::GetFileTimeStampRaw(HLERequestContext& ctx) {
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", name);
FileSys::FileTimeStampRaw vfs_timestamp{};
auto result = backend.GetFileTimeStampRaw(&vfs_timestamp, name);
if (result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
IPC::ResponseBuilder rb{ctx, 10};
rb.Push(ResultSuccess);
rb.PushRaw(vfs_timestamp);
}
void IFileSystem::GetFileSystemAttribute(HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
struct FileSystemAttribute {
u8 dir_entry_name_length_max_defined;
u8 file_entry_name_length_max_defined;
u8 dir_path_name_length_max_defined;
u8 file_path_name_length_max_defined;
INSERT_PADDING_BYTES_NOINIT(0x5);
u8 utf16_dir_entry_name_length_max_defined;
u8 utf16_file_entry_name_length_max_defined;
u8 utf16_dir_path_name_length_max_defined;
u8 utf16_file_path_name_length_max_defined;
INSERT_PADDING_BYTES_NOINIT(0x18);
s32 dir_entry_name_length_max;
s32 file_entry_name_length_max;
s32 dir_path_name_length_max;
s32 file_path_name_length_max;
INSERT_PADDING_WORDS_NOINIT(0x5);
s32 utf16_dir_entry_name_length_max;
s32 utf16_file_entry_name_length_max;
s32 utf16_dir_path_name_length_max;
s32 utf16_file_path_name_length_max;
INSERT_PADDING_WORDS_NOINIT(0x18);
INSERT_PADDING_WORDS_NOINIT(0x1);
};
static_assert(sizeof(FileSystemAttribute) == 0xc0, "FileSystemAttribute has incorrect size");
FileSystemAttribute savedata_attribute{};
savedata_attribute.dir_entry_name_length_max_defined = true;
savedata_attribute.file_entry_name_length_max_defined = true;
savedata_attribute.dir_entry_name_length_max = 0x40;
savedata_attribute.file_entry_name_length_max = 0x40;
IPC::ResponseBuilder rb{ctx, 50};
rb.Push(ResultSuccess);
rb.PushRaw(savedata_attribute);
}
} // namespace Service::FileSystem
+38
View File
@@ -0,0 +1,38 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/fsp/fsp_util.h"
#include "core/hle/service/service.h"
namespace Service::FileSystem {
class IFileSystem final : public ServiceFramework<IFileSystem> {
public:
explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_);
void CreateFile(HLERequestContext& ctx);
void DeleteFile(HLERequestContext& ctx);
void CreateDirectory(HLERequestContext& ctx);
void DeleteDirectory(HLERequestContext& ctx);
void DeleteDirectoryRecursively(HLERequestContext& ctx);
void CleanDirectoryRecursively(HLERequestContext& ctx);
void RenameFile(HLERequestContext& ctx);
void OpenFile(HLERequestContext& ctx);
void OpenDirectory(HLERequestContext& ctx);
void GetEntryType(HLERequestContext& ctx);
void Commit(HLERequestContext& ctx);
void GetFreeSpaceSize(HLERequestContext& ctx);
void GetTotalSpaceSize(HLERequestContext& ctx);
void GetFileTimeStampRaw(HLERequestContext& ctx);
void GetFileSystemAttribute(HLERequestContext& ctx);
private:
VfsDirectoryServiceWrapper backend;
SizeGetter size;
};
} // namespace Service::FileSystem
+62
View File
@@ -0,0 +1,62 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/file_sys/errors.h"
#include "core/hle/service/filesystem/fsp/fs_i_storage.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::FileSystem {
IStorage::IStorage(Core::System& system_, FileSys::VirtualFile backend_)
: ServiceFramework{system_, "IStorage"}, backend(std::move(backend_)) {
static const FunctionInfo functions[] = {
{0, &IStorage::Read, "Read"},
{1, nullptr, "Write"},
{2, nullptr, "Flush"},
{3, nullptr, "SetSize"},
{4, &IStorage::GetSize, "GetSize"},
{5, nullptr, "OperateRange"},
};
RegisterHandlers(functions);
}
void IStorage::Read(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s64 offset = rp.Pop<s64>();
const s64 length = rp.Pop<s64>();
LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length);
// Error checking
if (length < 0) {
LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(FileSys::ResultInvalidSize);
return;
}
if (offset < 0) {
LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(FileSys::ResultInvalidOffset);
return;
}
// Read the data from the Storage backend
std::vector<u8> output = backend->ReadBytes(length, offset);
// Write the data to memory
ctx.WriteBuffer(output);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IStorage::GetSize(HLERequestContext& ctx) {
const u64 size = backend->GetSize();
LOG_DEBUG(Service_FS, "called, size={}", size);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u64>(size);
}
} // namespace Service::FileSystem
+23
View File
@@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/service.h"
namespace Service::FileSystem {
class IStorage final : public ServiceFramework<IStorage> {
public:
explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_);
private:
FileSys::VirtualFile backend;
void Read(HLERequestContext& ctx);
void GetSize(HLERequestContext& ctx);
};
} // namespace Service::FileSystem
+22
View File
@@ -0,0 +1,22 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/filesystem/fsp/fsp_ldr.h"
namespace Service::FileSystem {
FSP_LDR::FSP_LDR(Core::System& system_) : ServiceFramework{system_, "fsp:ldr"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "OpenCodeFileSystem"},
{1, nullptr, "IsArchivedProgram"},
{2, nullptr, "SetCurrentProcess"},
};
// clang-format on
RegisterHandlers(functions);
}
FSP_LDR::~FSP_LDR() = default;
} // namespace Service::FileSystem
+20
View File
@@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::FileSystem {
class FSP_LDR final : public ServiceFramework<FSP_LDR> {
public:
explicit FSP_LDR(Core::System& system_);
~FSP_LDR() override;
};
} // namespace Service::FileSystem
+23
View File
@@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/filesystem/fsp/fsp_pr.h"
namespace Service::FileSystem {
FSP_PR::FSP_PR(Core::System& system_) : ServiceFramework{system_, "fsp:pr"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "RegisterProgram"},
{1, nullptr, "UnregisterProgram"},
{2, nullptr, "SetCurrentProcess"},
{256, nullptr, "SetEnabledProgramVerification"},
};
// clang-format on
RegisterHandlers(functions);
}
FSP_PR::~FSP_PR() = default;
} // namespace Service::FileSystem
+20
View File
@@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::FileSystem {
class FSP_PR final : public ServiceFramework<FSP_PR> {
public:
explicit FSP_PR(Core::System& system_);
~FSP_PR() override;
};
} // namespace Service::FileSystem
+727
View File
@@ -0,0 +1,727 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cinttypes>
#include <cstring>
#include <iterator>
#include <string>
#include <utility>
#include <vector>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/fs_directory.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/system_archive/system_archive.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/result.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/fsp/fs_i_filesystem.h"
#include "core/hle/service/filesystem/fsp/fs_i_storage.h"
#include "core/hle/service/filesystem/fsp/fsp_srv.h"
#include "core/hle/service/filesystem/romfs_controller.h"
#include "core/hle/service/filesystem/save_data_controller.h"
#include "core/hle/service/hle_ipc.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/reporter.h"
namespace Service::FileSystem {
enum class FileSystemType : u8 {
Invalid0 = 0,
Invalid1 = 1,
Logo = 2,
ContentControl = 3,
ContentManual = 4,
ContentMeta = 5,
ContentData = 6,
ApplicationPackage = 7,
};
class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
public:
explicit ISaveDataInfoReader(Core::System& system_,
std::shared_ptr<SaveDataController> save_data_controller_,
FileSys::SaveDataSpaceId space)
: ServiceFramework{system_, "ISaveDataInfoReader"}, save_data_controller{
save_data_controller_} {
static const FunctionInfo functions[] = {
{0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"},
};
RegisterHandlers(functions);
FindAllSaves(space);
}
void ReadSaveDataInfo(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
// Calculate how many entries we can fit in the output buffer
const u64 count_entries = ctx.GetWriteBufferNumElements<SaveDataInfo>();
// Cap at total number of entries.
const u64 actual_entries = std::min(count_entries, info.size() - next_entry_index);
// Determine data start and end
const auto* begin = reinterpret_cast<u8*>(info.data() + next_entry_index);
const auto* end = reinterpret_cast<u8*>(info.data() + next_entry_index + actual_entries);
const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
next_entry_index += actual_entries;
// Write the data to memory
ctx.WriteBuffer(begin, range_size);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u64>(actual_entries);
}
private:
static u64 stoull_be(std::string_view str) {
if (str.size() != 16)
return 0;
const auto bytes = Common::HexStringToArray<0x8>(str);
u64 out{};
std::memcpy(&out, bytes.data(), sizeof(u64));
return Common::swap64(out);
}
void FindAllSaves(FileSys::SaveDataSpaceId space) {
FileSys::VirtualDir save_root{};
const auto result = save_data_controller->OpenSaveDataSpace(&save_root, space);
if (result != ResultSuccess || save_root == nullptr) {
LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!", space);
return;
}
for (const auto& type : save_root->GetSubdirectories()) {
if (type->GetName() == "save") {
for (const auto& save_id : type->GetSubdirectories()) {
for (const auto& user_id : save_id->GetSubdirectories()) {
const auto save_id_numeric = stoull_be(save_id->GetName());
auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName());
std::reverse(user_id_numeric.begin(), user_id_numeric.end());
if (save_id_numeric != 0) {
// System Save Data
info.emplace_back(SaveDataInfo{
0,
space,
FileSys::SaveDataType::SystemSaveData,
{},
user_id_numeric,
save_id_numeric,
0,
user_id->GetSize(),
{},
{},
});
continue;
}
for (const auto& title_id : user_id->GetSubdirectories()) {
const auto device =
std::all_of(user_id_numeric.begin(), user_id_numeric.end(),
[](u8 val) { return val == 0; });
info.emplace_back(SaveDataInfo{
0,
space,
device ? FileSys::SaveDataType::DeviceSaveData
: FileSys::SaveDataType::SaveData,
{},
user_id_numeric,
save_id_numeric,
stoull_be(title_id->GetName()),
title_id->GetSize(),
{},
{},
});
}
}
}
} else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) {
// Temporary Storage
for (const auto& user_id : type->GetSubdirectories()) {
for (const auto& title_id : user_id->GetSubdirectories()) {
if (!title_id->GetFiles().empty() ||
!title_id->GetSubdirectories().empty()) {
auto user_id_numeric =
Common::HexStringToArray<0x10>(user_id->GetName());
std::reverse(user_id_numeric.begin(), user_id_numeric.end());
info.emplace_back(SaveDataInfo{
0,
space,
FileSys::SaveDataType::TemporaryStorage,
{},
user_id_numeric,
stoull_be(type->GetName()),
stoull_be(title_id->GetName()),
title_id->GetSize(),
{},
{},
});
}
}
}
}
}
}
struct SaveDataInfo {
u64_le save_id_unknown;
FileSys::SaveDataSpaceId space;
FileSys::SaveDataType type;
INSERT_PADDING_BYTES(0x6);
std::array<u8, 0x10> user_id;
u64_le save_id;
u64_le title_id;
u64_le save_image_size;
u16_le index;
FileSys::SaveDataRank rank;
INSERT_PADDING_BYTES(0x25);
};
static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size.");
ProcessId process_id = 0;
std::shared_ptr<SaveDataController> save_data_controller;
std::vector<SaveDataInfo> info;
u64 next_entry_index = 0;
};
FSP_SRV::FSP_SRV(Core::System& system_)
: ServiceFramework{system_, "fsp-srv"}, fsc{system.GetFileSystemController()},
content_provider{system.GetContentProvider()}, reporter{system.GetReporter()} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "OpenFileSystem"},
{1, &FSP_SRV::SetCurrentProcess, "SetCurrentProcess"},
{2, nullptr, "OpenDataFileSystemByCurrentProcess"},
{7, &FSP_SRV::OpenFileSystemWithPatch, "OpenFileSystemWithPatch"},
{8, nullptr, "OpenFileSystemWithId"},
{9, nullptr, "OpenDataFileSystemByApplicationId"},
{11, nullptr, "OpenBisFileSystem"},
{12, nullptr, "OpenBisStorage"},
{13, nullptr, "InvalidateBisCache"},
{17, nullptr, "OpenHostFileSystem"},
{18, &FSP_SRV::OpenSdCardFileSystem, "OpenSdCardFileSystem"},
{19, nullptr, "FormatSdCardFileSystem"},
{21, nullptr, "DeleteSaveDataFileSystem"},
{22, &FSP_SRV::CreateSaveDataFileSystem, "CreateSaveDataFileSystem"},
{23, &FSP_SRV::CreateSaveDataFileSystemBySystemSaveDataId, "CreateSaveDataFileSystemBySystemSaveDataId"},
{24, nullptr, "RegisterSaveDataFileSystemAtomicDeletion"},
{25, nullptr, "DeleteSaveDataFileSystemBySaveDataSpaceId"},
{26, nullptr, "FormatSdCardDryRun"},
{27, nullptr, "IsExFatSupported"},
{28, nullptr, "DeleteSaveDataFileSystemBySaveDataAttribute"},
{30, nullptr, "OpenGameCardStorage"},
{31, nullptr, "OpenGameCardFileSystem"},
{32, nullptr, "ExtendSaveDataFileSystem"},
{33, nullptr, "DeleteCacheStorage"},
{34, &FSP_SRV::GetCacheStorageSize, "GetCacheStorageSize"},
{35, nullptr, "CreateSaveDataFileSystemByHashSalt"},
{36, nullptr, "OpenHostFileSystemWithOption"},
{51, &FSP_SRV::OpenSaveDataFileSystem, "OpenSaveDataFileSystem"},
{52, &FSP_SRV::OpenSaveDataFileSystemBySystemSaveDataId, "OpenSaveDataFileSystemBySystemSaveDataId"},
{53, &FSP_SRV::OpenReadOnlySaveDataFileSystem, "OpenReadOnlySaveDataFileSystem"},
{57, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataSpaceId"},
{58, nullptr, "ReadSaveDataFileSystemExtraData"},
{59, nullptr, "WriteSaveDataFileSystemExtraData"},
{60, nullptr, "OpenSaveDataInfoReader"},
{61, &FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId, "OpenSaveDataInfoReaderBySaveDataSpaceId"},
{62, &FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage, "OpenSaveDataInfoReaderOnlyCacheStorage"},
{64, nullptr, "OpenSaveDataInternalStorageFileSystem"},
{65, nullptr, "UpdateSaveDataMacForDebug"},
{66, nullptr, "WriteSaveDataFileSystemExtraData2"},
{67, nullptr, "FindSaveDataWithFilter"},
{68, nullptr, "OpenSaveDataInfoReaderBySaveDataFilter"},
{69, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataAttribute"},
{70, &FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute, "WriteSaveDataFileSystemExtraDataBySaveDataAttribute"},
{71, &FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute, "ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute"},
{80, nullptr, "OpenSaveDataMetaFile"},
{81, nullptr, "OpenSaveDataTransferManager"},
{82, nullptr, "OpenSaveDataTransferManagerVersion2"},
{83, nullptr, "OpenSaveDataTransferProhibiterForCloudBackUp"},
{84, nullptr, "ListApplicationAccessibleSaveDataOwnerId"},
{85, nullptr, "OpenSaveDataTransferManagerForSaveDataRepair"},
{86, nullptr, "OpenSaveDataMover"},
{87, nullptr, "OpenSaveDataTransferManagerForRepair"},
{100, nullptr, "OpenImageDirectoryFileSystem"},
{101, nullptr, "OpenBaseFileSystem"},
{102, nullptr, "FormatBaseFileSystem"},
{110, nullptr, "OpenContentStorageFileSystem"},
{120, nullptr, "OpenCloudBackupWorkStorageFileSystem"},
{130, nullptr, "OpenCustomStorageFileSystem"},
{200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"},
{201, nullptr, "OpenDataStorageByProgramId"},
{202, &FSP_SRV::OpenDataStorageByDataId, "OpenDataStorageByDataId"},
{203, &FSP_SRV::OpenPatchDataStorageByCurrentProcess, "OpenPatchDataStorageByCurrentProcess"},
{204, nullptr, "OpenDataFileSystemByProgramIndex"},
{205, &FSP_SRV::OpenDataStorageWithProgramIndex, "OpenDataStorageWithProgramIndex"},
{206, nullptr, "OpenDataStorageByPath"},
{400, nullptr, "OpenDeviceOperator"},
{500, nullptr, "OpenSdCardDetectionEventNotifier"},
{501, nullptr, "OpenGameCardDetectionEventNotifier"},
{510, nullptr, "OpenSystemDataUpdateEventNotifier"},
{511, nullptr, "NotifySystemDataUpdateEvent"},
{520, nullptr, "SimulateGameCardDetectionEvent"},
{600, nullptr, "SetCurrentPosixTime"},
{601, nullptr, "QuerySaveDataTotalSize"},
{602, nullptr, "VerifySaveDataFileSystem"},
{603, nullptr, "CorruptSaveDataFileSystem"},
{604, nullptr, "CreatePaddingFile"},
{605, nullptr, "DeleteAllPaddingFiles"},
{606, nullptr, "GetRightsId"},
{607, nullptr, "RegisterExternalKey"},
{608, nullptr, "UnregisterAllExternalKey"},
{609, nullptr, "GetRightsIdByPath"},
{610, nullptr, "GetRightsIdAndKeyGenerationByPath"},
{611, nullptr, "SetCurrentPosixTimeWithTimeDifference"},
{612, nullptr, "GetFreeSpaceSizeForSaveData"},
{613, nullptr, "VerifySaveDataFileSystemBySaveDataSpaceId"},
{614, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId"},
{615, nullptr, "QuerySaveDataInternalStorageTotalSize"},
{616, nullptr, "GetSaveDataCommitId"},
{617, nullptr, "UnregisterExternalKey"},
{620, nullptr, "SetSdCardEncryptionSeed"},
{630, nullptr, "SetSdCardAccessibility"},
{631, nullptr, "IsSdCardAccessible"},
{640, nullptr, "IsSignedSystemPartitionOnSdCardValid"},
{700, nullptr, "OpenAccessFailureResolver"},
{701, nullptr, "GetAccessFailureDetectionEvent"},
{702, nullptr, "IsAccessFailureDetected"},
{710, nullptr, "ResolveAccessFailure"},
{720, nullptr, "AbandonAccessFailure"},
{800, nullptr, "GetAndClearFileSystemProxyErrorInfo"},
{810, nullptr, "RegisterProgramIndexMapInfo"},
{1000, nullptr, "SetBisRootForHost"},
{1001, nullptr, "SetSaveDataSize"},
{1002, nullptr, "SetSaveDataRootPath"},
{1003, &FSP_SRV::DisableAutoSaveDataCreation, "DisableAutoSaveDataCreation"},
{1004, &FSP_SRV::SetGlobalAccessLogMode, "SetGlobalAccessLogMode"},
{1005, &FSP_SRV::GetGlobalAccessLogMode, "GetGlobalAccessLogMode"},
{1006, &FSP_SRV::OutputAccessLogToSdCard, "OutputAccessLogToSdCard"},
{1007, nullptr, "RegisterUpdatePartition"},
{1008, nullptr, "OpenRegisteredUpdatePartition"},
{1009, nullptr, "GetAndClearMemoryReportInfo"},
{1010, nullptr, "SetDataStorageRedirectTarget"},
{1011, &FSP_SRV::GetProgramIndexForAccessLog, "GetProgramIndexForAccessLog"},
{1012, nullptr, "GetFsStackUsage"},
{1013, nullptr, "UnsetSaveDataRootPath"},
{1014, nullptr, "OutputMultiProgramTagAccessLog"},
{1016, nullptr, "FlushAccessLogOnSdCard"},
{1017, nullptr, "OutputApplicationInfoAccessLog"},
{1018, nullptr, "SetDebugOption"},
{1019, nullptr, "UnsetDebugOption"},
{1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
{1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"},
{1200, &FSP_SRV::OpenMultiCommitManager, "OpenMultiCommitManager"},
{1300, nullptr, "OpenBisWiper"},
};
// clang-format on
RegisterHandlers(functions);
if (Settings::values.enable_fs_access_log) {
access_log_mode = AccessLogMode::SdCard;
}
}
FSP_SRV::~FSP_SRV() = default;
void FSP_SRV::SetCurrentProcess(HLERequestContext& ctx) {
current_process_id = ctx.GetPID();
LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id);
const auto res =
fsc.OpenProcess(&program_id, &save_data_controller, &romfs_controller, current_process_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void FSP_SRV::OpenFileSystemWithPatch(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto type = rp.PopRaw<FileSystemType>();
const auto title_id = rp.PopRaw<u64>();
LOG_WARNING(Service_FS, "(STUBBED) called with type={}, title_id={:016X}", type, title_id);
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
rb.Push(ResultUnknown);
}
void FSP_SRV::OpenSdCardFileSystem(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
FileSys::VirtualDir sdmc_dir{};
fsc.OpenSDMC(&sdmc_dir);
auto filesystem = std::make_shared<IFileSystem>(
system, sdmc_dir, SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
}
void FSP_SRV::CreateSaveDataFileSystem(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto save_struct = rp.PopRaw<FileSys::SaveDataAttribute>();
[[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
u128 uid = rp.PopRaw<u128>();
LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(),
uid[1], uid[0]);
FileSys::VirtualDir save_data_dir{};
save_data_controller->CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandUser,
save_struct);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void FSP_SRV::CreateSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto save_struct = rp.PopRaw<FileSys::SaveDataAttribute>();
[[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
LOG_DEBUG(Service_FS, "called save_struct = {}", save_struct.DebugInfo());
FileSys::VirtualDir save_data_dir{};
save_data_controller->CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::NandSystem,
save_struct);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void FSP_SRV::OpenSaveDataFileSystem(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
FileSys::SaveDataSpaceId space_id;
FileSys::SaveDataAttribute attribute;
};
const auto parameters = rp.PopRaw<Parameters>();
LOG_INFO(Service_FS, "called.");
FileSys::VirtualDir dir{};
auto result =
save_data_controller->OpenSaveData(&dir, parameters.space_id, parameters.attribute);
if (result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
rb.Push(FileSys::ResultTargetNotFound);
return;
}
FileSys::StorageId id{};
switch (parameters.space_id) {
case FileSys::SaveDataSpaceId::NandUser:
id = FileSys::StorageId::NandUser;
break;
case FileSys::SaveDataSpaceId::SdCardSystem:
case FileSys::SaveDataSpaceId::SdCardUser:
id = FileSys::StorageId::SdCard;
break;
case FileSys::SaveDataSpaceId::NandSystem:
id = FileSys::StorageId::NandSystem;
break;
case FileSys::SaveDataSpaceId::TemporaryStorage:
case FileSys::SaveDataSpaceId::ProperSystem:
case FileSys::SaveDataSpaceId::SafeMode:
ASSERT(false);
}
auto filesystem =
std::make_shared<IFileSystem>(system, std::move(dir), SizeGetter::FromStorageId(fsc, id));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
}
void FSP_SRV::OpenSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem");
OpenSaveDataFileSystem(ctx);
}
void FSP_SRV::OpenReadOnlySaveDataFileSystem(HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem");
OpenSaveDataFileSystem(ctx);
}
void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto space = rp.PopRaw<FileSys::SaveDataSpaceId>();
LOG_INFO(Service_FS, "called, space={}", space);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ISaveDataInfoReader>(
std::make_shared<ISaveDataInfoReader>(system, save_data_controller, space));
}
void FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ISaveDataInfoReader>(system, save_data_controller,
FileSys::SaveDataSpaceId::TemporaryStorage);
}
void FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute(HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called.");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
FileSys::SaveDataSpaceId space_id;
FileSys::SaveDataAttribute attribute;
};
const auto parameters = rp.PopRaw<Parameters>();
// Stub this to None for now, backend needs an impl to read/write the SaveDataExtraData
constexpr auto flags = static_cast<u32>(FileSys::SaveDataFlags::None);
LOG_WARNING(Service_FS,
"(STUBBED) called, flags={}, space_id={}, attribute.title_id={:016X}\n"
"attribute.user_id={:016X}{:016X}, attribute.save_id={:016X}\n"
"attribute.type={}, attribute.rank={}, attribute.index={}",
flags, parameters.space_id, parameters.attribute.title_id,
parameters.attribute.user_id[1], parameters.attribute.user_id[0],
parameters.attribute.save_id, parameters.attribute.type, parameters.attribute.rank,
parameters.attribute.index);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(flags);
}
void FSP_SRV::OpenDataStorageByCurrentProcess(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
if (!romfs) {
auto current_romfs = romfs_controller->OpenRomFSCurrentProcess();
if (!current_romfs) {
// TODO (bunnei): Find the right error code to use here
LOG_CRITICAL(Service_FS, "no file system interface available!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
romfs = current_romfs;
}
auto storage = std::make_shared<IStorage>(system, romfs);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IStorage>(std::move(storage));
}
void FSP_SRV::OpenDataStorageByDataId(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto storage_id = rp.PopRaw<FileSys::StorageId>();
const auto unknown = rp.PopRaw<u32>();
const auto title_id = rp.PopRaw<u64>();
LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}",
storage_id, unknown, title_id);
auto data = romfs_controller->OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
if (!data) {
const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id);
if (archive != nullptr) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface(std::make_shared<IStorage>(system, archive));
return;
}
// TODO(DarkLordZach): Find the right error code to use here
LOG_ERROR(Service_FS,
"could not open data storage with title_id={:016X}, storage_id={:02X}", title_id,
storage_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
const FileSys::PatchManager pm{title_id, fsc, content_provider};
auto base =
romfs_controller->OpenBaseNca(title_id, storage_id, FileSys::ContentRecordType::Data);
auto storage = std::make_shared<IStorage>(
system, pm.PatchRomFS(base.get(), std::move(data), FileSys::ContentRecordType::Data));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IStorage>(std::move(storage));
}
void FSP_SRV::OpenPatchDataStorageByCurrentProcess(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto storage_id = rp.PopRaw<FileSys::StorageId>();
const auto title_id = rp.PopRaw<u64>();
LOG_DEBUG(Service_FS, "called with storage_id={:02X}, title_id={:016X}", storage_id, title_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(FileSys::ResultTargetNotFound);
}
void FSP_SRV::OpenDataStorageWithProgramIndex(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto program_index = rp.PopRaw<u8>();
LOG_DEBUG(Service_FS, "called, program_index={}", program_index);
auto patched_romfs = romfs_controller->OpenPatchedRomFSWithProgramIndex(
program_id, program_index, FileSys::ContentRecordType::Program);
if (!patched_romfs) {
// TODO: Find the right error code to use here
LOG_ERROR(Service_FS, "could not open storage with program_index={}", program_index);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
auto storage = std::make_shared<IStorage>(system, std::move(patched_romfs));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IStorage>(std::move(storage));
}
void FSP_SRV::DisableAutoSaveDataCreation(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
save_data_controller->SetAutoCreate(false);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void FSP_SRV::SetGlobalAccessLogMode(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
access_log_mode = rp.PopEnum<AccessLogMode>();
LOG_DEBUG(Service_FS, "called, access_log_mode={}", access_log_mode);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void FSP_SRV::GetGlobalAccessLogMode(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(access_log_mode);
}
void FSP_SRV::OutputAccessLogToSdCard(HLERequestContext& ctx) {
const auto raw = ctx.ReadBufferCopy();
auto log = Common::StringFromFixedZeroTerminatedBuffer(
reinterpret_cast<const char*>(raw.data()), raw.size());
LOG_DEBUG(Service_FS, "called");
reporter.SaveFSAccessLog(log);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void FSP_SRV::GetProgramIndexForAccessLog(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.PushEnum(AccessLogVersion::Latest);
rb.Push(access_log_program_index);
}
void FSP_SRV::GetCacheStorageSize(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto index{rp.Pop<s32>()};
LOG_WARNING(Service_FS, "(STUBBED) called with index={}", index);
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.Push(s64{0});
rb.Push(s64{0});
}
class IMultiCommitManager final : public ServiceFramework<IMultiCommitManager> {
public:
explicit IMultiCommitManager(Core::System& system_)
: ServiceFramework{system_, "IMultiCommitManager"} {
static const FunctionInfo functions[] = {
{1, &IMultiCommitManager::Add, "Add"},
{2, &IMultiCommitManager::Commit, "Commit"},
};
RegisterHandlers(functions);
}
private:
FileSys::VirtualFile backend;
void Add(HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Commit(HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
};
void FSP_SRV::OpenMultiCommitManager(HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IMultiCommitManager>(std::make_shared<IMultiCommitManager>(system));
}
} // namespace Service::FileSystem
+78
View File
@@ -0,0 +1,78 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include "core/hle/service/service.h"
namespace Core {
class Reporter;
}
namespace FileSys {
class ContentProvider;
class FileSystemBackend;
} // namespace FileSys
namespace Service::FileSystem {
class RomFsController;
class SaveDataController;
enum class AccessLogVersion : u32 {
V7_0_0 = 2,
Latest = V7_0_0,
};
enum class AccessLogMode : u32 {
None,
Log,
SdCard,
};
class FSP_SRV final : public ServiceFramework<FSP_SRV> {
public:
explicit FSP_SRV(Core::System& system_);
~FSP_SRV() override;
private:
void SetCurrentProcess(HLERequestContext& ctx);
void OpenFileSystemWithPatch(HLERequestContext& ctx);
void OpenSdCardFileSystem(HLERequestContext& ctx);
void CreateSaveDataFileSystem(HLERequestContext& ctx);
void CreateSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx);
void OpenSaveDataFileSystem(HLERequestContext& ctx);
void OpenSaveDataFileSystemBySystemSaveDataId(HLERequestContext& ctx);
void OpenReadOnlySaveDataFileSystem(HLERequestContext& ctx);
void OpenSaveDataInfoReaderBySaveDataSpaceId(HLERequestContext& ctx);
void OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx);
void WriteSaveDataFileSystemExtraDataBySaveDataAttribute(HLERequestContext& ctx);
void ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(HLERequestContext& ctx);
void OpenDataStorageByCurrentProcess(HLERequestContext& ctx);
void OpenDataStorageByDataId(HLERequestContext& ctx);
void OpenPatchDataStorageByCurrentProcess(HLERequestContext& ctx);
void OpenDataStorageWithProgramIndex(HLERequestContext& ctx);
void DisableAutoSaveDataCreation(HLERequestContext& ctx);
void SetGlobalAccessLogMode(HLERequestContext& ctx);
void GetGlobalAccessLogMode(HLERequestContext& ctx);
void OutputAccessLogToSdCard(HLERequestContext& ctx);
void GetProgramIndexForAccessLog(HLERequestContext& ctx);
void OpenMultiCommitManager(HLERequestContext& ctx);
void GetCacheStorageSize(HLERequestContext& ctx);
FileSystemController& fsc;
const FileSys::ContentProvider& content_provider;
const Core::Reporter& reporter;
FileSys::VirtualFile romfs;
u64 current_process_id = 0;
u32 access_log_program_index = 0;
AccessLogMode access_log_mode = AccessLogMode::None;
u64 program_id = 0;
std::shared_ptr<SaveDataController> save_data_controller;
std::shared_ptr<RomFsController> romfs_controller;
};
} // namespace Service::FileSystem
+22
View File
@@ -0,0 +1,22 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/filesystem/filesystem.h"
namespace Service::FileSystem {
struct SizeGetter {
std::function<u64()> get_free_size;
std::function<u64()> get_total_size;
static SizeGetter FromStorageId(const FileSystemController& fsc, FileSys::StorageId id) {
return {
[&fsc, id] { return fsc.GetFreeSpaceSize(id); },
[&fsc, id] { return fsc.GetTotalSpaceSize(id); },
};
}
};
} // namespace Service::FileSystem
@@ -5,7 +5,7 @@
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/vfs_types.h"
#include "core/file_sys/vfs/vfs_types.h"
namespace Service::FileSystem {
@@ -44,7 +44,7 @@ Result SaveDataController::CreateSaveData(FileSys::VirtualDir* out_save_data,
auto save_data = factory->Create(space, attribute);
if (save_data == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
return FileSys::ResultTargetNotFound;
}
*out_save_data = save_data;
@@ -56,7 +56,7 @@ Result SaveDataController::OpenSaveData(FileSys::VirtualDir* out_save_data,
const FileSys::SaveDataAttribute& attribute) {
auto save_data = factory->Open(space, attribute);
if (save_data == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
return FileSys::ResultTargetNotFound;
}
*out_save_data = save_data;
@@ -67,7 +67,7 @@ Result SaveDataController::OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_
FileSys::SaveDataSpaceId space) {
auto save_data_space = factory->GetSaveDataSpaceDirectory(space);
if (save_data_space == nullptr) {
return FileSys::ERROR_ENTITY_NOT_FOUND;
return FileSys::ResultTargetNotFound;
}
*out_save_data_space = save_data_space;
@@ -5,7 +5,7 @@
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs_types.h"
#include "core/file_sys/vfs/vfs_types.h"
namespace Service::FileSystem {
+1 -1
View File
@@ -8,7 +8,7 @@
#include "common/settings.h"
#include "common/time_zone.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/service/glue/time/manager.h"
#include "core/hle/service/glue/time/time_zone_binary.h"
+1 -1
View File
@@ -7,7 +7,7 @@
#include <string>
#include "common/common_types.h"
#include "core/file_sys/vfs_types.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/service/glue/time/file_timestamp_worker.h"
#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
#include "core/hle/service/glue/time/worker.h"
@@ -7,7 +7,7 @@
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/system_archive/system_archive.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/glue/time/time_zone_binary.h"
+16
View File
@@ -501,6 +501,22 @@ bool HLERequestContext::CanWriteBuffer(std::size_t buffer_index) const {
}
}
void HLERequestContext::AddMoveInterface(SessionRequestHandlerPtr s) {
ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve(
Kernel::LimitableResource::SessionCountMax, 1));
auto* session = Kernel::KSession::Create(kernel);
session->Initialize(nullptr, 0);
Kernel::KSession::Register(kernel, session);
auto& server = manager.lock()->GetServerManager();
auto next_manager = std::make_shared<Service::SessionRequestManager>(kernel, server);
next_manager->SetSessionHandler(std::move(s));
server.RegisterSession(&session->GetServerSession(), next_manager);
AddMoveObject(&session->GetClientSession());
}
std::string HLERequestContext::Description() const {
if (!command_header) {
return "No command header available";
+2
View File
@@ -339,6 +339,8 @@ public:
outgoing_move_objects.emplace_back(object);
}
void AddMoveInterface(SessionRequestHandlerPtr s);
void AddCopyObject(Kernel::KAutoObject* object) {
outgoing_copy_objects.emplace_back(object);
}
+78 -197
View File
@@ -6,12 +6,12 @@
#include "core/core.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/result.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/jit/jit.h"
#include "core/hle/service/jit/jit_code_memory.h"
#include "core/hle/service/jit/jit_context.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
#include "core/memory.h"
namespace Service::JIT {
@@ -21,6 +21,9 @@ struct CodeRange {
u64 size;
};
using Struct32 = std::array<u64, 4>;
static_assert(sizeof(Struct32) == 32, "Struct32 has wrong size");
class IJitEnvironment final : public ServiceFramework<IJitEnvironment> {
public:
explicit IJitEnvironment(Core::System& system_,
@@ -29,12 +32,13 @@ public:
: ServiceFramework{system_, "IJitEnvironment"}, process{std::move(process_)},
user_rx{std::move(user_rx_)}, user_ro{std::move(user_ro_)},
context{system_.ApplicationMemory()} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IJitEnvironment::GenerateCode, "GenerateCode"},
{1, &IJitEnvironment::Control, "Control"},
{1000, &IJitEnvironment::LoadPlugin, "LoadPlugin"},
{1001, &IJitEnvironment::GetCodeAddress, "GetCodeAddress"},
{0, C<&IJitEnvironment::GenerateCode>, "GenerateCode"},
{1, C<&IJitEnvironment::Control>, "Control"},
{1000, C<&IJitEnvironment::LoadPlugin>, "LoadPlugin"},
{1001, C<&IJitEnvironment::GetCodeAddress>, "GetCodeAddress"},
};
// clang-format on
@@ -50,28 +54,10 @@ public:
configuration.sys_ro_memory = configuration.user_ro_memory;
}
void GenerateCode(HLERequestContext& ctx) {
LOG_DEBUG(Service_JIT, "called");
struct InputParameters {
u32 data_size;
u64 command;
std::array<CodeRange, 2> ranges;
Struct32 data;
};
struct OutputParameters {
s32 return_value;
std::array<CodeRange, 2> ranges;
};
IPC::RequestParser rp{ctx};
const auto parameters{rp.PopRaw<InputParameters>()};
// Optional input/output buffers
const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span<const u8>()};
std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
Result GenerateCode(Out<s32> out_return_value, Out<CodeRange> out_range0,
Out<CodeRange> out_range1, OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
u32 data_size, u64 command, CodeRange range0, CodeRange range1,
Struct32 data, InBuffer<BufferAttr_HipcMapAlias> buffer) {
// Function call prototype:
// void GenerateCode(s32* ret, CodeRange* c0_out, CodeRange* c1_out, JITConfiguration* cfg,
// u64 cmd, u8* input_buf, size_t input_size, CodeRange* c0_in,
@@ -83,66 +69,36 @@ public:
// other arguments are used to transfer state between the game and the plugin.
const VAddr ret_ptr{context.AddHeap(0u)};
const VAddr c0_in_ptr{context.AddHeap(parameters.ranges[0])};
const VAddr c1_in_ptr{context.AddHeap(parameters.ranges[1])};
const VAddr c0_out_ptr{context.AddHeap(ClearSize(parameters.ranges[0]))};
const VAddr c1_out_ptr{context.AddHeap(ClearSize(parameters.ranges[1]))};
const VAddr c0_in_ptr{context.AddHeap(range0)};
const VAddr c1_in_ptr{context.AddHeap(range1)};
const VAddr c0_out_ptr{context.AddHeap(ClearSize(range0))};
const VAddr c1_out_ptr{context.AddHeap(ClearSize(range1))};
const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())};
const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())};
const VAddr data_ptr{context.AddHeap(parameters.data)};
const VAddr input_ptr{context.AddHeap(buffer.data(), buffer.size())};
const VAddr output_ptr{context.AddHeap(out_buffer.data(), out_buffer.size())};
const VAddr data_ptr{context.AddHeap(data)};
const VAddr configuration_ptr{context.AddHeap(configuration)};
// The callback does not directly return a value, it only writes to the output pointer
context.CallFunction(callbacks.GenerateCode, ret_ptr, c0_out_ptr, c1_out_ptr,
configuration_ptr, parameters.command, input_ptr, input_buffer.size(),
c0_in_ptr, c1_in_ptr, data_ptr, parameters.data_size, output_ptr,
output_buffer.size());
configuration_ptr, command, input_ptr, buffer.size(), c0_in_ptr,
c1_in_ptr, data_ptr, data_size, output_ptr, out_buffer.size());
const s32 return_value{context.GetHeap<s32>(ret_ptr)};
*out_return_value = context.GetHeap<s32>(ret_ptr);
*out_range0 = context.GetHeap<CodeRange>(c0_out_ptr);
*out_range1 = context.GetHeap<CodeRange>(c1_out_ptr);
context.GetHeap(output_ptr, out_buffer.data(), out_buffer.size());
if (return_value == 0) {
// The callback has written to the output executable code range,
// requiring an instruction cache invalidation
Core::InvalidateInstructionCacheRange(process.GetPointerUnsafe(),
configuration.user_rx_memory.offset,
configuration.user_rx_memory.size);
// Write back to the IPC output buffer, if provided
if (ctx.CanWriteBuffer()) {
context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());
ctx.WriteBuffer(output_buffer.data(), output_buffer.size());
}
const OutputParameters out{
.return_value = return_value,
.ranges =
{
context.GetHeap<CodeRange>(c0_out_ptr),
context.GetHeap<CodeRange>(c1_out_ptr),
},
};
IPC::ResponseBuilder rb{ctx, 8};
rb.Push(ResultSuccess);
rb.PushRaw(out);
} else {
if (*out_return_value != 0) {
LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
R_THROW(ResultUnknown);
}
};
void Control(HLERequestContext& ctx) {
LOG_DEBUG(Service_JIT, "called");
IPC::RequestParser rp{ctx};
const auto command{rp.PopRaw<u64>()};
// Optional input/output buffers
const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span<const u8>()};
std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
R_SUCCEED();
}
Result Control(Out<s32> out_return_value, InBuffer<BufferAttr_HipcMapAlias> in_data,
OutBuffer<BufferAttr_HipcMapAlias> out_data, u64 command) {
// Function call prototype:
// u64 Control(s32* ret, JITConfiguration* cfg, u64 cmd, u8* input_buf, size_t input_size,
// u8* output_buf, size_t output_size);
@@ -152,53 +108,30 @@ public:
const VAddr ret_ptr{context.AddHeap(0u)};
const VAddr configuration_ptr{context.AddHeap(configuration)};
const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())};
const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())};
const VAddr input_ptr{context.AddHeap(in_data.data(), in_data.size())};
const VAddr output_ptr{context.AddHeap(out_data.data(), out_data.size())};
const u64 wrapper_value{context.CallFunction(callbacks.Control, ret_ptr, configuration_ptr,
command, input_ptr, input_buffer.size(),
output_ptr, output_buffer.size())};
command, input_ptr, in_data.size(), output_ptr,
out_data.size())};
const s32 return_value{context.GetHeap<s32>(ret_ptr)};
*out_return_value = context.GetHeap<s32>(ret_ptr);
context.GetHeap(output_ptr, out_data.data(), out_data.size());
if (wrapper_value == 0 && return_value == 0) {
// Write back to the IPC output buffer, if provided
if (ctx.CanWriteBuffer()) {
context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());
ctx.WriteBuffer(output_buffer.data(), output_buffer.size());
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(return_value);
} else {
LOG_WARNING(Service_JIT, "plugin Control callback failed");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
if (wrapper_value == 0 && *out_return_value == 0) {
R_SUCCEED();
}
LOG_WARNING(Service_JIT, "plugin Control callback failed");
R_THROW(ResultUnknown);
}
void LoadPlugin(HLERequestContext& ctx) {
LOG_DEBUG(Service_JIT, "called");
IPC::RequestParser rp{ctx};
const auto tmem_size{rp.PopRaw<u64>()};
const auto tmem_handle{ctx.GetCopyHandle(0)};
const auto nro_plugin{ctx.ReadBuffer(1)};
if (tmem_size == 0) {
LOG_ERROR(Service_JIT, "attempted to load plugin with empty transfer memory");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
auto tmem{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(tmem_handle)};
Result LoadPlugin(u64 tmem_size, InCopyHandle<Kernel::KTransferMemory>& tmem,
InBuffer<BufferAttr_HipcMapAlias> nrr,
InBuffer<BufferAttr_HipcMapAlias> nro) {
if (tmem.IsNull()) {
LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
LOG_ERROR(Service_JIT, "Invalid transfer memory handle!");
R_THROW(ResultUnknown);
}
// Set up the configuration with the required TransferMemory address
@@ -206,7 +139,7 @@ public:
configuration.transfer_memory.size = tmem_size;
// Gather up all the callbacks from the loaded plugin
auto symbols{Core::Symbols::GetSymbols(nro_plugin, true)};
auto symbols{Core::Symbols::GetSymbols(nro, true)};
const auto GetSymbol{[&](const std::string& name) { return symbols[name].first; }};
callbacks.rtld_fini = GetSymbol("_fini");
@@ -223,16 +156,12 @@ public:
if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 ||
callbacks.OnPrepared == 0) {
LOG_ERROR(Service_JIT, "plugin does not implement all necessary functionality");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
R_THROW(ResultUnknown);
}
if (!context.LoadNRO(nro_plugin)) {
if (!context.LoadNRO(nro)) {
LOG_ERROR(Service_JIT, "failed to load plugin");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
R_THROW(ResultUnknown);
}
context.MapProcessMemory(configuration.sys_ro_memory.offset,
@@ -252,9 +181,7 @@ public:
const auto version{context.CallFunction(callbacks.GetVersion)};
if (version != 1) {
LOG_ERROR(Service_JIT, "unknown plugin version {}", version);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
R_THROW(ResultUnknown);
}
// Function prototype:
@@ -280,22 +207,19 @@ public:
const auto configuration_ptr{context.AddHeap(configuration)};
context.CallFunction(callbacks.OnPrepared, configuration_ptr);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
R_SUCCEED();
}
void GetCodeAddress(HLERequestContext& ctx) {
Result GetCodeAddress(Out<u64> rx_offset, Out<u64> ro_offset) {
LOG_DEBUG(Service_JIT, "called");
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.Push(configuration.user_rx_memory.offset);
rb.Push(configuration.user_ro_memory.offset);
*rx_offset = configuration.user_rx_memory.offset;
*ro_offset = configuration.user_ro_memory.offset;
R_SUCCEED();
}
private:
using Struct32 = std::array<u8, 32>;
struct GuestCallbacks {
VAddr rtld_fini;
VAddr rtld_init;
@@ -335,7 +259,7 @@ public:
explicit JITU(Core::System& system_) : ServiceFramework{system_, "jit:u"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &JITU::CreateJitEnvironment, "CreateJitEnvironment"},
{0, C<&JITU::CreateJitEnvironment>, "CreateJitEnvironment"},
};
// clang-format on
@@ -343,76 +267,33 @@ public:
}
private:
void CreateJitEnvironment(HLERequestContext& ctx) {
LOG_DEBUG(Service_JIT, "called");
struct Parameters {
u64 rx_size;
u64 ro_size;
};
IPC::RequestParser rp{ctx};
const auto parameters{rp.PopRaw<Parameters>()};
const auto process_handle{ctx.GetCopyHandle(0)};
const auto rx_mem_handle{ctx.GetCopyHandle(1)};
const auto ro_mem_handle{ctx.GetCopyHandle(2)};
if (parameters.rx_size == 0 || parameters.ro_size == 0) {
LOG_ERROR(Service_JIT, "attempted to init with empty code regions");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle)};
Result CreateJitEnvironment(Out<SharedPointer<IJitEnvironment>> out_jit_environment,
u64 rx_size, u64 ro_size, InCopyHandle<Kernel::KProcess>& process,
InCopyHandle<Kernel::KCodeMemory>& rx_mem,
InCopyHandle<Kernel::KCodeMemory>& ro_mem) {
if (process.IsNull()) {
LOG_ERROR(Service_JIT, "process is null for handle=0x{:08X}", process_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
LOG_ERROR(Service_JIT, "process is null");
R_THROW(ResultUnknown);
}
auto rx_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(rx_mem_handle)};
if (rx_mem.IsNull()) {
LOG_ERROR(Service_JIT, "rx_mem is null for handle=0x{:08X}", rx_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
LOG_ERROR(Service_JIT, "rx_mem is null");
R_THROW(ResultUnknown);
}
auto ro_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(ro_mem_handle)};
if (ro_mem.IsNull()) {
LOG_ERROR(Service_JIT, "ro_mem is null for handle=0x{:08X}", ro_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
if (rx_mem.IsNull()) {
LOG_ERROR(Service_JIT, "ro_mem is null");
R_THROW(ResultUnknown);
}
CodeMemory rx, ro;
Result res;
res = rx.Initialize(*process, *rx_mem, parameters.rx_size,
Kernel::Svc::MemoryPermission::ReadExecute, generate_random);
if (R_FAILED(res)) {
LOG_ERROR(Service_JIT, "rx_mem could not be mapped for handle=0x{:08X}", rx_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
return;
}
R_TRY(rx.Initialize(*process, *rx_mem, rx_size, Kernel::Svc::MemoryPermission::ReadExecute,
generate_random));
R_TRY(ro.Initialize(*process, *ro_mem, ro_size, Kernel::Svc::MemoryPermission::Read,
generate_random));
res = ro.Initialize(*process, *ro_mem, parameters.ro_size,
Kernel::Svc::MemoryPermission::Read, generate_random);
if (R_FAILED(res)) {
LOG_ERROR(Service_JIT, "ro_mem could not be mapped for handle=0x{:08X}", ro_mem_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IJitEnvironment>(system, std::move(process), std::move(rx),
std::move(ro));
*out_jit_environment = std::make_shared<IJitEnvironment>(system, std::move(process),
std::move(rx), std::move(ro));
R_SUCCEED();
}
private:
+1 -1
View File
@@ -301,7 +301,7 @@ Result NfcInterface::TranslateResultToServiceError(Result result) const {
return result;
}
if (result.module != ErrorModule::NFC) {
if (result.GetModule() != ErrorModule::NFC) {
return result;
}
+1 -1
View File
@@ -6,7 +6,7 @@
#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/glue/glue_manager.h"
#include "core/hle/service/ipc_helpers.h"
+40 -152
View File
@@ -6,13 +6,13 @@
#include "common/scope_exit.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ro/ro.h"
#include "core/hle/service/ro/ro_nro_utils.h"
#include "core/hle/service/ro/ro_results.h"
#include "core/hle/service/ro/ro_types.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
namespace Service::RO {
@@ -500,46 +500,65 @@ private:
}
};
class RoInterface {
class RoInterface : public ServiceFramework<RoInterface> {
public:
explicit RoInterface(std::shared_ptr<RoContext> ro, NrrKind nrr_kind)
: m_ro(ro), m_context_id(InvalidContextId), m_nrr_kind(nrr_kind) {}
explicit RoInterface(Core::System& system_, const char* name_, std::shared_ptr<RoContext> ro,
NrrKind nrr_kind)
: ServiceFramework{system_, name_}, m_ro(ro), m_context_id(InvalidContextId),
m_nrr_kind(nrr_kind) {
// clang-format off
static const FunctionInfo functions[] = {
{0, C<&RoInterface::MapManualLoadModuleMemory>, "MapManualLoadModuleMemory"},
{1, C<&RoInterface::UnmapManualLoadModuleMemory>, "UnmapManualLoadModuleMemory"},
{2, C<&RoInterface::RegisterModuleInfo>, "RegisterModuleInfo"},
{3, C<&RoInterface::UnregisterModuleInfo>, "UnregisterModuleInfo"},
{4, C<&RoInterface::RegisterProcessHandle>, "RegisterProcessHandle"},
{10, C<&RoInterface::RegisterProcessModuleInfo>, "RegisterProcessModuleInfo"},
};
// clang-format on
RegisterHandlers(functions);
}
~RoInterface() {
m_ro->UnregisterProcess(m_context_id);
}
Result MapManualLoadModuleMemory(u64* out_load_address, u64 client_pid, u64 nro_address,
u64 nro_size, u64 bss_address, u64 bss_size) {
R_TRY(m_ro->ValidateProcess(m_context_id, client_pid));
R_RETURN(m_ro->MapManualLoadModuleMemory(out_load_address, m_context_id, nro_address,
Result MapManualLoadModuleMemory(Out<u64> out_load_address, ClientProcessId client_pid,
u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) {
R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
R_RETURN(m_ro->MapManualLoadModuleMemory(out_load_address.Get(), m_context_id, nro_address,
nro_size, bss_address, bss_size));
}
Result UnmapManualLoadModuleMemory(u64 client_pid, u64 nro_address) {
R_TRY(m_ro->ValidateProcess(m_context_id, client_pid));
Result UnmapManualLoadModuleMemory(ClientProcessId client_pid, u64 nro_address) {
R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
R_RETURN(m_ro->UnmapManualLoadModuleMemory(m_context_id, nro_address));
}
Result RegisterModuleInfo(u64 client_pid, u64 nrr_address, u64 nrr_size) {
R_TRY(m_ro->ValidateProcess(m_context_id, client_pid));
Result RegisterModuleInfo(ClientProcessId client_pid, u64 nrr_address, u64 nrr_size) {
R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
R_RETURN(
m_ro->RegisterModuleInfo(m_context_id, nrr_address, nrr_size, NrrKind::User, true));
}
Result UnregisterModuleInfo(u64 client_pid, u64 nrr_address) {
R_TRY(m_ro->ValidateProcess(m_context_id, client_pid));
Result UnregisterModuleInfo(ClientProcessId client_pid, u64 nrr_address) {
R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
R_RETURN(m_ro->UnregisterModuleInfo(m_context_id, nrr_address));
}
Result RegisterProcessHandle(u64 client_pid, Kernel::KProcess* process) {
Result RegisterProcessHandle(ClientProcessId client_pid,
InCopyHandle<Kernel::KProcess>& process) {
// Register the process.
R_RETURN(m_ro->RegisterProcess(std::addressof(m_context_id), process, client_pid));
R_RETURN(m_ro->RegisterProcess(std::addressof(m_context_id), process.GetPointerUnsafe(),
*client_pid));
}
Result RegisterProcessModuleInfo(u64 client_pid, u64 nrr_address, u64 nrr_size,
Kernel::KProcess* process) {
Result RegisterProcessModuleInfo(ClientProcessId client_pid, u64 nrr_address, u64 nrr_size,
InCopyHandle<Kernel::KProcess>& process) {
// Validate the process.
R_TRY(m_ro->ValidateProcess(m_context_id, client_pid));
R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid));
// Register the module.
R_RETURN(m_ro->RegisterModuleInfo(m_context_id, nrr_address, nrr_size, m_nrr_kind,
@@ -552,137 +571,6 @@ private:
NrrKind m_nrr_kind{};
};
class IRoInterface : public ServiceFramework<IRoInterface> {
public:
explicit IRoInterface(Core::System& system_, const char* name_, std::shared_ptr<RoContext> ro,
NrrKind nrr_kind)
: ServiceFramework{system_, name_}, interface {
ro, nrr_kind
} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IRoInterface::MapManualLoadModuleMemory, "MapManualLoadModuleMemory"},
{1, &IRoInterface::UnmapManualLoadModuleMemory, "UnmapManualLoadModuleMemory"},
{2, &IRoInterface::RegisterModuleInfo, "RegisterModuleInfo"},
{3, &IRoInterface::UnregisterModuleInfo, "UnregisterModuleInfo"},
{4, &IRoInterface::RegisterProcessHandle, "RegisterProcessHandle"},
{10, &IRoInterface::RegisterProcessModuleInfo, "RegisterProcessModuleInfo"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void MapManualLoadModuleMemory(HLERequestContext& ctx) {
LOG_DEBUG(Service_LDR, "(called)");
struct InputParameters {
u64 client_pid;
u64 nro_address;
u64 nro_size;
u64 bss_address;
u64 bss_size;
};
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<InputParameters>();
u64 load_address = 0;
auto result = interface.MapManualLoadModuleMemory(&load_address, ctx.GetPID(),
params.nro_address, params.nro_size,
params.bss_address, params.bss_size);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(result);
rb.Push(load_address);
}
void UnmapManualLoadModuleMemory(HLERequestContext& ctx) {
LOG_DEBUG(Service_LDR, "(called)");
struct InputParameters {
u64 client_pid;
u64 nro_address;
};
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<InputParameters>();
auto result = interface.UnmapManualLoadModuleMemory(ctx.GetPID(), params.nro_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void RegisterModuleInfo(HLERequestContext& ctx) {
LOG_DEBUG(Service_LDR, "(called)");
struct InputParameters {
u64 client_pid;
u64 nrr_address;
u64 nrr_size;
};
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<InputParameters>();
auto result =
interface.RegisterModuleInfo(ctx.GetPID(), params.nrr_address, params.nrr_size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void UnregisterModuleInfo(HLERequestContext& ctx) {
LOG_DEBUG(Service_LDR, "(called)");
struct InputParameters {
u64 client_pid;
u64 nrr_address;
};
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<InputParameters>();
auto result = interface.UnregisterModuleInfo(ctx.GetPID(), params.nrr_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void RegisterProcessHandle(HLERequestContext& ctx) {
LOG_DEBUG(Service_LDR, "(called)");
auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0));
auto client_pid = ctx.GetPID();
auto result = interface.RegisterProcessHandle(client_pid, process.GetPointerUnsafe());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void RegisterProcessModuleInfo(HLERequestContext& ctx) {
LOG_DEBUG(Service_LDR, "(called)");
struct InputParameters {
u64 client_pid;
u64 nrr_address;
u64 nrr_size;
};
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<InputParameters>();
auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0));
auto client_pid = ctx.GetPID();
auto result = interface.RegisterProcessModuleInfo(
client_pid, params.nrr_address, params.nrr_size, process.GetPointerUnsafe());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
RoInterface interface;
};
} // namespace
void LoopProcess(Core::System& system) {
@@ -691,11 +579,11 @@ void LoopProcess(Core::System& system) {
auto ro = std::make_shared<RoContext>();
const auto RoInterfaceFactoryForUser = [&, ro] {
return std::make_shared<IRoInterface>(system, "ldr:ro", ro, NrrKind::User);
return std::make_shared<RoInterface>(system, "ldr:ro", ro, NrrKind::User);
};
const auto RoInterfaceFactoryForJitPlugin = [&, ro] {
return std::make_shared<IRoInterface>(system, "ro:1", ro, NrrKind::JitPlugin);
return std::make_shared<RoInterface>(system, "ro:1", ro, NrrKind::JitPlugin);
};
server_manager->RegisterNamedService("ldr:ro", std::move(RoInterfaceFactoryForUser));
+16
View File
@@ -206,6 +206,22 @@ protected:
RegisterHandlersBaseTipc(functions, n);
}
protected:
template <bool Domain, auto F>
void CmifReplyWrap(HLERequestContext& ctx);
/**
* Wraps the template pointer-to-member function for use in a domain session.
*/
template <auto F>
static constexpr HandlerFnP<Self> D = &Self::template CmifReplyWrap<true, F>;
/**
* Wraps the template pointer-to-member function for use in a non-domain session.
*/
template <auto F>
static constexpr HandlerFnP<Self> C = &Self::template CmifReplyWrap<false, F>;
private:
/**
* This function is used to allow invocation of pointers to handlers stored in the base class
@@ -67,13 +67,13 @@ Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System&
const auto ver_file = romfs->GetFile("file");
if (ver_file == nullptr) {
return early_exit_failure("The system version archive didn't contain the file 'file'.",
FileSys::ERROR_INVALID_ARGUMENT);
FileSys::ResultInvalidArgument);
}
auto data = ver_file->ReadAllBytes();
if (data.size() != sizeof(FirmwareVersionFormat)) {
return early_exit_failure("The system version file 'file' was not the correct size.",
FileSys::ERROR_OUT_OF_BOUNDS);
FileSys::ResultOutOfRange);
}
std::memcpy(&out_firmware, data.data(), sizeof(FirmwareVersionFormat));