code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/crypto/key_manager.h"
#include "core/hle/service/es/es.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"

namespace Service::ES {

constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::ETicket, 2};
constexpr Result ERROR_INVALID_RIGHTS_ID{ErrorModule::ETicket, 3};

class ETicket final : public ServiceFramework<ETicket> {
public:
    explicit ETicket(Core::System& system_) : ServiceFramework{system_, "es"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {1, &ETicket::ImportTicket, "ImportTicket"},
            {2, nullptr, "ImportTicketCertificateSet"},
            {3, nullptr, "DeleteTicket"},
            {4, nullptr, "DeletePersonalizedTicket"},
            {5, nullptr, "DeleteAllCommonTicket"},
            {6, nullptr, "DeleteAllPersonalizedTicket"},
            {7, nullptr, "DeleteAllPersonalizedTicketEx"},
            {8, &ETicket::GetTitleKey, "GetTitleKey"},
            {9, &ETicket::CountCommonTicket, "CountCommonTicket"},
            {10, &ETicket::CountPersonalizedTicket, "CountPersonalizedTicket"},
            {11, &ETicket::ListCommonTicketRightsIds, "ListCommonTicketRightsIds"},
            {12, &ETicket::ListPersonalizedTicketRightsIds, "ListPersonalizedTicketRightsIds"},
            {13, nullptr, "ListMissingPersonalizedTicket"},
            {14, &ETicket::GetCommonTicketSize, "GetCommonTicketSize"},
            {15, &ETicket::GetPersonalizedTicketSize, "GetPersonalizedTicketSize"},
            {16, &ETicket::GetCommonTicketData, "GetCommonTicketData"},
            {17, &ETicket::GetPersonalizedTicketData, "GetPersonalizedTicketData"},
            {18, nullptr, "OwnTicket"},
            {19, nullptr, "GetTicketInfo"},
            {20, nullptr, "ListLightTicketInfo"},
            {21, nullptr, "SignData"},
            {22, nullptr, "GetCommonTicketAndCertificateSize"},
            {23, nullptr, "GetCommonTicketAndCertificateData"},
            {24, nullptr, "ImportPrepurchaseRecord"},
            {25, nullptr, "DeletePrepurchaseRecord"},
            {26, nullptr, "DeleteAllPrepurchaseRecord"},
            {27, nullptr, "CountPrepurchaseRecord"},
            {28, nullptr, "ListPrepurchaseRecordRightsIds"},
            {29, nullptr, "ListPrepurchaseRecordInfo"},
            {30, nullptr, "CountTicket"},
            {31, nullptr, "ListTicketRightsIds"},
            {32, nullptr, "CountPrepurchaseRecordEx"},
            {33, nullptr, "ListPrepurchaseRecordRightsIdsEx"},
            {34, nullptr, "GetEncryptedTicketSize"},
            {35, nullptr, "GetEncryptedTicketData"},
            {36, nullptr, "DeleteAllInactiveELicenseRequiredPersonalizedTicket"},
            {37, nullptr, "OwnTicket2"},
            {38, nullptr, "OwnTicket3"},
            {39, nullptr, "DeleteAllInactivePersonalizedTicket"},
            {40, nullptr, "DeletePrepurchaseRecordByNintendoAccountId"},
            {501, nullptr, "Unknown501"},
            {502, nullptr, "Unknown502"},
            {503, nullptr, "GetTitleKey"},
            {504, nullptr, "Unknown504"},
            {508, nullptr, "Unknown508"},
            {509, nullptr, "Unknown509"},
            {510, nullptr, "Unknown510"},
            {511, nullptr, "Unknown511"},
            {1001, nullptr, "Unknown1001"},
            {1002, nullptr, "Unknown1001"},
            {1003, nullptr, "Unknown1003"},
            {1004, nullptr, "Unknown1004"},
            {1005, nullptr, "Unknown1005"},
            {1006, nullptr, "Unknown1006"},
            {1007, nullptr, "Unknown1007"},
            {1009, nullptr, "Unknown1009"},
            {1010, nullptr, "Unknown1010"},
            {1011, nullptr, "Unknown1011"},
            {1012, nullptr, "Unknown1012"},
            {1013, nullptr, "Unknown1013"},
            {1014, nullptr, "Unknown1014"},
            {1015, nullptr, "Unknown1015"},
            {1016, nullptr, "Unknown1016"},
            {1017, nullptr, "Unknown1017"},
            {1018, nullptr, "Unknown1018"},
            {1019, nullptr, "Unknown1019"},
            {1020, nullptr, "Unknown1020"},
            {1021, nullptr, "Unknown1021"},
            {1501, nullptr, "Unknown1501"},
            {1502, nullptr, "Unknown1502"},
            {1503, nullptr, "Unknown1503"},
            {1504, nullptr, "Unknown1504"},
            {1505, nullptr, "Unknown1505"},
            {1506, nullptr, "Unknown1506"},
            {2000, nullptr, "Unknown2000"},
            {2001, nullptr, "Unknown2001"},
            {2002, nullptr, "Unknown2002"},
            {2003, nullptr, "Unknown2003"},
            {2100, nullptr, "Unknown2100"},
            {2501, nullptr, "Unknown2501"},
            {2502, nullptr, "Unknown2502"},
            {2601, nullptr, "Unknown2601"},
            {3001, nullptr, "Unknown3001"},
            {3002, nullptr, "Unknown3002"},
        };
        // clang-format on
        RegisterHandlers(functions);

        keys.PopulateTickets();
        keys.SynthesizeTickets();
    }

private:
    bool CheckRightsId(HLERequestContext& ctx, const u128& rights_id) {
        if (rights_id == u128{}) {
            LOG_ERROR(Service_ETicket, "The rights ID was invalid!");
            IPC::ResponseBuilder rb{ctx, 2};
            rb.Push(ERROR_INVALID_RIGHTS_ID);
            return false;
        }

        return true;
    }

    void ImportTicket(HLERequestContext& ctx) {
        const auto raw_ticket = ctx.ReadBuffer();
        [[maybe_unused]] const auto cert = ctx.ReadBuffer(1);

        if (raw_ticket.size() < sizeof(Core::Crypto::Ticket)) {
            LOG_ERROR(Service_ETicket, "The input buffer is not large enough!");
            IPC::ResponseBuilder rb{ctx, 2};
            rb.Push(ERROR_INVALID_ARGUMENT);
            return;
        }

        Core::Crypto::Ticket ticket = Core::Crypto::Ticket::Read(raw_ticket);
        if (!keys.AddTicket(ticket)) {
            LOG_ERROR(Service_ETicket, "The ticket could not be imported!");
            IPC::ResponseBuilder rb{ctx, 2};
            rb.Push(ERROR_INVALID_ARGUMENT);
            return;
        }

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
    }

    void GetTitleKey(HLERequestContext& ctx) {
        IPC::RequestParser rp{ctx};
        const auto rights_id = rp.PopRaw<u128>();

        LOG_DEBUG(Service_ETicket, "called, rights_id={:016X}{:016X}", rights_id[1], rights_id[0]);

        if (!CheckRightsId(ctx, rights_id))
            return;

        const auto key =
            keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]);

        if (key == Core::Crypto::Key128{}) {
            LOG_ERROR(Service_ETicket,
                      "The titlekey doesn't exist in the KeyManager or the rights ID was invalid!");
            IPC::ResponseBuilder rb{ctx, 2};
            rb.Push(ERROR_INVALID_RIGHTS_ID);
            return;
        }

        ctx.WriteBuffer(key);

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
    }

    void CountCommonTicket(HLERequestContext& ctx) {
        LOG_DEBUG(Service_ETicket, "called");

        const u32 count = static_cast<u32>(keys.GetCommonTickets().size());

        IPC::ResponseBuilder rb{ctx, 3};
        rb.Push(ResultSuccess);
        rb.Push<u32>(count);
    }

    void CountPersonalizedTicket(HLERequestContext& ctx) {
        LOG_DEBUG(Service_ETicket, "called");

        const u32 count = static_cast<u32>(keys.GetPersonalizedTickets().size());

        IPC::ResponseBuilder rb{ctx, 3};
        rb.Push(ResultSuccess);
        rb.Push<u32>(count);
    }

    void ListCommonTicketRightsIds(HLERequestContext& ctx) {
        size_t out_entries = 0;
        if (!keys.GetCommonTickets().empty()) {
            out_entries = ctx.GetWriteBufferNumElements<u128>();
        }
        LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries);

        keys.PopulateTickets();
        const auto tickets = keys.GetCommonTickets();
        std::vector<u128> ids;
        std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids),
                       [](const auto& pair) { return pair.first; });

        out_entries = std::min(ids.size(), out_entries);
        ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128));

        IPC::ResponseBuilder rb{ctx, 3};
        rb.Push(ResultSuccess);
        rb.Push<u32>(static_cast<u32>(out_entries));
    }

    void ListPersonalizedTicketRightsIds(HLERequestContext& ctx) {
        size_t out_entries = 0;
        if (!keys.GetPersonalizedTickets().empty()) {
            out_entries = ctx.GetWriteBufferNumElements<u128>();
        }

        LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries);

        keys.PopulateTickets();
        const auto tickets = keys.GetPersonalizedTickets();
        std::vector<u128> ids;
        std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids),
                       [](const auto& pair) { return pair.first; });

        out_entries = std::min(ids.size(), out_entries);
        ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128));

        IPC::ResponseBuilder rb{ctx, 3};
        rb.Push(ResultSuccess);
        rb.Push<u32>(static_cast<u32>(out_entries));
    }

    void GetCommonTicketSize(HLERequestContext& ctx) {
        IPC::RequestParser rp{ctx};
        const auto rights_id = rp.PopRaw<u128>();

        LOG_DEBUG(Service_ETicket, "called, rights_id={:016X}{:016X}", rights_id[1], rights_id[0]);

        if (!CheckRightsId(ctx, rights_id))
            return;

        const auto ticket = keys.GetCommonTickets().at(rights_id);

        IPC::ResponseBuilder rb{ctx, 4};
        rb.Push(ResultSuccess);
        rb.Push<u64>(ticket.GetSize());
    }

    void GetPersonalizedTicketSize(HLERequestContext& ctx) {
        IPC::RequestParser rp{ctx};
        const auto rights_id = rp.PopRaw<u128>();

        LOG_DEBUG(Service_ETicket, "called, rights_id={:016X}{:016X}", rights_id[1], rights_id[0]);

        if (!CheckRightsId(ctx, rights_id))
            return;

        const auto ticket = keys.GetPersonalizedTickets().at(rights_id);

        IPC::ResponseBuilder rb{ctx, 4};
        rb.Push(ResultSuccess);
        rb.Push<u64>(ticket.GetSize());
    }

    void GetCommonTicketData(HLERequestContext& ctx) {
        IPC::RequestParser rp{ctx};
        const auto rights_id = rp.PopRaw<u128>();

        LOG_DEBUG(Service_ETicket, "called, rights_id={:016X}{:016X}", rights_id[1], rights_id[0]);

        if (!CheckRightsId(ctx, rights_id))
            return;

        const auto ticket = keys.GetCommonTickets().at(rights_id);

        const auto write_size = std::min<u64>(ticket.GetSize(), ctx.GetWriteBufferSize());
        ctx.WriteBuffer(&ticket, write_size);

        IPC::ResponseBuilder rb{ctx, 4};
        rb.Push(ResultSuccess);
        rb.Push<u64>(write_size);
    }

    void GetPersonalizedTicketData(HLERequestContext& ctx) {
        IPC::RequestParser rp{ctx};
        const auto rights_id = rp.PopRaw<u128>();

        LOG_DEBUG(Service_ETicket, "called, rights_id={:016X}{:016X}", rights_id[1], rights_id[0]);

        if (!CheckRightsId(ctx, rights_id))
            return;

        const auto ticket = keys.GetPersonalizedTickets().at(rights_id);

        const auto write_size = std::min<u64>(ticket.GetSize(), ctx.GetWriteBufferSize());
        ctx.WriteBuffer(&ticket, write_size);

        IPC::ResponseBuilder rb{ctx, 4};
        rb.Push(ResultSuccess);
        rb.Push<u64>(write_size);
    }

    Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
};

void LoopProcess(Core::System& system) {
    auto server_manager = std::make_unique<ServerManager>(system);

    server_manager->RegisterNamedService("es", std::make_shared<ETicket>(system));
    ServerManager::RunServer(std::move(server_manager));
}

} // namespace Service::ES


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

namespace Core {
class System;
}

namespace Service::ES {

void LoopProcess(Core::System& system);

} // namespace Service::ES

repo_name: litucks/torzu

license: mit


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <memory>

#include "core/hle/service/eupld/eupld.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"

namespace Service::EUPLD {

class ErrorUploadContext final : public ServiceFramework<ErrorUploadContext> {
public:
    explicit ErrorUploadContext(Core::System& system_) : ServiceFramework{system_, "eupld:c"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, nullptr, "SetUrl"},
            {1, nullptr, "ImportCrt"},
            {2, nullptr, "ImportPki"},
            {3, nullptr, "SetAutoUpload"},
            {4, nullptr, "GetAutoUpload"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }
};

class ErrorUploadRequest final : public ServiceFramework<ErrorUploadRequest> {
public:
    explicit ErrorUploadRequest(Core::System& system_) : ServiceFramework{system_, "eupld:r"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, nullptr, "Initialize"},
            {1, nullptr, "UploadAll"},
            {2, nullptr, "UploadSelected"},
            {3, nullptr, "GetUploadStatus"},
            {4, nullptr, "CancelUpload"},
            {5, nullptr, "GetResult"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }
};

void LoopProcess(Core::System& system) {
    auto server_manager = std::make_unique<ServerManager>(system);

    server_manager->RegisterNamedService("eupld:c", std::make_shared<ErrorUploadContext>(system));
    server_manager->RegisterNamedService("eupld:r", std::make_shared<ErrorUploadRequest>(system));
    ServerManager::RunServer(std::move(server_manager));
}

} // namespace Service::EUPLD


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

namespace Core {
class System;
}

namespace Service::EUPLD {

void LoopProcess(Core::System& system);

} // namespace Service::EUPLD


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <array>
#include <cstring>
#include <ctime>
#include <fmt/chrono.h>
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/hle/service/fatal/fatal.h"
#include "core/hle/service/fatal/fatal_p.h"
#include "core/hle/service/fatal/fatal_u.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/server_manager.h"
#include "core/reporter.h"

namespace Service::Fatal {

Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_,
                             const char* name)
    : ServiceFramework{system_, name}, module{std::move(module_)} {}

Module::Interface::~Interface() = default;

struct FatalInfo {
    enum class Architecture : s32 {
        AArch64,
        AArch32,
    };

    const char* ArchAsString() const {
        return arch == Architecture::AArch64 ? "AArch64" : "AArch32";
    }

    std::array<u64_le, 31> registers{};
    u64_le sp{};
    u64_le pc{};
    u64_le pstate{};
    u64_le afsr0{};
    u64_le afsr1{};
    u64_le esr{};
    u64_le far{};

    std::array<u64_le, 32> backtrace{};
    u64_le program_entry_point{};

    // Bit flags that indicate which registers have been set with values
    // for this context. The service itself uses these to determine which
    // registers to specifically print out.
    u64_le set_flags{};

    u32_le backtrace_size{};
    Architecture arch{};
    u32_le unk10{}; // TODO(ogniK): Is this even used or is it just padding?
};
static_assert(sizeof(FatalInfo) == 0x250, "FatalInfo is an invalid size");

enum class FatalType : u32 {
    ErrorReportAndScreen = 0,
    ErrorReport = 1,
    ErrorScreen = 2,
};

static void GenerateErrorReport(Core::System& system, Result error_code, const FatalInfo& info) {
    const auto title_id = system.GetApplicationProcessProgramID();
    std::string crash_report = fmt::format(
        "Yuzu {}-{} crash report\n"
        "Title ID:                        {:016x}\n"
        "Result:                          0x{:X} ({:04}-{:04d})\n"
        "Set flags:                       0x{:16X}\n"
        "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.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++) {
            crash_report +=
                fmt::format("    X[{:02d}]:                       {:016x}\n", i, info.registers[i]);
        }
        crash_report += fmt::format("    SP:                          {:016x}\n", info.sp);
        crash_report += fmt::format("    PC:                          {:016x}\n", info.pc);
        crash_report += fmt::format("    PSTATE:                      {:016x}\n", info.pstate);
        crash_report += fmt::format("    AFSR0:                       {:016x}\n", info.afsr0);
        crash_report += fmt::format("    AFSR1:                       {:016x}\n", info.afsr1);
        crash_report += fmt::format("    ESR:                         {:016x}\n", info.esr);
        crash_report += fmt::format("    FAR:                         {:016x}\n", info.far);
        crash_report += "\nBacktrace:\n";
        for (u32 i = 0; i < std::min<u32>(info.backtrace_size, 32); i++) {
            crash_report +=
                fmt::format("    Backtrace[{:02d}]:               {:016x}\n", i, info.backtrace[i]);
        }

        crash_report += fmt::format("Architecture:                    {}\n", info.ArchAsString());
        crash_report += fmt::format("Unknown 10:                      0x{:016x}\n", info.unk10);
    }

    LOG_ERROR(Service_Fatal, "{}", crash_report);

    system.GetReporter().SaveCrashReport(
        title_id, error_code, info.set_flags, info.program_entry_point, info.sp, info.pc,
        info.pstate, info.afsr0, info.afsr1, info.esr, info.far, info.registers, info.backtrace,
        info.backtrace_size, info.ArchAsString(), info.unk10);
}

static void ThrowFatalError(Core::System& system, Result error_code, FatalType fatal_type,
                            const FatalInfo& info) {
    LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code 0x{:X}", fatal_type,
              error_code.raw);

    switch (fatal_type) {
    case FatalType::ErrorReportAndScreen:
        GenerateErrorReport(system, error_code, info);
        [[fallthrough]];
    case FatalType::ErrorScreen:
        // Since we have no fatal:u error screen. We should just kill execution instead
        ASSERT(false);
        break;
        // Should not throw a fatal screen but should generate an error report
    case FatalType::ErrorReport:
        GenerateErrorReport(system, error_code, info);
        break;
    }
}

void Module::Interface::ThrowFatal(HLERequestContext& ctx) {
    LOG_ERROR(Service_Fatal, "called");
    IPC::RequestParser rp{ctx};
    const auto error_code = rp.Pop<Result>();

    ThrowFatalError(system, error_code, FatalType::ErrorScreen, {});
    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void Module::Interface::ThrowFatalWithPolicy(HLERequestContext& ctx) {
    LOG_ERROR(Service_Fatal, "called");
    IPC::RequestParser rp(ctx);
    const auto error_code = rp.Pop<Result>();
    const auto fatal_type = rp.PopEnum<FatalType>();

    ThrowFatalError(system, error_code, fatal_type,
                    {}); // No info is passed with ThrowFatalWithPolicy
    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void Module::Interface::ThrowFatalWithCpuContext(HLERequestContext& ctx) {
    LOG_ERROR(Service_Fatal, "called");
    IPC::RequestParser rp(ctx);
    const auto error_code = rp.Pop<Result>();
    const auto fatal_type = rp.PopEnum<FatalType>();
    const auto fatal_info = ctx.ReadBuffer();
    FatalInfo info{};

    ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!");
    std::memcpy(&info, fatal_info.data(), sizeof(FatalInfo));

    ThrowFatalError(system, error_code, fatal_type, info);
    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void LoopProcess(Core::System& system) {
    auto server_manager = std::make_unique<ServerManager>(system);
    auto module = std::make_shared<Module>();

    server_manager->RegisterNamedService("fatal:p", std::make_shared<Fatal_P>(module, system));
    server_manager->RegisterNamedService("fatal:u", std::make_shared<Fatal_U>(module, system));
    ServerManager::RunServer(std::move(server_manager));
}

} // namespace Service::Fatal


code:
// 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::Fatal {

class Module final {
public:
    class Interface : public ServiceFramework<Interface> {
    public:
        explicit Interface(std::shared_ptr<Module> module_, Core::System& system_,
                           const char* name);
        ~Interface() override;

        void ThrowFatal(HLERequestContext& ctx);
        void ThrowFatalWithPolicy(HLERequestContext& ctx);
        void ThrowFatalWithCpuContext(HLERequestContext& ctx);

    protected:
        std::shared_ptr<Module> module;
    };
};

void LoopProcess(Core::System& system);

} // namespace Service::Fatal


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/fatal/fatal_p.h"

namespace Service::Fatal {

Fatal_P::Fatal_P(std::shared_ptr<Module> module_, Core::System& system_)
    : Interface(std::move(module_), system_, "fatal:p") {
    static const FunctionInfo functions[] = {
        {0, nullptr, "GetFatalEvent"},
        {10, nullptr, "GetFatalContext"},
    };
    RegisterHandlers(functions);
}

Fatal_P::~Fatal_P() = default;

} // namespace Service::Fatal


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/fatal/fatal.h"

namespace Service::Fatal {

class Fatal_P final : public Module::Interface {
public:
    explicit Fatal_P(std::shared_ptr<Module> module_, Core::System& system_);
    ~Fatal_P() override;
};

} // namespace Service::Fatal


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/fatal/fatal_u.h"

namespace Service::Fatal {

Fatal_U::Fatal_U(std::shared_ptr<Module> module_, Core::System& system_)
    : Interface(std::move(module_), system_, "fatal:u") {
    static const FunctionInfo functions[] = {
        {0, &Fatal_U::ThrowFatal, "ThrowFatal"},
        {1, &Fatal_U::ThrowFatalWithPolicy, "ThrowFatalWithPolicy"},
        {2, &Fatal_U::ThrowFatalWithCpuContext, "ThrowFatalWithCpuContext"},
    };
    RegisterHandlers(functions);
}

Fatal_U::~Fatal_U() = default;

} // namespace Service::Fatal


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/fatal/fatal.h"

namespace Service::Fatal {

class Fatal_U final : public Module::Interface {
public:
    explicit Fatal_U(std::shared_ptr<Module> module_, Core::System& system_);
    ~Fatal_U() override;
};

} // namespace Service::Fatal


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <memory>

#include "core/hle/service/fgm/fgm.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"

namespace Service::FGM {

class IRequest final : public ServiceFramework<IRequest> {
public:
    explicit IRequest(Core::System& system_) : ServiceFramework{system_, "IRequest"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, nullptr, "Initialize"},
            {1, nullptr, "Set"},
            {2, nullptr, "Get"},
            {3, nullptr, "Cancel"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }
};

class FGM final : public ServiceFramework<FGM> {
public:
    explicit FGM(Core::System& system_, const char* name) : ServiceFramework{system_, name} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, &FGM::Initialize, "Initialize"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }

private:
    void Initialize(HLERequestContext& ctx) {
        LOG_DEBUG(Service_FGM, "called");

        IPC::ResponseBuilder rb{ctx, 2, 0, 1};
        rb.Push(ResultSuccess);
        rb.PushIpcInterface<IRequest>(system);
    }
};

class FGM_DBG final : public ServiceFramework<FGM_DBG> {
public:
    explicit FGM_DBG(Core::System& system_) : ServiceFramework{system_, "fgm:dbg"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, nullptr, "Initialize"},
            {1, nullptr, "Read"},
            {2, nullptr, "Cancel"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }
};

void LoopProcess(Core::System& system) {
    auto server_manager = std::make_unique<ServerManager>(system);

    server_manager->RegisterNamedService("fgm", std::make_shared<FGM>(system, "fgm"));
    server_manager->RegisterNamedService("fgm:0", std::make_shared<FGM>(system, "fgm:0"));
    server_manager->RegisterNamedService("fgm:9", std::make_shared<FGM>(system, "fgm:9"));
    server_manager->RegisterNamedService("fgm:dbg", std::make_shared<FGM_DBG>(system));
    ServerManager::RunServer(std::move(server_manager));
}

} // namespace Service::FGM


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

namespace Core {
class System;
}

namespace Service::FGM {

void LoopProcess(Core::System& system);

} // namespace Service::FGM


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <utility>

#include "common/assert.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/file_sys/bis_factory.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/errors.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/vfs.h"
#include "core/file_sys/vfs/vfs_offset.h"
#include "core/hle/service/filesystem/filesystem.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"
#include "core/loader/loader.h"

namespace Service::FileSystem {

static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
                                                       std::string_view dir_name_) {
    std::string dir_name(Common::FS::SanitizePath(dir_name_));
    if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\")
        return base;

    return base->GetDirectoryRelative(dir_name);
}

VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_)
    : backing(std::move(backing_)) {}

VfsDirectoryServiceWrapper::~VfsDirectoryServiceWrapper() = default;

std::string VfsDirectoryServiceWrapper::GetName() const {
    return backing->GetName();
}

Result VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const {
    std::string path(Common::FS::SanitizePath(path_));
    auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
    if (dir == nullptr) {
        return FileSys::ResultPathNotFound;
    }

    FileSys::DirectoryEntryType entry_type{};
    if (GetEntryType(&entry_type, path) == ResultSuccess) {
        return FileSys::ResultPathAlreadyExists;
    }

    auto file = dir->CreateFile(Common::FS::GetFilename(path));
    if (file == nullptr) {
        // TODO(DarkLordZach): Find a better error code for this
        return ResultUnknown;
    }
    if (!file->Resize(size)) {
        // TODO(DarkLordZach): Find a better error code for this
        return ResultUnknown;
    }
    return ResultSuccess;
}

Result VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
    std::string path(Common::FS::SanitizePath(path_));
    if (path.empty()) {
        // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
        return ResultSuccess;
    }

    auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
    if (dir == nullptr || dir->GetFile(Common::FS::GetFilename(path)) == nullptr) {
        return FileSys::ResultPathNotFound;
    }
    if (!dir->DeleteFile(Common::FS::GetFilename(path))) {
        // TODO(DarkLordZach): Find a better error code for this
        return ResultUnknown;
    }

    return ResultSuccess;
}

Result VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
    std::string path(Common::FS::SanitizePath(path_));

    // NOTE: This is inaccurate behavior. CreateDirectory is not recursive.
    // CreateDirectory should return PathNotFound if the parent directory does not exist.
    // This is here temporarily in order to have UMM "work" in the meantime.
    // TODO (Morph): Remove this when a hardware test verifies the correct behavior.
    const auto components = Common::FS::SplitPathComponents(path);
    std::string relative_path;
    for (const auto& component : components) {
        relative_path = Common::FS::SanitizePath(fmt::format("{}/{}", relative_path, component));
        auto new_dir = backing->CreateSubdirectory(relative_path);
        if (new_dir == nullptr) {
            // TODO(DarkLordZach): Find a better error code for this
            return ResultUnknown;
        }
    }
    return ResultSuccess;
}

Result VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
    std::string path(Common::FS::SanitizePath(path_));
    auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
    if (!dir->DeleteSubdirectory(Common::FS::GetFilename(path))) {
        // TODO(DarkLordZach): Find a better error code for this
        return ResultUnknown;
    }
    return ResultSuccess;
}

Result VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
    std::string path(Common::FS::SanitizePath(path_));
    auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
    if (!dir->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path))) {
        // TODO(DarkLordZach): Find a better error code for this
        return ResultUnknown;
    }
    return ResultSuccess;
}

Result VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const {
    const std::string sanitized_path(Common::FS::SanitizePath(path));
    auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(sanitized_path));

    if (!dir->CleanSubdirectoryRecursive(Common::FS::GetFilename(sanitized_path))) {
        // TODO(DarkLordZach): Find a better error code for this
        return ResultUnknown;
    }

    return ResultSuccess;
}

Result VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
                                              const std::string& dest_path_) const {
    std::string src_path(Common::FS::SanitizePath(src_path_));
    std::string dest_path(Common::FS::SanitizePath(dest_path_));
    auto src = backing->GetFileRelative(src_path);
    auto dst = backing->GetFileRelative(dest_path);
    if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
        // Use more-optimized vfs implementation rename.
        if (src == nullptr) {
            return FileSys::ResultPathNotFound;
        }

        if (dst && Common::FS::Exists(dst->GetFullPath())) {
            LOG_ERROR(Service_FS, "File at new_path={} already exists", dst->GetFullPath());
            return FileSys::ResultPathAlreadyExists;
        }

        if (!src->Rename(Common::FS::GetFilename(dest_path))) {
            // TODO(DarkLordZach): Find a better error code for this
            return ResultUnknown;
        }
        return ResultSuccess;
    }

    // Move by hand -- TODO(DarkLordZach): Optimize
    auto c_res = CreateFile(dest_path, src->GetSize());
    if (c_res != ResultSuccess)
        return c_res;

    auto dest = backing->GetFileRelative(dest_path);
    ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found.");

    ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
               "Could not write all of the bytes but everything else has succeeded.");

    if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) {
        // TODO(DarkLordZach): Find a better error code for this
        return ResultUnknown;
    }

    return ResultSuccess;
}

Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
                                                   const std::string& dest_path_) const {
    std::string src_path(Common::FS::SanitizePath(src_path_));
    std::string dest_path(Common::FS::SanitizePath(dest_path_));
    auto src = GetDirectoryRelativeWrapped(backing, src_path);
    if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
        // Use more-optimized vfs implementation rename.
        if (src == nullptr)
            return FileSys::ResultPathNotFound;
        if (!src->Rename(Common::FS::GetFilename(dest_path))) {
            // TODO(DarkLordZach): Find a better error code for this
            return ResultUnknown;
        }
        return ResultSuccess;
    }

    // TODO(DarkLordZach): Implement renaming across the tree (move).
    ASSERT_MSG(false,
               "Could not rename directory with path \"{}\" to new path \"{}\" because parent dirs "
               "don't match -- UNIMPLEMENTED",
               src_path, dest_path);

    // TODO(DarkLordZach): Find a better error code for this
    return ResultUnknown;
}

Result VfsDirectoryServiceWrapper::OpenFile(FileSys::VirtualFile* out_file,
                                            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] == '\\')) {
        npath.remove_prefix(1);
    }

    auto file = backing->GetFileRelative(npath);
    if (file == nullptr) {
        return FileSys::ResultPathNotFound;
    }

    if (mode == FileSys::OpenMode::AllowAppend) {
        *out_file = std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize());
    } else {
        *out_file = file;
    }

    return ResultSuccess;
}

Result VfsDirectoryServiceWrapper::OpenDirectory(FileSys::VirtualDir* out_directory,
                                                 const std::string& path_) {
    std::string path(Common::FS::SanitizePath(path_));
    auto dir = GetDirectoryRelativeWrapped(backing, path);
    if (dir == nullptr) {
        // TODO(DarkLordZach): Find a better error code for this
        return FileSys::ResultPathNotFound;
    }
    *out_directory = dir;
    return ResultSuccess;
}

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::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::DirectoryEntryType::Directory;
        return ResultSuccess;
    }

    if (dir->GetFile(filename) != nullptr) {
        *out_entry_type = FileSys::DirectoryEntryType::File;
        return ResultSuccess;
    }

    if (dir->GetSubdirectory(filename) != nullptr) {
        *out_entry_type = FileSys::DirectoryEntryType::Directory;
        return ResultSuccess;
    }

    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::ResultPathNotFound;
    }

    FileSys::DirectoryEntryType entry_type;
    if (GetEntryType(&entry_type, path) != ResultSuccess) {
        return FileSys::ResultPathNotFound;
    }

    *out_file_time_stamp_raw = dir->GetFileTimeStamp(Common::FS::GetFilename(path));
    return ResultSuccess;
}

FileSystemController::FileSystemController(Core::System& system_) : system{system_} {}

FileSystemController::~FileSystemController() = default;

Result FileSystemController::RegisterProcess(
    ProcessId process_id, ProgramId program_id,
    std::shared_ptr<FileSys::RomFSFactory>&& romfs_factory) {
    std::scoped_lock lk{registration_lock};

    registrations.emplace(process_id, Registration{
                                          .program_id = program_id,
                                          .romfs_factory = std::move(romfs_factory),
                                          .save_data_factory = CreateSaveDataFactory(program_id),
                                      });

    LOG_DEBUG(Service_FS, "Registered for process {}", process_id);
    return ResultSuccess;
}

Result FileSystemController::OpenProcess(
    ProgramId* out_program_id, std::shared_ptr<SaveDataController>* out_save_data_controller,
    std::shared_ptr<RomFsController>* out_romfs_controller, ProcessId process_id) {
    std::scoped_lock lk{registration_lock};

    const auto it = registrations.find(process_id);
    if (it == registrations.end()) {
        return FileSys::ResultTargetNotFound;
    }

    *out_program_id = it->second.program_id;
    *out_save_data_controller =
        std::make_shared<SaveDataController>(system, it->second.save_data_factory);
    *out_romfs_controller =
        std::make_shared<RomFsController>(it->second.romfs_factory, it->second.program_id);
    return ResultSuccess;
}

void FileSystemController::SetPackedUpdate(ProcessId process_id, FileSys::VirtualFile update_raw) {
    LOG_TRACE(Service_FS, "Setting packed update for romfs");

    std::scoped_lock lk{registration_lock};
    const auto it = registrations.find(process_id);
    if (it == registrations.end()) {
        return;
    }

    it->second.romfs_factory->SetPackedUpdate(std::move(update_raw));
}

std::shared_ptr<SaveDataController> FileSystemController::OpenSaveDataController() {
    return std::make_shared<SaveDataController>(system, CreateSaveDataFactory(ProgramId{}));
}

std::shared_ptr<FileSys::SaveDataFactory> FileSystemController::CreateSaveDataFactory(
    ProgramId program_id) {
    using YuzuPath = Common::FS::YuzuPath;
    const auto rw_mode = FileSys::OpenMode::ReadWrite;

    auto vfs = system.GetFilesystem();
    const auto nand_directory =
        vfs->OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::NANDDir), rw_mode);
    return std::make_shared<FileSys::SaveDataFactory>(system, program_id,
                                                      std::move(nand_directory));
}

Result FileSystemController::OpenSDMC(FileSys::VirtualDir* out_sdmc) const {
    LOG_TRACE(Service_FS, "Opening SDMC");

    if (sdmc_factory == nullptr) {
        return FileSys::ResultPortSdCardNoDevice;
    }

    auto sdmc = sdmc_factory->Open();
    if (sdmc == nullptr) {
        return FileSys::ResultPortSdCardNoDevice;
    }

    *out_sdmc = sdmc;
    return ResultSuccess;
}

Result FileSystemController::OpenBISPartition(FileSys::VirtualDir* out_bis_partition,
                                              FileSys::BisPartitionId id) const {
    LOG_TRACE(Service_FS, "Opening BIS Partition with id={:08X}", id);

    if (bis_factory == nullptr) {
        return FileSys::ResultTargetNotFound;
    }

    auto part = bis_factory->OpenPartition(id);
    if (part == nullptr) {
        return FileSys::ResultInvalidArgument;
    }

    *out_bis_partition = part;
    return ResultSuccess;
}

Result FileSystemController::OpenBISPartitionStorage(
    FileSys::VirtualFile* out_bis_partition_storage, FileSys::BisPartitionId id) const {
    LOG_TRACE(Service_FS, "Opening BIS Partition Storage with id={:08X}", id);

    if (bis_factory == nullptr) {
        return FileSys::ResultTargetNotFound;
    }

    auto part = bis_factory->OpenPartitionStorage(id, system.GetFilesystem());
    if (part == nullptr) {
        return FileSys::ResultInvalidArgument;
    }

    *out_bis_partition_storage = part;
    return ResultSuccess;
}

u64 FileSystemController::GetFreeSpaceSize(FileSys::StorageId id) const {
    switch (id) {
    case FileSys::StorageId::None:
    case FileSys::StorageId::GameCard:
        return 0;
    case FileSys::StorageId::SdCard:
        if (sdmc_factory == nullptr)
            return 0;
        return sdmc_factory->GetSDMCFreeSpace();
    case FileSys::StorageId::Host:
        if (bis_factory == nullptr)
            return 0;
        return bis_factory->GetSystemNANDFreeSpace() + bis_factory->GetUserNANDFreeSpace();
    case FileSys::StorageId::NandSystem:
        if (bis_factory == nullptr)
            return 0;
        return bis_factory->GetSystemNANDFreeSpace();
    case FileSys::StorageId::NandUser:
        if (bis_factory == nullptr)
            return 0;
        return bis_factory->GetUserNANDFreeSpace();
    }

    return 0;
}

u64 FileSystemController::GetTotalSpaceSize(FileSys::StorageId id) const {
    switch (id) {
    case FileSys::StorageId::None:
    case FileSys::StorageId::GameCard:
        return 0;
    case FileSys::StorageId::SdCard:
        if (sdmc_factory == nullptr)
            return 0;
        return sdmc_factory->GetSDMCTotalSpace();
    case FileSys::StorageId::Host:
        if (bis_factory == nullptr)
            return 0;
        return bis_factory->GetFullNANDTotalSpace();
    case FileSys::StorageId::NandSystem:
        if (bis_factory == nullptr)
            return 0;
        return bis_factory->GetSystemNANDTotalSpace();
    case FileSys::StorageId::NandUser:
        if (bis_factory == nullptr)
            return 0;
        return bis_factory->GetUserNANDTotalSpace();
    }

    return 0;
}

void FileSystemController::SetGameCard(FileSys::VirtualFile file) {
    gamecard = std::make_unique<FileSys::XCI>(file);
    const auto dir = gamecard->ConcatenatedPseudoDirectory();
    gamecard_registered = std::make_unique<FileSys::RegisteredCache>(dir);
    gamecard_placeholder = std::make_unique<FileSys::PlaceholderCache>(dir);
}

FileSys::XCI* FileSystemController::GetGameCard() const {
    return gamecard.get();
}

FileSys::RegisteredCache* FileSystemController::GetGameCardContents() const {
    return gamecard_registered.get();
}

FileSys::PlaceholderCache* FileSystemController::GetGameCardPlaceholder() const {
    return gamecard_placeholder.get();
}

FileSys::RegisteredCache* FileSystemController::GetSystemNANDContents() const {
    LOG_TRACE(Service_FS, "Opening System NAND Contents");

    if (bis_factory == nullptr)
        return nullptr;

    return bis_factory->GetSystemNANDContents();
}

FileSys::RegisteredCache* FileSystemController::GetUserNANDContents() const {
    LOG_TRACE(Service_FS, "Opening User NAND Contents");

    if (bis_factory == nullptr)
        return nullptr;

    return bis_factory->GetUserNANDContents();
}

FileSys::RegisteredCache* FileSystemController::GetSDMCContents() const {
    LOG_TRACE(Service_FS, "Opening SDMC Contents");

    if (sdmc_factory == nullptr)
        return nullptr;

    return sdmc_factory->GetSDMCContents();
}

FileSys::PlaceholderCache* FileSystemController::GetSystemNANDPlaceholder() const {
    LOG_TRACE(Service_FS, "Opening System NAND Placeholder");

    if (bis_factory == nullptr)
        return nullptr;

    return bis_factory->GetSystemNANDPlaceholder();
}

FileSys::PlaceholderCache* FileSystemController::GetUserNANDPlaceholder() const {
    LOG_TRACE(Service_FS, "Opening User NAND Placeholder");

    if (bis_factory == nullptr)
        return nullptr;

    return bis_factory->GetUserNANDPlaceholder();
}

FileSys::PlaceholderCache* FileSystemController::GetSDMCPlaceholder() const {
    LOG_TRACE(Service_FS, "Opening SDMC Placeholder");

    if (sdmc_factory == nullptr)
        return nullptr;

    return sdmc_factory->GetSDMCPlaceholder();
}

FileSys::RegisteredCache* FileSystemController::GetRegisteredCacheForStorage(
    FileSys::StorageId id) const {
    switch (id) {
    case FileSys::StorageId::None:
    case FileSys::StorageId::Host:
        UNIMPLEMENTED();
        return nullptr;
    case FileSys::StorageId::GameCard:
        return GetGameCardContents();
    case FileSys::StorageId::NandSystem:
        return GetSystemNANDContents();
    case FileSys::StorageId::NandUser:
        return GetUserNANDContents();
    case FileSys::StorageId::SdCard:
        return GetSDMCContents();
    }

    return nullptr;
}

FileSys::PlaceholderCache* FileSystemController::GetPlaceholderCacheForStorage(
    FileSys::StorageId id) const {
    switch (id) {
    case FileSys::StorageId::None:
    case FileSys::StorageId::Host:
        UNIMPLEMENTED();
        return nullptr;
    case FileSys::StorageId::GameCard:
        return GetGameCardPlaceholder();
    case FileSys::StorageId::NandSystem:
        return GetSystemNANDPlaceholder();
    case FileSys::StorageId::NandUser:
        return GetUserNANDPlaceholder();
    case FileSys::StorageId::SdCard:
        return GetSDMCPlaceholder();
    }

    return nullptr;
}

FileSys::VirtualDir FileSystemController::GetSystemNANDContentDirectory() const {
    LOG_TRACE(Service_FS, "Opening system NAND content directory");

    if (bis_factory == nullptr)
        return nullptr;

    return bis_factory->GetSystemNANDContentDirectory();
}

FileSys::VirtualDir FileSystemController::GetUserNANDContentDirectory() const {
    LOG_TRACE(Service_FS, "Opening user NAND content directory");

    if (bis_factory == nullptr)
        return nullptr;

    return bis_factory->GetUserNANDContentDirectory();
}

FileSys::VirtualDir FileSystemController::GetSDMCContentDirectory() const {
    LOG_TRACE(Service_FS, "Opening SDMC content directory");

    if (sdmc_factory == nullptr)
        return nullptr;

    return sdmc_factory->GetSDMCContentDirectory();
}

FileSys::VirtualDir FileSystemController::GetNANDImageDirectory() const {
    LOG_TRACE(Service_FS, "Opening NAND image directory");

    if (bis_factory == nullptr)
        return nullptr;

    return bis_factory->GetImageDirectory();
}

FileSys::VirtualDir FileSystemController::GetSDMCImageDirectory() const {
    LOG_TRACE(Service_FS, "Opening SDMC image directory");

    if (sdmc_factory == nullptr)
        return nullptr;

    return sdmc_factory->GetImageDirectory();
}

FileSys::VirtualDir FileSystemController::GetContentDirectory(ContentStorageId id) const {
    switch (id) {
    case ContentStorageId::System:
        return GetSystemNANDContentDirectory();
    case ContentStorageId::User:
        return GetUserNANDContentDirectory();
    case ContentStorageId::SdCard:
        return GetSDMCContentDirectory();
    }

    return nullptr;
}

FileSys::VirtualDir FileSystemController::GetImageDirectory(ImageDirectoryId id) const {
    switch (id) {
    case ImageDirectoryId::NAND:
        return GetNANDImageDirectory();
    case ImageDirectoryId::SdCard:
        return GetSDMCImageDirectory();
    }

    return nullptr;
}

FileSys::VirtualDir FileSystemController::GetModificationLoadRoot(u64 title_id) const {
    LOG_TRACE(Service_FS, "Opening mod load root for tid={:016X}", title_id);

    if (bis_factory == nullptr)
        return nullptr;

    return bis_factory->GetModificationLoadRoot(title_id);
}

FileSys::VirtualDir FileSystemController::GetSDMCModificationLoadRoot(u64 title_id) const {
    LOG_TRACE(Service_FS, "Opening SDMC mod load root for tid={:016X}", title_id);

    if (sdmc_factory == nullptr) {
        return nullptr;
    }

    return sdmc_factory->GetSDMCModificationLoadRoot(title_id);
}

FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id) const {
    LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id);

    if (bis_factory == nullptr)
        return nullptr;

    return bis_factory->GetModificationDumpRoot(title_id);
}

FileSys::VirtualDir FileSystemController::GetBCATDirectory(u64 title_id) const {
    LOG_TRACE(Service_FS, "Opening BCAT root for tid={:016X}", title_id);

    if (bis_factory == nullptr)
        return nullptr;

    return bis_factory->GetBCATDirectory(title_id);
}

void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
    if (overwrite) {
        bis_factory = nullptr;
        sdmc_factory = nullptr;
    }

    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::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::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);

    if (bis_factory == nullptr) {
        bis_factory = std::make_unique<FileSys::BISFactory>(
            nand_directory, std::move(load_directory), std::move(dump_directory));
        system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SysNAND,
                                       bis_factory->GetSystemNANDContents());
        system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::UserNAND,
                                       bis_factory->GetUserNANDContents());
    }

    if (sdmc_factory == nullptr) {
        sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory),
                                                              std::move(sd_load_directory));
        system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC,
                                       sdmc_factory->GetSDMCContents());
    }
}

void FileSystemController::Reset() {
    std::scoped_lock lk{registration_lock};
    registrations.clear();
}

void LoopProcess(Core::System& system) {
    auto server_manager = std::make_unique<ServerManager>(system);

    const auto FileSystemProxyFactory = [&] { return std::make_shared<FSP_SRV>(system); };

    server_manager->RegisterNamedService("fsp-ldr", std::make_shared<FSP_LDR>(system));
    server_manager->RegisterNamedService("fsp:pr", std::make_shared<FSP_PR>(system));
    server_manager->RegisterNamedService("fsp-srv", std::move(FileSystemProxyFactory));
    ServerManager::RunServer(std::move(server_manager));
}

} // namespace Service::FileSystem


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <memory>
#include <mutex>
#include "common/common_types.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 {
class System;
}

namespace FileSys {
class BISFactory;
class NCA;
class RegisteredCache;
class RegisteredCacheUnion;
class PlaceholderCache;
class RomFSFactory;
class SaveDataFactory;
class SDMCFactory;
class XCI;

enum class BisPartitionId : u32;
enum class ContentRecordType : u8;
enum class SaveDataSpaceId : u8;
enum class SaveDataType : u8;
enum class StorageId : u8;

struct SaveDataAttribute;
struct SaveDataSize;
} // namespace FileSys

namespace Service {

namespace SM {
class ServiceManager;
} // namespace SM

namespace FileSystem {

class RomFsController;
class SaveDataController;

enum class ContentStorageId : u32 {
    System,
    User,
    SdCard,
};

enum class ImageDirectoryId : u32 {
    NAND,
    SdCard,
};

using ProcessId = u64;
using ProgramId = u64;

class FileSystemController {
public:
    explicit FileSystemController(Core::System& system_);
    ~FileSystemController();

    Result RegisterProcess(ProcessId process_id, ProgramId program_id,
                           std::shared_ptr<FileSys::RomFSFactory>&& factory);
    Result OpenProcess(ProgramId* out_program_id,
                       std::shared_ptr<SaveDataController>* out_save_data_controller,
                       std::shared_ptr<RomFsController>* out_romfs_controller,
                       ProcessId process_id);
    void SetPackedUpdate(ProcessId process_id, FileSys::VirtualFile update_raw);

    std::shared_ptr<SaveDataController> OpenSaveDataController();

    Result OpenSDMC(FileSys::VirtualDir* out_sdmc) const;
    Result OpenBISPartition(FileSys::VirtualDir* out_bis_partition,
                            FileSys::BisPartitionId id) const;
    Result OpenBISPartitionStorage(FileSys::VirtualFile* out_bis_partition_storage,
                                   FileSys::BisPartitionId id) const;

    u64 GetFreeSpaceSize(FileSys::StorageId id) const;
    u64 GetTotalSpaceSize(FileSys::StorageId id) const;

    void SetGameCard(FileSys::VirtualFile file);
    FileSys::XCI* GetGameCard() const;

    FileSys::RegisteredCache* GetSystemNANDContents() const;
    FileSys::RegisteredCache* GetUserNANDContents() const;
    FileSys::RegisteredCache* GetSDMCContents() const;
    FileSys::RegisteredCache* GetGameCardContents() const;

    FileSys::PlaceholderCache* GetSystemNANDPlaceholder() const;
    FileSys::PlaceholderCache* GetUserNANDPlaceholder() const;
    FileSys::PlaceholderCache* GetSDMCPlaceholder() const;
    FileSys::PlaceholderCache* GetGameCardPlaceholder() const;

    FileSys::RegisteredCache* GetRegisteredCacheForStorage(FileSys::StorageId id) const;
    FileSys::PlaceholderCache* GetPlaceholderCacheForStorage(FileSys::StorageId id) const;

    FileSys::VirtualDir GetSystemNANDContentDirectory() const;
    FileSys::VirtualDir GetUserNANDContentDirectory() const;
    FileSys::VirtualDir GetSDMCContentDirectory() const;

    FileSys::VirtualDir GetNANDImageDirectory() const;
    FileSys::VirtualDir GetSDMCImageDirectory() const;

    FileSys::VirtualDir GetContentDirectory(ContentStorageId id) const;
    FileSys::VirtualDir GetImageDirectory(ImageDirectoryId id) const;

    FileSys::VirtualDir GetSDMCModificationLoadRoot(u64 title_id) const;
    FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const;
    FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const;

    FileSys::VirtualDir GetBCATDirectory(u64 title_id) const;

    // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
    // above is called.
    void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);

    void Reset();

private:
    std::shared_ptr<FileSys::SaveDataFactory> CreateSaveDataFactory(ProgramId program_id);

    struct Registration {
        ProgramId program_id;
        std::shared_ptr<FileSys::RomFSFactory> romfs_factory;
        std::shared_ptr<FileSys::SaveDataFactory> save_data_factory;
    };

    std::mutex registration_lock;
    std::map<ProcessId, Registration> registrations;

    std::unique_ptr<FileSys::SDMCFactory> sdmc_factory;
    std::unique_ptr<FileSys::BISFactory> bis_factory;

    std::unique_ptr<FileSys::XCI> gamecard;
    std::unique_ptr<FileSys::RegisteredCache> gamecard_registered;
    std::unique_ptr<FileSys::PlaceholderCache> gamecard_placeholder;

    Core::System& system;
};

void LoopProcess(Core::System& system);

// A class that wraps a VfsDirectory with methods that return ResultVal and Result instead of
// pointers and booleans. This makes using a VfsDirectory with switch services much easier and
// avoids repetitive code.
class VfsDirectoryServiceWrapper {
public:
    explicit VfsDirectoryServiceWrapper(FileSys::VirtualDir backing);
    ~VfsDirectoryServiceWrapper();

    /**
     * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
     */
    std::string GetName() const;

    /**
     * Create a file specified by its path
     * @param path Path relative to the Archive
     * @param size The size of the new file, filled with zeroes
     * @return Result of the operation
     */
    Result CreateFile(const std::string& path, u64 size) const;

    /**
     * Delete a file specified by its path
     * @param path Path relative to the archive
     * @return Result of the operation
     */
    Result DeleteFile(const std::string& path) const;

    /**
     * Create a directory specified by its path
     * @param path Path relative to the archive
     * @return Result of the operation
     */
    Result CreateDirectory(const std::string& path) const;

    /**
     * Delete a directory specified by its path
     * @param path Path relative to the archive
     * @return Result of the operation
     */
    Result DeleteDirectory(const std::string& path) const;

    /**
     * Delete a directory specified by its path and anything under it
     * @param path Path relative to the archive
     * @return Result of the operation
     */
    Result DeleteDirectoryRecursively(const std::string& path) const;

    /**
     * Cleans the specified directory. This is similar to DeleteDirectoryRecursively,
     * in that it deletes all the contents of the specified directory, however, this
     * function does *not* delete the directory itself. It only deletes everything
     * within it.
     *
     * @param path Path relative to the archive.
     *
     * @return Result of the operation.
     */
    Result CleanDirectoryRecursively(const std::string& path) const;

    /**
     * Rename a File specified by its path
     * @param src_path Source path relative to the archive
     * @param dest_path Destination path relative to the archive
     * @return Result of the operation
     */
    Result RenameFile(const std::string& src_path, const std::string& dest_path) const;

    /**
     * Rename a Directory specified by its path
     * @param src_path Source path relative to the archive
     * @param dest_path Destination path relative to the archive
     * @return Result of the operation
     */
    Result RenameDirectory(const std::string& src_path, const std::string& dest_path) const;

    /**
     * Open a file specified by its path, using the specified mode
     * @param path Path relative to the archive
     * @param mode Mode to open the file with
     * @return Opened file, or error code
     */
    Result OpenFile(FileSys::VirtualFile* out_file, const std::string& path,
                    FileSys::OpenMode mode) const;

    /**
     * Open a directory specified by its path
     * @param path Path relative to the archive
     * @return Opened directory, or error code
     */
    Result OpenDirectory(FileSys::VirtualDir* out_directory, const std::string& path);

    /**
     * Get the type of the specified path
     * @return The type of the specified path or error code
     */
    Result GetEntryType(FileSys::DirectoryEntryType* out_entry_type, const std::string& path) const;

    /**
     * Get the timestamp of the specified path
     * @return The timestamp of the specified path or error code
     */
    Result GetFileTimeStampRaw(FileSys::FileTimeStampRaw* out_time_stamp_raw,
                               const std::string& path) const;

private:
    FileSys::VirtualDir backing;
};

} // namespace FileSystem
} // namespace Service


code:
// 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/cmif_serialization.h"
#include "core/hle/service/filesystem/fsp/fs_i_directory.h"

namespace Service::FileSystem {

IDirectory::IDirectory(Core::System& system_, FileSys::VirtualDir directory_,
                       FileSys::OpenDirectoryMode mode)
    : ServiceFramework{system_, "IDirectory"},
      backend(std::make_unique<FileSys::Fsa::IDirectory>(directory_, mode)) {
    static const FunctionInfo functions[] = {
        {0, D<&IDirectory::Read>, "Read"},
        {1, D<&IDirectory::GetEntryCount>, "GetEntryCount"},
    };
    RegisterHandlers(functions);
}

Result IDirectory::Read(
    Out<s64> out_count,
    const OutArray<FileSys::DirectoryEntry, BufferAttr_HipcMapAlias> out_entries) {
    LOG_DEBUG(Service_FS, "called.");

    R_RETURN(backend->Read(out_count, out_entries.data(), out_entries.size()));
}

Result IDirectory::GetEntryCount(Out<s64> out_count) {
    LOG_DEBUG(Service_FS, "called");

    R_RETURN(backend->GetEntryCount(out_count));
}

} // namespace Service::FileSystem


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/file_sys/fsa/fs_i_directory.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/service/cmif_types.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 directory_,
                        FileSys::OpenDirectoryMode mode);

private:
    std::unique_ptr<FileSys::Fsa::IDirectory> backend;

    Result Read(Out<s64> out_count,
                const OutArray<FileSys::DirectoryEntry, BufferAttr_HipcMapAlias> out_entries);
    Result GetEntryCount(Out<s64> out_count);
};

} // namespace Service::FileSystem


code:
// 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/cmif_serialization.h"
#include "core/hle/service/filesystem/fsp/fs_i_file.h"

namespace Service::FileSystem {

IFile::IFile(Core::System& system_, FileSys::VirtualFile file_)
    : ServiceFramework{system_, "IFile"}, backend{std::make_unique<FileSys::Fsa::IFile>(file_)} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0, D<&IFile::Read>, "Read"},
        {1, D<&IFile::Write>, "Write"},
        {2, D<&IFile::Flush>, "Flush"},
        {3, D<&IFile::SetSize>, "SetSize"},
        {4, D<&IFile::GetSize>, "GetSize"},
        {5, nullptr, "OperateRange"},
        {6, nullptr, "OperateRangeWithBuffer"},
    };
    // clang-format on
    RegisterHandlers(functions);
}

Result IFile::Read(
    FileSys::ReadOption option, Out<s64> out_size, s64 offset,
    const OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_buffer,
    s64 size) {
    LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option.value, offset,
              size);

    // Read the data from the Storage backend
    R_RETURN(
        backend->Read(reinterpret_cast<size_t*>(out_size.Get()), offset, out_buffer.data(), size));
}

Result IFile::Write(
    const InBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> buffer,
    FileSys::WriteOption option, s64 offset, s64 size) {
    LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option.value, offset,
              size);

    R_RETURN(backend->Write(offset, buffer.data(), size, option));
}

Result IFile::Flush() {
    LOG_DEBUG(Service_FS, "called");

    R_RETURN(backend->Flush());
}

Result IFile::SetSize(s64 size) {
    LOG_DEBUG(Service_FS, "called, size={}", size);

    R_RETURN(backend->SetSize(size));
}

Result IFile::GetSize(Out<s64> out_size) {
    LOG_DEBUG(Service_FS, "called");

    R_RETURN(backend->GetSize(out_size));
}

} // namespace Service::FileSystem


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/file_sys/fsa/fs_i_file.h"
#include "core/hle/service/cmif_types.h"
#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 file_);

private:
    std::unique_ptr<FileSys::Fsa::IFile> backend;

    Result Read(FileSys::ReadOption option, Out<s64> out_size, s64 offset,
                const OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure>
                    out_buffer,
                s64 size);
    Result Write(
        const InBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> buffer,
        FileSys::WriteOption option, s64 offset, s64 size);
    Result Flush();
    Result SetSize(s64 size);
    Result GetSize(Out<s64> out_size);
};

} // namespace Service::FileSystem


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/string_util.h"
#include "core/file_sys/fssrv/fssrv_sf_path.h"
#include "core/hle/service/cmif_serialization.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"

namespace Service::FileSystem {

IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_)
    : ServiceFramework{system_, "IFileSystem"}, backend{std::make_unique<FileSys::Fsa::IFileSystem>(
                                                    dir_)},
      size_getter{std::move(size_getter_)} {
    static const FunctionInfo functions[] = {
        {0, D<&IFileSystem::CreateFile>, "CreateFile"},
        {1, D<&IFileSystem::DeleteFile>, "DeleteFile"},
        {2, D<&IFileSystem::CreateDirectory>, "CreateDirectory"},
        {3, D<&IFileSystem::DeleteDirectory>, "DeleteDirectory"},
        {4, D<&IFileSystem::DeleteDirectoryRecursively>, "DeleteDirectoryRecursively"},
        {5, D<&IFileSystem::RenameFile>, "RenameFile"},
        {6, nullptr, "RenameDirectory"},
        {7, D<&IFileSystem::GetEntryType>, "GetEntryType"},
        {8, D<&IFileSystem::OpenFile>, "OpenFile"},
        {9, D<&IFileSystem::OpenDirectory>, "OpenDirectory"},
        {10, D<&IFileSystem::Commit>, "Commit"},
        {11, D<&IFileSystem::GetFreeSpaceSize>, "GetFreeSpaceSize"},
        {12, D<&IFileSystem::GetTotalSpaceSize>, "GetTotalSpaceSize"},
        {13, D<&IFileSystem::CleanDirectoryRecursively>, "CleanDirectoryRecursively"},
        {14, D<&IFileSystem::GetFileTimeStampRaw>, "GetFileTimeStampRaw"},
        {15, nullptr, "QueryEntry"},
        {16, D<&IFileSystem::GetFileSystemAttribute>, "GetFileSystemAttribute"},
    };
    RegisterHandlers(functions);
}

Result IFileSystem::CreateFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
                               s32 option, s64 size) {
    LOG_DEBUG(Service_FS, "called. file={}, option=0x{:X}, size=0x{:08X}", path->str, option, size);

    R_RETURN(backend->CreateFile(FileSys::Path(path->str), size));
}

Result IFileSystem::DeleteFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
    LOG_DEBUG(Service_FS, "called. file={}", path->str);

    R_RETURN(backend->DeleteFile(FileSys::Path(path->str)));
}

Result IFileSystem::CreateDirectory(
    const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
    LOG_DEBUG(Service_FS, "called. directory={}", path->str);

    R_RETURN(backend->CreateDirectory(FileSys::Path(path->str)));
}

Result IFileSystem::DeleteDirectory(
    const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
    LOG_DEBUG(Service_FS, "called. directory={}", path->str);

    R_RETURN(backend->DeleteDirectory(FileSys::Path(path->str)));
}

Result IFileSystem::DeleteDirectoryRecursively(
    const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
    LOG_DEBUG(Service_FS, "called. directory={}", path->str);

    R_RETURN(backend->DeleteDirectoryRecursively(FileSys::Path(path->str)));
}

Result IFileSystem::CleanDirectoryRecursively(
    const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
    LOG_DEBUG(Service_FS, "called. Directory: {}", path->str);

    R_RETURN(backend->CleanDirectoryRecursively(FileSys::Path(path->str)));
}

Result IFileSystem::RenameFile(
    const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
    const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path) {
    LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", old_path->str, new_path->str);

    R_RETURN(backend->RenameFile(FileSys::Path(old_path->str), FileSys::Path(new_path->str)));
}

Result IFileSystem::OpenFile(OutInterface<IFile> out_interface,
                             const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
                             u32 mode) {
    LOG_DEBUG(Service_FS, "called. file={}, mode={}", path->str, mode);

    FileSys::VirtualFile vfs_file{};
    R_TRY(backend->OpenFile(&vfs_file, FileSys::Path(path->str),
                            static_cast<FileSys::OpenMode>(mode)));

    *out_interface = std::make_shared<IFile>(system, vfs_file);
    R_SUCCEED();
}

Result IFileSystem::OpenDirectory(OutInterface<IDirectory> out_interface,
                                  const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
                                  u32 mode) {
    LOG_DEBUG(Service_FS, "called. directory={}, mode={}", path->str, mode);

    FileSys::VirtualDir vfs_dir{};
    R_TRY(backend->OpenDirectory(&vfs_dir, FileSys::Path(path->str),
                                 static_cast<FileSys::OpenDirectoryMode>(mode)));

    *out_interface = std::make_shared<IDirectory>(system, vfs_dir,
                                                  static_cast<FileSys::OpenDirectoryMode>(mode));
    R_SUCCEED();
}

Result IFileSystem::GetEntryType(
    Out<u32> out_type, const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
    LOG_DEBUG(Service_FS, "called. file={}", path->str);

    FileSys::DirectoryEntryType vfs_entry_type{};
    R_TRY(backend->GetEntryType(&vfs_entry_type, FileSys::Path(path->str)));

    *out_type = static_cast<u32>(vfs_entry_type);
    R_SUCCEED();
}

Result IFileSystem::Commit() {
    LOG_WARNING(Service_FS, "(STUBBED) called");

    R_SUCCEED();
}

Result IFileSystem::GetFreeSpaceSize(
    Out<s64> out_size, const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
    LOG_DEBUG(Service_FS, "called");

    *out_size = size_getter.get_free_size();
    R_SUCCEED();
}

Result IFileSystem::GetTotalSpaceSize(
    Out<s64> out_size, const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
    LOG_DEBUG(Service_FS, "called");

    *out_size = size_getter.get_total_size();
    R_SUCCEED();
}

Result IFileSystem::GetFileTimeStampRaw(
    Out<FileSys::FileTimeStampRaw> out_timestamp,
    const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
    LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", path->str);

    FileSys::FileTimeStampRaw vfs_timestamp{};
    R_TRY(backend->GetFileTimeStampRaw(&vfs_timestamp, FileSys::Path(path->str)));

    *out_timestamp = vfs_timestamp;
    R_SUCCEED();
}

Result IFileSystem::GetFileSystemAttribute(Out<FileSys::FileSystemAttribute> out_attribute) {
    LOG_WARNING(Service_FS, "(STUBBED) called");

    FileSys::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;

    *out_attribute = savedata_attribute;
    R_SUCCEED();
}

} // namespace Service::FileSystem


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "common/common_funcs.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/fsa/fs_i_filesystem.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/fsp/fsp_types.h"
#include "core/hle/service/service.h"

namespace FileSys::Sf {
struct Path;
}

namespace Service::FileSystem {

class IFile;
class IDirectory;

class IFileSystem final : public ServiceFramework<IFileSystem> {
public:
    explicit IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_);

    Result CreateFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, s32 option,
                      s64 size);
    Result DeleteFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
    Result CreateDirectory(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
    Result DeleteDirectory(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
    Result DeleteDirectoryRecursively(
        const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
    Result CleanDirectoryRecursively(
        const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
    Result RenameFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
                      const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path);
    Result OpenFile(OutInterface<IFile> out_interface,
                    const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, u32 mode);
    Result OpenDirectory(OutInterface<IDirectory> out_interface,
                         const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
                         u32 mode);
    Result GetEntryType(Out<u32> out_type,
                        const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
    Result Commit();
    Result GetFreeSpaceSize(Out<s64> out_size,
                            const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
    Result GetTotalSpaceSize(Out<s64> out_size,
                             const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
    Result GetFileTimeStampRaw(Out<FileSys::FileTimeStampRaw> out_timestamp,
                               const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
    Result GetFileSystemAttribute(Out<FileSys::FileSystemAttribute> out_attribute);

private:
    std::unique_ptr<FileSys::Fsa::IFileSystem> backend;
    SizeGetter size_getter;
};

} // namespace Service::FileSystem


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/filesystem/fsp/fs_i_filesystem.h"
#include "core/hle/service/filesystem/fsp/fs_i_multi_commit_manager.h"

namespace Service::FileSystem {

IMultiCommitManager::IMultiCommitManager(Core::System& system_)
    : ServiceFramework{system_, "IMultiCommitManager"} {
    static const FunctionInfo functions[] = {
        {1, D<&IMultiCommitManager::Add>, "Add"},
        {2, D<&IMultiCommitManager::Commit>, "Commit"},
    };
    RegisterHandlers(functions);
}

IMultiCommitManager::~IMultiCommitManager() = default;

Result IMultiCommitManager::Add(std::shared_ptr<IFileSystem> filesystem) {
    LOG_WARNING(Service_FS, "(STUBBED) called");

    R_SUCCEED();
}

Result IMultiCommitManager::Commit() {
    LOG_WARNING(Service_FS, "(STUBBED) called");

    R_SUCCEED();
}

} // namespace Service::FileSystem


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/file_sys/vfs/vfs.h"
#include "core/hle/service/service.h"

namespace Service::FileSystem {

class IMultiCommitManager final : public ServiceFramework<IMultiCommitManager> {
public:
    explicit IMultiCommitManager(Core::System& system_);
    ~IMultiCommitManager() override;

private:
    Result Add(std::shared_ptr<IFileSystem> filesystem);
    Result Commit();

    FileSys::VirtualFile backend;
};

} // namespace Service::FileSystem


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/hex_util.h"
#include "core/file_sys/savedata_factory.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/filesystem/fsp/fs_i_save_data_info_reader.h"
#include "core/hle/service/filesystem/save_data_controller.h"

namespace Service::FileSystem {

ISaveDataInfoReader::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, D<&ISaveDataInfoReader::ReadSaveDataInfo>, "ReadSaveDataInfo"},
    };
    RegisterHandlers(functions);

    FindAllSaves(space);
}

ISaveDataInfoReader::~ISaveDataInfoReader() = default;

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);
}

Result ISaveDataInfoReader::ReadSaveDataInfo(
    Out<u64> out_count, OutArray<SaveDataInfo, BufferAttr_HipcMapAlias> out_entries) {
    LOG_DEBUG(Service_FS, "called");

    // Calculate how many entries we can fit in the output buffer
    const u64 count_entries = out_entries.size();

    // 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
    std::memcpy(out_entries.data(), begin, range_size);
    *out_count = actual_entries;

    R_SUCCEED();
}

void ISaveDataInfoReader::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") {
            FindNormalSaves(space, type);
        } else if (space == FileSys::SaveDataSpaceId::Temporary) {
            FindTemporaryStorageSaves(space, type);
        }
    }
}

void ISaveDataInfoReader::FindNormalSaves(FileSys::SaveDataSpaceId space,
                                          const FileSys::VirtualDir& type) {
    for (const auto& save_id : type->GetSubdirectories()) {
        for (const auto& user_id : save_id->GetSubdirectories()) {
            // Skip non user id subdirectories
            if (user_id->GetName().size() != 0x20) {
                continue;
            }

            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::System,
                    {},
                    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::Device : FileSys::SaveDataType::Account,
                    {},
                    user_id_numeric,
                    save_id_numeric,
                    stoull_be(title_id->GetName()),
                    title_id->GetSize(),
                    {},
                    {},
                });
            }
        }
    }
}

void ISaveDataInfoReader::FindTemporaryStorageSaves(FileSys::SaveDataSpaceId space,
                                                    const FileSys::VirtualDir& type) {
    for (const auto& user_id : type->GetSubdirectories()) {
        // Skip non user id subdirectories
        if (user_id->GetName().size() != 0x20) {
            continue;
        }
        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::Temporary,
                    {},
                    user_id_numeric,
                    stoull_be(type->GetName()),
                    stoull_be(title_id->GetName()),
                    title_id->GetSize(),
                    {},
                    {},
                });
            }
        }
    }
}

} // namespace Service::FileSystem


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <vector>
#include "common/common_types.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"

namespace Service::FileSystem {

class SaveDataController;

class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
public:
    explicit ISaveDataInfoReader(Core::System& system_,
                                 std::shared_ptr<SaveDataController> save_data_controller_,
                                 FileSys::SaveDataSpaceId space);
    ~ISaveDataInfoReader() override;

    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.");

    Result ReadSaveDataInfo(Out<u64> out_count,
                            OutArray<SaveDataInfo, BufferAttr_HipcMapAlias> out_entries);

private:
    void FindAllSaves(FileSys::SaveDataSpaceId space);
    void FindNormalSaves(FileSys::SaveDataSpaceId space, const FileSys::VirtualDir& type);
    void FindTemporaryStorageSaves(FileSys::SaveDataSpaceId space, const FileSys::VirtualDir& type);

    std::shared_ptr<SaveDataController> save_data_controller;
    std::vector<SaveDataInfo> info;
    u64 next_entry_index = 0;
};

} // namespace Service::FileSystem


code:
// 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/cmif_serialization.h"
#include "core/hle/service/filesystem/fsp/fs_i_storage.h"

namespace Service::FileSystem {

IStorage::IStorage(Core::System& system_, FileSys::VirtualFile backend_)
    : ServiceFramework{system_, "IStorage"}, backend(std::move(backend_)) {
    static const FunctionInfo functions[] = {
        {0, D<&IStorage::Read>, "Read"},
        {1, nullptr, "Write"},
        {2, nullptr, "Flush"},
        {3, nullptr, "SetSize"},
        {4, D<&IStorage::GetSize>, "GetSize"},
        {5, nullptr, "OperateRange"},
    };
    RegisterHandlers(functions);
}

Result IStorage::Read(
    OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_bytes,
    s64 offset, s64 length) {
    LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length);

    R_UNLESS(length >= 0, FileSys::ResultInvalidSize);
    R_UNLESS(offset >= 0, FileSys::ResultInvalidOffset);

    // Read the data from the Storage backend
    backend->Read(out_bytes.data(), length, offset);

    R_SUCCEED();
}

Result IStorage::GetSize(Out<u64> out_size) {
    *out_size = backend->GetSize();

    LOG_DEBUG(Service_FS, "called, size={}", *out_size);

    R_SUCCEED();
}

} // namespace Service::FileSystem


code:
// 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/cmif_types.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;

    Result Read(
        OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_bytes,
        s64 offset, s64 length);
    Result GetSize(Out<u64> out_size);
};

} // namespace Service::FileSystem


code:
// 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


code:
// 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


code:
// 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


code:
// 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