code:
// 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/content_archive.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.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/cmif_serialization.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_multi_commit_manager.h"
#include "core/hle/service/filesystem/fsp/fs_i_save_data_info_reader.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/fsp/save_data_transfer_prohibiter.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/loader/loader.h"
#include "core/reporter.h"

namespace Service::FileSystem {

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, D<&FSP_SRV::SetCurrentProcess>, "SetCurrentProcess"},
        {2, nullptr, "OpenDataFileSystemByCurrentProcess"},
        {7, D<&FSP_SRV::OpenFileSystemWithPatch>, "OpenFileSystemWithPatch"},
        {8, nullptr, "OpenFileSystemWithId"},
        {9, nullptr, "OpenDataFileSystemByApplicationId"},
        {11, nullptr, "OpenBisFileSystem"},
        {12, nullptr, "OpenBisStorage"},
        {13, nullptr, "InvalidateBisCache"},
        {17, nullptr, "OpenHostFileSystem"},
        {18, D<&FSP_SRV::OpenSdCardFileSystem>, "OpenSdCardFileSystem"},
        {19, nullptr, "FormatSdCardFileSystem"},
        {21, nullptr, "DeleteSaveDataFileSystem"},
        {22, D<&FSP_SRV::CreateSaveDataFileSystem>, "CreateSaveDataFileSystem"},
        {23, D<&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, D<&FSP_SRV::ExtendSaveDataFileSystem>, "ExtendSaveDataFileSystem"},
        {33, nullptr, "DeleteCacheStorage"},
        {34, D<&FSP_SRV::GetCacheStorageSize>, "GetCacheStorageSize"},
        {35, nullptr, "CreateSaveDataFileSystemByHashSalt"},
        {36, nullptr, "OpenHostFileSystemWithOption"},
        {51, D<&FSP_SRV::OpenSaveDataFileSystem>, "OpenSaveDataFileSystem"},
        {52, D<&FSP_SRV::OpenSaveDataFileSystemBySystemSaveDataId>, "OpenSaveDataFileSystemBySystemSaveDataId"},
        {53, D<&FSP_SRV::OpenReadOnlySaveDataFileSystem>, "OpenReadOnlySaveDataFileSystem"},
        {57, D<&FSP_SRV::ReadSaveDataFileSystemExtraDataBySaveDataSpaceId>, "ReadSaveDataFileSystemExtraDataBySaveDataSpaceId"},
        {58, D<&FSP_SRV::ReadSaveDataFileSystemExtraData>, "ReadSaveDataFileSystemExtraData"},
        {59, D<&FSP_SRV::WriteSaveDataFileSystemExtraData>, "WriteSaveDataFileSystemExtraData"},
        {60, nullptr, "OpenSaveDataInfoReader"},
        {61, D<&FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId>, "OpenSaveDataInfoReaderBySaveDataSpaceId"},
        {62, D<&FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage>, "OpenSaveDataInfoReaderOnlyCacheStorage"},
        {64, nullptr, "OpenSaveDataInternalStorageFileSystem"},
        {65, nullptr, "UpdateSaveDataMacForDebug"},
        {66, nullptr, "WriteSaveDataFileSystemExtraData2"},
        {67, D<&FSP_SRV::FindSaveDataWithFilter>, "FindSaveDataWithFilter"},
        {68, nullptr, "OpenSaveDataInfoReaderBySaveDataFilter"},
        {69, D<&FSP_SRV::ReadSaveDataFileSystemExtraDataBySaveDataAttribute>, "ReadSaveDataFileSystemExtraDataBySaveDataAttribute"},
        {70, D<&FSP_SRV::WriteSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute>, "WriteSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute"},
        {71, D<&FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute>, "ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute"},
        {80, nullptr, "OpenSaveDataMetaFile"},
        {81, nullptr, "OpenSaveDataTransferManager"},
        {82, nullptr, "OpenSaveDataTransferManagerVersion2"},
        {83, D<&FSP_SRV::OpenSaveDataTransferProhibiter>, "OpenSaveDataTransferProhibiter"},
        {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, D<&FSP_SRV::OpenDataStorageByCurrentProcess>, "OpenDataStorageByCurrentProcess"},
        {201, nullptr, "OpenDataStorageByProgramId"},
        {202, D<&FSP_SRV::OpenDataStorageByDataId>, "OpenDataStorageByDataId"},
        {203, D<&FSP_SRV::OpenPatchDataStorageByCurrentProcess>, "OpenPatchDataStorageByCurrentProcess"},
        {204, nullptr, "OpenDataFileSystemByProgramIndex"},
        {205, D<&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, D<&FSP_SRV::DisableAutoSaveDataCreation>, "DisableAutoSaveDataCreation"},
        {1004, D<&FSP_SRV::SetGlobalAccessLogMode>, "SetGlobalAccessLogMode"},
        {1005, D<&FSP_SRV::GetGlobalAccessLogMode>, "GetGlobalAccessLogMode"},
        {1006, D<&FSP_SRV::OutputAccessLogToSdCard>, "OutputAccessLogToSdCard"},
        {1007, nullptr, "RegisterUpdatePartition"},
        {1008, nullptr, "OpenRegisteredUpdatePartition"},
        {1009, nullptr, "GetAndClearMemoryReportInfo"},
        {1010, nullptr, "SetDataStorageRedirectTarget"},
        {1011, D<&FSP_SRV::GetProgramIndexForAccessLog>, "GetProgramIndexForAccessLog"},
        {1012, nullptr, "GetFsStackUsage"},
        {1013, nullptr, "UnsetSaveDataRootPath"},
        {1014, nullptr, "OutputMultiProgramTagAccessLog"},
        {1016, D<&FSP_SRV::FlushAccessLogOnSdCard>, "FlushAccessLogOnSdCard"},
        {1017, nullptr, "OutputApplicationInfoAccessLog"},
        {1018, nullptr, "SetDebugOption"},
        {1019, nullptr, "UnsetDebugOption"},
        {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
        {1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"},
        {1200, D<&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;

Result FSP_SRV::SetCurrentProcess(ClientProcessId pid) {
    current_process_id = *pid;

    LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id);

    R_RETURN(
        fsc.OpenProcess(&program_id, &save_data_controller, &romfs_controller, current_process_id));
}

Result FSP_SRV::OpenFileSystemWithPatch(OutInterface<IFileSystem> out_interface,
                                        FileSystemProxyType type, u64 open_program_id) {
    LOG_ERROR(Service_FS, "(STUBBED) called with type={}, program_id={:016X}", type,
              open_program_id);

    // FIXME: many issues with this
    ASSERT(type == FileSystemProxyType::Manual);
    const auto manual_romfs = romfs_controller->OpenPatchedRomFS(
        open_program_id, FileSys::ContentRecordType::HtmlDocument);

    ASSERT(manual_romfs != nullptr);

    const auto extracted_romfs = FileSys::ExtractRomFS(manual_romfs);
    ASSERT(extracted_romfs != nullptr);

    *out_interface = std::make_shared<IFileSystem>(
        system, extracted_romfs, SizeGetter::FromStorageId(fsc, FileSys::StorageId::NandUser));

    R_SUCCEED();
}

Result FSP_SRV::OpenSdCardFileSystem(OutInterface<IFileSystem> out_interface) {
    LOG_DEBUG(Service_FS, "called");

    FileSys::VirtualDir sdmc_dir{};
    fsc.OpenSDMC(&sdmc_dir);

    *out_interface = std::make_shared<IFileSystem>(
        system, sdmc_dir, SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard));

    R_SUCCEED();
}

Result FSP_SRV::CreateSaveDataFileSystem(FileSys::SaveDataCreationInfo save_create_struct,
                                         FileSys::SaveDataAttribute save_struct, u128 uid) {
    LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(),
              uid[1], uid[0]);

    FileSys::VirtualDir save_data_dir{};
    R_RETURN(save_data_controller->CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::User,
                                                  save_struct));
}

Result FSP_SRV::CreateSaveDataFileSystemBySystemSaveDataId(
    FileSys::SaveDataAttribute save_struct, FileSys::SaveDataCreationInfo save_create_struct) {
    LOG_DEBUG(Service_FS, "called save_struct = {}", save_struct.DebugInfo());

    FileSys::VirtualDir save_data_dir{};
    R_RETURN(save_data_controller->CreateSaveData(&save_data_dir, FileSys::SaveDataSpaceId::System,
                                                  save_struct));
}

Result FSP_SRV::OpenSaveDataFileSystem(OutInterface<IFileSystem> out_interface,
                                       FileSys::SaveDataSpaceId space_id,
                                       FileSys::SaveDataAttribute attribute) {
    LOG_INFO(Service_FS, "called.");

    FileSys::VirtualDir dir{};
    R_TRY(save_data_controller->OpenSaveData(&dir, space_id, attribute));

    FileSys::StorageId id{};
    switch (space_id) {
    case FileSys::SaveDataSpaceId::User:
        id = FileSys::StorageId::NandUser;
        break;
    case FileSys::SaveDataSpaceId::SdSystem:
    case FileSys::SaveDataSpaceId::SdUser:
        id = FileSys::StorageId::SdCard;
        break;
    case FileSys::SaveDataSpaceId::System:
        id = FileSys::StorageId::NandSystem;
        break;
    case FileSys::SaveDataSpaceId::Temporary:
    case FileSys::SaveDataSpaceId::ProperSystem:
    case FileSys::SaveDataSpaceId::SafeMode:
        ASSERT(false);
    }

    *out_interface =
        std::make_shared<IFileSystem>(system, std::move(dir), SizeGetter::FromStorageId(fsc, id));

    R_SUCCEED();
}

Result FSP_SRV::OpenSaveDataFileSystemBySystemSaveDataId(OutInterface<IFileSystem> out_interface,
                                                         FileSys::SaveDataSpaceId space_id,
                                                         FileSys::SaveDataAttribute attribute) {
    LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem");
    R_RETURN(OpenSaveDataFileSystem(out_interface, space_id, attribute));
}

Result FSP_SRV::OpenReadOnlySaveDataFileSystem(OutInterface<IFileSystem> out_interface,
                                               FileSys::SaveDataSpaceId space_id,
                                               FileSys::SaveDataAttribute attribute) {
    LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem");
    R_RETURN(OpenSaveDataFileSystem(out_interface, space_id, attribute));
}

Result FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(
    OutInterface<ISaveDataInfoReader> out_interface, FileSys::SaveDataSpaceId space) {
    LOG_INFO(Service_FS, "called, space={}", space);

    *out_interface = std::make_shared<ISaveDataInfoReader>(system, save_data_controller, space);

    R_SUCCEED();
}

Result FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage(
    OutInterface<ISaveDataInfoReader> out_interface) {
    LOG_WARNING(Service_FS, "(STUBBED) called");

    *out_interface = std::make_shared<ISaveDataInfoReader>(system, save_data_controller,
                                                           FileSys::SaveDataSpaceId::Temporary);

    R_SUCCEED();
}

Result FSP_SRV::FindSaveDataWithFilter(Out<s64> out_count,
                                       OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
                                       FileSys::SaveDataSpaceId space_id,
                                       FileSys::SaveDataFilter filter) {
    LOG_WARNING(Service_FS, "(STUBBED) called");
    R_THROW(FileSys::ResultTargetNotFound);
}

Result FSP_SRV::WriteSaveDataFileSystemExtraData(InBuffer<BufferAttr_HipcMapAlias> buffer,
                                                 FileSys::SaveDataSpaceId space_id,
                                                 u64 save_data_id) {
    LOG_WARNING(Service_FS, "(STUBBED) called, space_id={}, save_data_id={:016X}", space_id,
                save_data_id);
    R_SUCCEED();
}

Result FSP_SRV::WriteSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(
    InBuffer<BufferAttr_HipcMapAlias> buffer, InBuffer<BufferAttr_HipcMapAlias> mask_buffer,
    FileSys::SaveDataSpaceId space_id, FileSys::SaveDataAttribute attribute) {
    LOG_WARNING(Service_FS,
                "(STUBBED) called, space_id={}, attribute.program_id={:016X}\n"
                "attribute.user_id={:016X}{:016X}, attribute.save_id={:016X}\n"
                "attribute.type={}, attribute.rank={}, attribute.index={}",
                space_id, attribute.program_id, attribute.user_id[1], attribute.user_id[0],
                attribute.system_save_data_id, attribute.type, attribute.rank, attribute.index);
    R_SUCCEED();
}

Result FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(
    FileSys::SaveDataSpaceId space_id, FileSys::SaveDataAttribute attribute,
    InBuffer<BufferAttr_HipcMapAlias> mask_buffer, OutBuffer<BufferAttr_HipcMapAlias> out_buffer) {
    // Stub this to None for now, backend needs an impl to read/write the SaveDataExtraData
    // In an earlier version of the code, this was returned as an out argument, but this is not
    // correct
    [[maybe_unused]] constexpr auto flags = static_cast<u32>(FileSys::SaveDataFlags::None);

    LOG_WARNING(Service_FS,
                "(STUBBED) called, flags={}, space_id={}, attribute.program_id={:016X}\n"
                "attribute.user_id={:016X}{:016X}, attribute.save_id={:016X}\n"
                "attribute.type={}, attribute.rank={}, attribute.index={}",
                flags, space_id, attribute.program_id, attribute.user_id[1], attribute.user_id[0],
                attribute.system_save_data_id, attribute.type, attribute.rank, attribute.index);

    R_SUCCEED();
}

Result FSP_SRV::ReadSaveDataFileSystemExtraData(OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
                                                u64 save_data_id) {
    // Stub, backend needs an impl to read/write the SaveDataExtraData
    LOG_WARNING(Service_FS, "(STUBBED) called, save_data_id={:016X}", save_data_id);
    std::memset(out_buffer.data(), 0, out_buffer.size());
    R_SUCCEED();
}

Result FSP_SRV::ReadSaveDataFileSystemExtraDataBySaveDataAttribute(
    OutBuffer<BufferAttr_HipcMapAlias> out_buffer, FileSys::SaveDataSpaceId space_id,
    FileSys::SaveDataAttribute attribute) {
    // Stub, backend needs an impl to read/write the SaveDataExtraData
    LOG_WARNING(Service_FS,
                "(STUBBED) called, space_id={}, attribute.program_id={:016X}\n"
                "attribute.user_id={:016X}{:016X}, attribute.save_id={:016X}\n"
                "attribute.type={}, attribute.rank={}, attribute.index={}",
                space_id, attribute.program_id, attribute.user_id[1], attribute.user_id[0],
                attribute.system_save_data_id, attribute.type, attribute.rank, attribute.index);
    std::memset(out_buffer.data(), 0, out_buffer.size());
    R_SUCCEED();
}

Result FSP_SRV::ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(
    OutBuffer<BufferAttr_HipcMapAlias> out_buffer, FileSys::SaveDataSpaceId space_id,
    u64 save_data_id) {
    // Stub, backend needs an impl to read/write the SaveDataExtraData
    LOG_WARNING(Service_FS, "(STUBBED) called, space_id={}, save_data_id={:016X}", space_id,
                save_data_id);
    std::memset(out_buffer.data(), 0, out_buffer.size());
    R_SUCCEED();
}

Result FSP_SRV::OpenSaveDataTransferProhibiter(
    OutInterface<ISaveDataTransferProhibiter> out_prohibiter, u64 id) {
    LOG_WARNING(Service_FS, "(STUBBED) called, id={:016X}", id);
    *out_prohibiter = std::make_shared<ISaveDataTransferProhibiter>(system);
    R_SUCCEED();
}

Result FSP_SRV::OpenDataStorageByCurrentProcess(OutInterface<IStorage> out_interface) {
    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!");
            R_RETURN(ResultUnknown);
        }

        romfs = current_romfs;
    }

    *out_interface = std::make_shared<IStorage>(system, romfs);

    R_SUCCEED();
}

Result FSP_SRV::OpenDataStorageByDataId(OutInterface<IStorage> out_interface,
                                        FileSys::StorageId storage_id, u32 unknown, u64 title_id) {
    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) {
            *out_interface = std::make_shared<IStorage>(system, archive);
            R_SUCCEED();
        }

        // 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);
        R_RETURN(ResultUnknown);
    }

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

    *out_interface = std::move(storage);
    R_SUCCEED();
}

Result FSP_SRV::OpenPatchDataStorageByCurrentProcess(OutInterface<IStorage> out_interface,
                                                     FileSys::StorageId storage_id, u64 title_id) {
    LOG_WARNING(Service_FS, "(STUBBED) called with storage_id={:02X}, title_id={:016X}", storage_id,
                title_id);

    R_RETURN(FileSys::ResultTargetNotFound);
}

Result FSP_SRV::OpenDataStorageWithProgramIndex(OutInterface<IStorage> out_interface,
                                                u8 program_index) {
    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);
        R_RETURN(ResultUnknown);
    }

    *out_interface = std::make_shared<IStorage>(system, std::move(patched_romfs));

    R_SUCCEED();
}

Result FSP_SRV::DisableAutoSaveDataCreation() {
    LOG_DEBUG(Service_FS, "called");

    save_data_controller->SetAutoCreate(false);

    R_SUCCEED();
}

Result FSP_SRV::SetGlobalAccessLogMode(AccessLogMode access_log_mode_) {
    LOG_DEBUG(Service_FS, "called, access_log_mode={}", access_log_mode_);

    access_log_mode = access_log_mode_;

    R_SUCCEED();
}

Result FSP_SRV::GetGlobalAccessLogMode(Out<AccessLogMode> out_access_log_mode) {
    LOG_DEBUG(Service_FS, "called");

    *out_access_log_mode = access_log_mode;

    R_SUCCEED();
}

Result FSP_SRV::OutputAccessLogToSdCard(InBuffer<BufferAttr_HipcMapAlias> log_message_buffer) {
    LOG_DEBUG(Service_FS, "called");

    auto log = Common::StringFromFixedZeroTerminatedBuffer(
        reinterpret_cast<const char*>(log_message_buffer.data()), log_message_buffer.size());
    reporter.SaveFSAccessLog(log);

    R_SUCCEED();
}

Result FSP_SRV::GetProgramIndexForAccessLog(Out<AccessLogVersion> out_access_log_version,
                                            Out<u32> out_access_log_program_index) {
    LOG_DEBUG(Service_FS, "(STUBBED) called");

    *out_access_log_version = AccessLogVersion::Latest;
    *out_access_log_program_index = access_log_program_index;

    R_SUCCEED();
}

Result FSP_SRV::FlushAccessLogOnSdCard() {
    LOG_DEBUG(Service_FS, "(STUBBED) called");

    R_SUCCEED();
}

Result FSP_SRV::ExtendSaveDataFileSystem(FileSys::SaveDataSpaceId space_id, u64 save_data_id,
                                         s64 available_size, s64 journal_size) {
    // We don't have an index of save data ids, so we can't implement this.
    LOG_WARNING(Service_FS,
                "(STUBBED) called, space_id={}, save_data_id={:016X}, available_size={:#x}, "
                "journal_size={:#x}",
                space_id, save_data_id, available_size, journal_size);
    R_SUCCEED();
}

Result FSP_SRV::GetCacheStorageSize(s32 index, Out<s64> out_data_size, Out<s64> out_journal_size) {
    LOG_WARNING(Service_FS, "(STUBBED) called with index={}", index);

    *out_data_size = 0;
    *out_journal_size = 0;

    R_SUCCEED();
}

Result FSP_SRV::OpenMultiCommitManager(OutInterface<IMultiCommitManager> out_interface) {
    LOG_DEBUG(Service_FS, "called");

    *out_interface = std::make_shared<IMultiCommitManager>(system);

    R_SUCCEED();
}

} // namespace Service::FileSystem


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

#pragma once

#include <memory>
#include "core/file_sys/fs_save_data_types.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/filesystem/fsp/fsp_types.h"
#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;

class IFileSystem;
class ISaveDataInfoReader;
class ISaveDataTransferProhibiter;
class IStorage;
class IMultiCommitManager;

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:
    Result SetCurrentProcess(ClientProcessId pid);
    Result OpenFileSystemWithPatch(OutInterface<IFileSystem> out_interface,
                                   FileSystemProxyType type, u64 open_program_id);
    Result OpenSdCardFileSystem(OutInterface<IFileSystem> out_interface);
    Result CreateSaveDataFileSystem(FileSys::SaveDataCreationInfo save_create_struct,
                                    FileSys::SaveDataAttribute save_struct, u128 uid);
    Result CreateSaveDataFileSystemBySystemSaveDataId(
        FileSys::SaveDataAttribute save_struct, FileSys::SaveDataCreationInfo save_create_struct);
    Result OpenSaveDataFileSystem(OutInterface<IFileSystem> out_interface,
                                  FileSys::SaveDataSpaceId space_id,
                                  FileSys::SaveDataAttribute attribute);
    Result OpenSaveDataFileSystemBySystemSaveDataId(OutInterface<IFileSystem> out_interface,
                                                    FileSys::SaveDataSpaceId space_id,
                                                    FileSys::SaveDataAttribute attribute);
    Result OpenReadOnlySaveDataFileSystem(OutInterface<IFileSystem> out_interface,
                                          FileSys::SaveDataSpaceId space_id,
                                          FileSys::SaveDataAttribute attribute);
    Result OpenSaveDataInfoReaderBySaveDataSpaceId(OutInterface<ISaveDataInfoReader> out_interface,
                                                   FileSys::SaveDataSpaceId space);
    Result OpenSaveDataInfoReaderOnlyCacheStorage(OutInterface<ISaveDataInfoReader> out_interface);
    Result FindSaveDataWithFilter(Out<s64> out_count, OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
                                  FileSys::SaveDataSpaceId space_id,
                                  FileSys::SaveDataFilter filter);
    Result WriteSaveDataFileSystemExtraData(InBuffer<BufferAttr_HipcMapAlias> buffer,
                                            FileSys::SaveDataSpaceId space_id, u64 save_data_id);
    Result WriteSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(
        InBuffer<BufferAttr_HipcMapAlias> buffer, InBuffer<BufferAttr_HipcMapAlias> mask_buffer,
        FileSys::SaveDataSpaceId space_id, FileSys::SaveDataAttribute attribute);
    Result ReadSaveDataFileSystemExtraData(OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
                                           u64 save_data_id);
    Result ReadSaveDataFileSystemExtraDataBySaveDataAttribute(
        OutBuffer<BufferAttr_HipcMapAlias> out_buffer, FileSys::SaveDataSpaceId space_id,
        FileSys::SaveDataAttribute attribute);
    Result ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(
        OutBuffer<BufferAttr_HipcMapAlias> out_buffer, FileSys::SaveDataSpaceId space_id,
        u64 save_data_id);
    Result ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(
        FileSys::SaveDataSpaceId space_id, FileSys::SaveDataAttribute attribute,
        InBuffer<BufferAttr_HipcMapAlias> mask_buffer,
        OutBuffer<BufferAttr_HipcMapAlias> out_buffer);
    Result OpenSaveDataTransferProhibiter(OutInterface<ISaveDataTransferProhibiter> out_prohibiter,
                                          u64 id);
    Result OpenDataStorageByCurrentProcess(OutInterface<IStorage> out_interface);
    Result OpenDataStorageByDataId(OutInterface<IStorage> out_interface,
                                   FileSys::StorageId storage_id, u32 unknown, u64 title_id);
    Result OpenPatchDataStorageByCurrentProcess(OutInterface<IStorage> out_interface,
                                                FileSys::StorageId storage_id, u64 title_id);
    Result OpenDataStorageWithProgramIndex(OutInterface<IStorage> out_interface, u8 program_index);
    Result DisableAutoSaveDataCreation();
    Result SetGlobalAccessLogMode(AccessLogMode access_log_mode_);
    Result GetGlobalAccessLogMode(Out<AccessLogMode> out_access_log_mode);
    Result OutputAccessLogToSdCard(InBuffer<BufferAttr_HipcMapAlias> log_message_buffer);
    Result FlushAccessLogOnSdCard();
    Result GetProgramIndexForAccessLog(Out<AccessLogVersion> out_access_log_version,
                                       Out<u32> out_access_log_program_index);
    Result OpenMultiCommitManager(OutInterface<IMultiCommitManager> out_interface);
    Result ExtendSaveDataFileSystem(FileSys::SaveDataSpaceId space_id, u64 save_data_id,
                                    s64 available_size, s64 journal_size);
    Result GetCacheStorageSize(s32 index, Out<s64> out_data_size, Out<s64> out_journal_size);

    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

repo_name: litucks/torzu

license: mit


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

enum class FileSystemProxyType : u8 {
    Code = 0,
    Rom = 1,
    Logo = 2,
    Control = 3,
    Manual = 4,
    Meta = 5,
    Data = 6,
    Package = 7,
    RegisteredUpdate = 8,
};

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


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

#include "core/hle/service/filesystem/fsp/save_data_transfer_prohibiter.h"

namespace Service::FileSystem {

ISaveDataTransferProhibiter::ISaveDataTransferProhibiter(Core::System& system_)
    : ServiceFramework{system_, "ISaveDataTransferProhibiter"} {}

ISaveDataTransferProhibiter::~ISaveDataTransferProhibiter() = default;

} // namespace Service::FileSystem


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

#pragma once

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

namespace Service::FileSystem {

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

} // namespace Service::FileSystem


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

#include "core/hle/service/filesystem/romfs_controller.h"

namespace Service::FileSystem {

RomFsController::RomFsController(std::shared_ptr<FileSys::RomFSFactory> factory_, u64 program_id_)
    : factory{std::move(factory_)}, program_id{program_id_} {}
RomFsController::~RomFsController() = default;

FileSys::VirtualFile RomFsController::OpenRomFSCurrentProcess() {
    return factory->OpenCurrentProcess(program_id);
}

FileSys::VirtualFile RomFsController::OpenPatchedRomFS(u64 title_id,
                                                       FileSys::ContentRecordType type) {
    return factory->OpenPatchedRomFS(title_id, type);
}

FileSys::VirtualFile RomFsController::OpenPatchedRomFSWithProgramIndex(
    u64 title_id, u8 program_index, FileSys::ContentRecordType type) {
    return factory->OpenPatchedRomFSWithProgramIndex(title_id, program_index, type);
}

FileSys::VirtualFile RomFsController::OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
                                                FileSys::ContentRecordType type) {
    return factory->Open(title_id, storage_id, type);
}

std::shared_ptr<FileSys::NCA> RomFsController::OpenBaseNca(u64 title_id,
                                                           FileSys::StorageId storage_id,
                                                           FileSys::ContentRecordType type) {
    return factory->GetEntry(title_id, storage_id, type);
}

} // namespace Service::FileSystem


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

#pragma once

#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/vfs/vfs_types.h"

namespace Service::FileSystem {

class RomFsController {
public:
    explicit RomFsController(std::shared_ptr<FileSys::RomFSFactory> factory_, u64 program_id_);
    ~RomFsController();

    FileSys::VirtualFile OpenRomFSCurrentProcess();
    FileSys::VirtualFile OpenPatchedRomFS(u64 title_id, FileSys::ContentRecordType type);
    FileSys::VirtualFile OpenPatchedRomFSWithProgramIndex(u64 title_id, u8 program_index,
                                                          FileSys::ContentRecordType type);
    FileSys::VirtualFile OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
                                   FileSys::ContentRecordType type);
    std::shared_ptr<FileSys::NCA> OpenBaseNca(u64 title_id, FileSys::StorageId storage_id,
                                              FileSys::ContentRecordType type);

private:
    const std::shared_ptr<FileSys::RomFSFactory> factory;
    const u64 program_id;
};

} // namespace Service::FileSystem


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

#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/patch_manager.h"
#include "core/hle/service/filesystem/save_data_controller.h"
#include "core/loader/loader.h"

namespace Service::FileSystem {

namespace {

// A default size for normal/journal save data size if application control metadata cannot be found.
// This should be large enough to satisfy even the most extreme requirements (~4.2GB)
constexpr u64 SufficientSaveDataSize = 0xF0000000;

FileSys::SaveDataSize GetDefaultSaveDataSize(Core::System& system, u64 program_id) {
    const FileSys::PatchManager pm{program_id, system.GetFileSystemController(),
                                   system.GetContentProvider()};
    const auto metadata = pm.GetControlMetadata();
    const auto& nacp = metadata.first;

    if (nacp != nullptr) {
        return {nacp->GetDefaultNormalSaveSize(), nacp->GetDefaultJournalSaveSize()};
    }

    return {SufficientSaveDataSize, SufficientSaveDataSize};
}

} // namespace

SaveDataController::SaveDataController(Core::System& system_,
                                       std::shared_ptr<FileSys::SaveDataFactory> factory_)
    : system{system_}, factory{std::move(factory_)} {}
SaveDataController::~SaveDataController() = default;

Result SaveDataController::CreateSaveData(FileSys::VirtualDir* out_save_data,
                                          FileSys::SaveDataSpaceId space,
                                          const FileSys::SaveDataAttribute& attribute) {
    LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", space,
              attribute.DebugInfo());

    auto save_data = factory->Create(space, attribute);
    if (save_data == nullptr) {
        return FileSys::ResultTargetNotFound;
    }

    *out_save_data = save_data;
    return ResultSuccess;
}

Result SaveDataController::OpenSaveData(FileSys::VirtualDir* out_save_data,
                                        FileSys::SaveDataSpaceId space,
                                        const FileSys::SaveDataAttribute& attribute) {
    auto save_data = factory->Open(space, attribute);
    if (save_data == nullptr) {
        return FileSys::ResultTargetNotFound;
    }

    *out_save_data = save_data;
    return ResultSuccess;
}

Result SaveDataController::OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space,
                                             FileSys::SaveDataSpaceId space) {
    auto save_data_space = factory->GetSaveDataSpaceDirectory(space);
    if (save_data_space == nullptr) {
        return FileSys::ResultTargetNotFound;
    }

    *out_save_data_space = save_data_space;
    return ResultSuccess;
}

FileSys::SaveDataSize SaveDataController::ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id,
                                                           u128 user_id) {
    const auto value = factory->ReadSaveDataSize(type, title_id, user_id);

    if (value.normal == 0 && value.journal == 0) {
        const auto size = GetDefaultSaveDataSize(system, title_id);
        factory->WriteSaveDataSize(type, title_id, user_id, size);
        return size;
    }

    return value;
}

void SaveDataController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
                                           FileSys::SaveDataSize new_value) {
    factory->WriteSaveDataSize(type, title_id, user_id, new_value);
}

void SaveDataController::SetAutoCreate(bool state) {
    factory->SetAutoCreate(state);
}

} // namespace Service::FileSystem


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

#pragma once

#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs/vfs_types.h"

namespace Service::FileSystem {

class SaveDataController {
public:
    explicit SaveDataController(Core::System& system,
                                std::shared_ptr<FileSys::SaveDataFactory> factory_);
    ~SaveDataController();

    Result CreateSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space,
                          const FileSys::SaveDataAttribute& attribute);
    Result OpenSaveData(FileSys::VirtualDir* out_save_data, FileSys::SaveDataSpaceId space,
                        const FileSys::SaveDataAttribute& attribute);
    Result OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_space,
                             FileSys::SaveDataSpaceId space);

    FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id);
    void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
                           FileSys::SaveDataSize new_value);
    void SetAutoCreate(bool state);

private:
    Core::System& system;
    const std::shared_ptr<FileSys::SaveDataFactory> factory;
};

} // namespace Service::FileSystem


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

#include <queue>
#include "common/logging/log.h"
#include "common/uuid.h"
#include "core/core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/acc/errors.h"
#include "core/hle/service/friend/friend.h"
#include "core/hle/service/friend/friend_interface.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/server_manager.h"

namespace Service::Friend {

class IFriendService final : public ServiceFramework<IFriendService> {
public:
    explicit IFriendService(Core::System& system_)
        : ServiceFramework{system_, "IFriendService"}, service_context{system, "IFriendService"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, &IFriendService::GetCompletionEvent, "GetCompletionEvent"},
            {1, nullptr, "Cancel"},
            {10100, nullptr, "GetFriendListIds"},
            {10101, &IFriendService::GetFriendList, "GetFriendList"},
            {10102, nullptr, "UpdateFriendInfo"},
            {10110, nullptr, "GetFriendProfileImage"},
            {10120, &IFriendService::CheckFriendListAvailability, "CheckFriendListAvailability"},
            {10121, nullptr, "EnsureFriendListAvailable"},
            {10200, nullptr, "SendFriendRequestForApplication"},
            {10211, nullptr, "AddFacedFriendRequestForApplication"},
            {10400, &IFriendService::GetBlockedUserListIds, "GetBlockedUserListIds"},
            {10420, &IFriendService::CheckBlockedUserListAvailability, "CheckBlockedUserListAvailability"},
            {10421, nullptr, "EnsureBlockedUserListAvailable"},
            {10500, nullptr, "GetProfileList"},
            {10600, nullptr, "DeclareOpenOnlinePlaySession"},
            {10601, &IFriendService::DeclareCloseOnlinePlaySession, "DeclareCloseOnlinePlaySession"},
            {10610, &IFriendService::UpdateUserPresence, "UpdateUserPresence"},
            {10700, &IFriendService::GetPlayHistoryRegistrationKey, "GetPlayHistoryRegistrationKey"},
            {10701, nullptr, "GetPlayHistoryRegistrationKeyWithNetworkServiceAccountId"},
            {10702, nullptr, "AddPlayHistory"},
            {11000, nullptr, "GetProfileImageUrl"},
            {20100, &IFriendService::GetFriendCount, "GetFriendCount"},
            {20101, &IFriendService::GetNewlyFriendCount, "GetNewlyFriendCount"},
            {20102, nullptr, "GetFriendDetailedInfo"},
            {20103, nullptr, "SyncFriendList"},
            {20104, nullptr, "RequestSyncFriendList"},
            {20110, nullptr, "LoadFriendSetting"},
            {20200, &IFriendService::GetReceivedFriendRequestCount, "GetReceivedFriendRequestCount"},
            {20201, nullptr, "GetFriendRequestList"},
            {20300, nullptr, "GetFriendCandidateList"},
            {20301, nullptr, "GetNintendoNetworkIdInfo"},
            {20302, nullptr, "GetSnsAccountLinkage"},
            {20303, nullptr, "GetSnsAccountProfile"},
            {20304, nullptr, "GetSnsAccountFriendList"},
            {20400, nullptr, "GetBlockedUserList"},
            {20401, nullptr, "SyncBlockedUserList"},
            {20500, nullptr, "GetProfileExtraList"},
            {20501, nullptr, "GetRelationship"},
            {20600, nullptr, "GetUserPresenceView"},
            {20700, nullptr, "GetPlayHistoryList"},
            {20701, &IFriendService::GetPlayHistoryStatistics, "GetPlayHistoryStatistics"},
            {20800, nullptr, "LoadUserSetting"},
            {20801, nullptr, "SyncUserSetting"},
            {20900, nullptr, "RequestListSummaryOverlayNotification"},
            {21000, nullptr, "GetExternalApplicationCatalog"},
            {22000, nullptr, "GetReceivedFriendInvitationList"},
            {22001, nullptr, "GetReceivedFriendInvitationDetailedInfo"},
            {22010, &IFriendService::GetReceivedFriendInvitationCountCache, "GetReceivedFriendInvitationCountCache"},
            {30100, nullptr, "DropFriendNewlyFlags"},
            {30101, nullptr, "DeleteFriend"},
            {30110, nullptr, "DropFriendNewlyFlag"},
            {30120, nullptr, "ChangeFriendFavoriteFlag"},
            {30121, nullptr, "ChangeFriendOnlineNotificationFlag"},
            {30200, nullptr, "SendFriendRequest"},
            {30201, nullptr, "SendFriendRequestWithApplicationInfo"},
            {30202, nullptr, "CancelFriendRequest"},
            {30203, nullptr, "AcceptFriendRequest"},
            {30204, nullptr, "RejectFriendRequest"},
            {30205, nullptr, "ReadFriendRequest"},
            {30210, nullptr, "GetFacedFriendRequestRegistrationKey"},
            {30211, nullptr, "AddFacedFriendRequest"},
            {30212, nullptr, "CancelFacedFriendRequest"},
            {30213, nullptr, "GetFacedFriendRequestProfileImage"},
            {30214, nullptr, "GetFacedFriendRequestProfileImageFromPath"},
            {30215, nullptr, "SendFriendRequestWithExternalApplicationCatalogId"},
            {30216, nullptr, "ResendFacedFriendRequest"},
            {30217, nullptr, "SendFriendRequestWithNintendoNetworkIdInfo"},
            {30300, nullptr, "GetSnsAccountLinkPageUrl"},
            {30301, nullptr, "UnlinkSnsAccount"},
            {30400, nullptr, "BlockUser"},
            {30401, nullptr, "BlockUserWithApplicationInfo"},
            {30402, nullptr, "UnblockUser"},
            {30500, nullptr, "GetProfileExtraFromFriendCode"},
            {30700, nullptr, "DeletePlayHistory"},
            {30810, nullptr, "ChangePresencePermission"},
            {30811, nullptr, "ChangeFriendRequestReception"},
            {30812, nullptr, "ChangePlayLogPermission"},
            {30820, nullptr, "IssueFriendCode"},
            {30830, nullptr, "ClearPlayLog"},
            {30900, nullptr, "SendFriendInvitation"},
            {30910, nullptr, "ReadFriendInvitation"},
            {30911, nullptr, "ReadAllFriendInvitations"},
            {40100, nullptr, "DeleteFriendListCache"},
            {40400, nullptr, "DeleteBlockedUserListCache"},
            {49900, nullptr, "DeleteNetworkServiceAccountCache"},
        };
        // clang-format on

        RegisterHandlers(functions);

        completion_event = service_context.CreateEvent("IFriendService:CompletionEvent");
    }

    ~IFriendService() override {
        service_context.CloseEvent(completion_event);
    }

private:
    enum class PresenceFilter : u32 {
        None = 0,
        Online = 1,
        OnlinePlay = 2,
        OnlineOrOnlinePlay = 3,
    };

    struct SizedFriendFilter {
        PresenceFilter presence;
        u8 is_favorite;
        u8 same_app;
        u8 same_app_played;
        u8 arbitrary_app_played;
        u64 group_id;
    };
    static_assert(sizeof(SizedFriendFilter) == 0x10, "SizedFriendFilter is an invalid size");

    void GetCompletionEvent(HLERequestContext& ctx) {
        LOG_DEBUG(Service_Friend, "called");

        IPC::ResponseBuilder rb{ctx, 2, 1};
        rb.Push(ResultSuccess);
        rb.PushCopyObjects(completion_event->GetReadableEvent());
    }

    void GetFriendList(HLERequestContext& ctx) {
        IPC::RequestParser rp{ctx};
        const auto friend_offset = rp.Pop<u32>();
        const auto uuid = rp.PopRaw<Common::UUID>();
        [[maybe_unused]] const auto filter = rp.PopRaw<SizedFriendFilter>();
        const auto pid = rp.Pop<u64>();
        LOG_WARNING(Service_Friend, "(STUBBED) called, offset={}, uuid=0x{}, pid={}", friend_offset,
                    uuid.RawString(), pid);

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

        rb.Push<u32>(0); // Friend count
        // TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId"
    }

    void CheckFriendListAvailability(HLERequestContext& ctx) {
        IPC::RequestParser rp{ctx};
        const auto uuid{rp.PopRaw<Common::UUID>()};

        LOG_WARNING(Service_Friend, "(STUBBED) called, uuid=0x{}", uuid.RawString());

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

    void GetBlockedUserListIds(HLERequestContext& ctx) {
        // This is safe to stub, as there should be no adverse consequences from reporting no
        // blocked users.
        LOG_WARNING(Service_Friend, "(STUBBED) called");
        IPC::ResponseBuilder rb{ctx, 3};
        rb.Push(ResultSuccess);
        rb.Push<u32>(0); // Indicates there are no blocked users
    }

    void CheckBlockedUserListAvailability(HLERequestContext& ctx) {
        IPC::RequestParser rp{ctx};
        const auto uuid{rp.PopRaw<Common::UUID>()};

        LOG_WARNING(Service_Friend, "(STUBBED) called, uuid=0x{}", uuid.RawString());

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

    void DeclareCloseOnlinePlaySession(HLERequestContext& ctx) {
        // Stub used by Splatoon 2
        LOG_WARNING(Service_Friend, "(STUBBED) called");
        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
    }

    void UpdateUserPresence(HLERequestContext& ctx) {
        // Stub used by Retro City Rampage
        LOG_WARNING(Service_Friend, "(STUBBED) called");
        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
    }

    void GetPlayHistoryRegistrationKey(HLERequestContext& ctx) {
        IPC::RequestParser rp{ctx};
        const auto local_play = rp.Pop<bool>();
        const auto uuid = rp.PopRaw<Common::UUID>();

        LOG_WARNING(Service_Friend, "(STUBBED) called, local_play={}, uuid=0x{}", local_play,
                    uuid.RawString());

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

    void GetFriendCount(HLERequestContext& ctx) {
        LOG_DEBUG(Service_Friend, "(STUBBED) called");

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

    void GetNewlyFriendCount(HLERequestContext& ctx) {
        LOG_DEBUG(Service_Friend, "(STUBBED) called");

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

    void GetReceivedFriendRequestCount(HLERequestContext& ctx) {
        LOG_DEBUG(Service_Friend, "(STUBBED) called");

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

    void GetPlayHistoryStatistics(HLERequestContext& ctx) {
        LOG_ERROR(Service_Friend, "(STUBBED) called, check in out");

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

    void GetReceivedFriendInvitationCountCache(HLERequestContext& ctx) {
        LOG_DEBUG(Service_Friend, "(STUBBED) called, check in out");

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

    KernelHelpers::ServiceContext service_context;

    Kernel::KEvent* completion_event;
};

class INotificationService final : public ServiceFramework<INotificationService> {
public:
    explicit INotificationService(Core::System& system_, Common::UUID uuid_)
        : ServiceFramework{system_, "INotificationService"}, uuid{uuid_},
          service_context{system_, "INotificationService"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, &INotificationService::GetEvent, "GetEvent"},
            {1, &INotificationService::Clear, "Clear"},
            {2, &INotificationService::Pop, "Pop"}
        };
        // clang-format on

        RegisterHandlers(functions);

        notification_event = service_context.CreateEvent("INotificationService:NotifyEvent");
    }

    ~INotificationService() override {
        service_context.CloseEvent(notification_event);
    }

private:
    void GetEvent(HLERequestContext& ctx) {
        LOG_DEBUG(Service_Friend, "called");

        IPC::ResponseBuilder rb{ctx, 2, 1};
        rb.Push(ResultSuccess);
        rb.PushCopyObjects(notification_event->GetReadableEvent());
    }

    void Clear(HLERequestContext& ctx) {
        LOG_DEBUG(Service_Friend, "called");
        while (!notifications.empty()) {
            notifications.pop();
        }
        std::memset(&states, 0, sizeof(States));

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

    void Pop(HLERequestContext& ctx) {
        LOG_DEBUG(Service_Friend, "called");

        if (notifications.empty()) {
            LOG_ERROR(Service_Friend, "No notifications in queue!");
            IPC::ResponseBuilder rb{ctx, 2};
            rb.Push(Account::ResultNoNotifications);
            return;
        }

        const auto notification = notifications.front();
        notifications.pop();

        switch (notification.notification_type) {
        case NotificationTypes::HasUpdatedFriendsList:
            states.has_updated_friends = false;
            break;
        case NotificationTypes::HasReceivedFriendRequest:
            states.has_received_friend_request = false;
            break;
        default:
            // HOS seems not have an error case for an unknown notification
            LOG_WARNING(Service_Friend, "Unknown notification {:08X}",
                        notification.notification_type);
            break;
        }

        IPC::ResponseBuilder rb{ctx, 6};
        rb.Push(ResultSuccess);
        rb.PushRaw<SizedNotificationInfo>(notification);
    }

    enum class NotificationTypes : u32 {
        HasUpdatedFriendsList = 0x65,
        HasReceivedFriendRequest = 0x1
    };

    struct SizedNotificationInfo {
        NotificationTypes notification_type;
        INSERT_PADDING_WORDS(
            1); // TODO(ogniK): This doesn't seem to be used within any IPC returns as of now
        u64_le account_id;
    };
    static_assert(sizeof(SizedNotificationInfo) == 0x10,
                  "SizedNotificationInfo is an incorrect size");

    struct States {
        bool has_updated_friends;
        bool has_received_friend_request;
    };

    Common::UUID uuid;
    KernelHelpers::ServiceContext service_context;

    Kernel::KEvent* notification_event;
    std::queue<SizedNotificationInfo> notifications;
    States states{};
};

void Module::Interface::CreateFriendService(HLERequestContext& ctx) {
    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
    rb.Push(ResultSuccess);
    rb.PushIpcInterface<IFriendService>(system);
    LOG_DEBUG(Service_Friend, "called");
}

void Module::Interface::CreateNotificationService(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    auto uuid = rp.PopRaw<Common::UUID>();

    LOG_DEBUG(Service_Friend, "called, uuid=0x{}", uuid.RawString());

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

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;

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

    server_manager->RegisterNamedService("friend:a",
                                         std::make_shared<Friend>(module, system, "friend:a"));
    server_manager->RegisterNamedService("friend:m",
                                         std::make_shared<Friend>(module, system, "friend:m"));
    server_manager->RegisterNamedService("friend:s",
                                         std::make_shared<Friend>(module, system, "friend:s"));
    server_manager->RegisterNamedService("friend:u",
                                         std::make_shared<Friend>(module, system, "friend:u"));
    server_manager->RegisterNamedService("friend:v",
                                         std::make_shared<Friend>(module, system, "friend:v"));

    ServerManager::RunServer(std::move(server_manager));
}

} // namespace Service::Friend


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::Friend {

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 CreateFriendService(HLERequestContext& ctx);
        void CreateNotificationService(HLERequestContext& ctx);

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

void LoopProcess(Core::System& system);

} // namespace Service::Friend


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

#include "core/hle/service/friend/friend_interface.h"

namespace Service::Friend {

Friend::Friend(std::shared_ptr<Module> module_, Core::System& system_, const char* name)
    : Interface(std::move(module_), system_, name) {
    static const FunctionInfo functions[] = {
        {0, &Friend::CreateFriendService, "CreateFriendService"},
        {1, &Friend::CreateNotificationService, "CreateNotificationService"},
        {2, nullptr, "CreateDaemonSuspendSessionService"},
    };
    RegisterHandlers(functions);
}

Friend::~Friend() = default;

} // namespace Service::Friend


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

#pragma once

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

namespace Service::Friend {

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

} // namespace Service::Friend


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

#include <memory>

#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/glue/arp.h"
#include "core/hle/service/glue/errors.h"
#include "core/hle/service/glue/glue_manager.h"
#include "core/hle/service/ipc_helpers.h"

namespace Service::Glue {

namespace {
std::optional<u64> GetTitleIDForProcessID(Core::System& system, u64 process_id) {
    auto list = system.Kernel().GetProcessList();

    const auto iter = std::find_if(list.begin(), list.end(), [&process_id](auto& process) {
        return process->GetProcessId() == process_id;
    });

    if (iter == list.end()) {
        return std::nullopt;
    }

    return (*iter)->GetProgramId();
}
} // Anonymous namespace

ARP_R::ARP_R(Core::System& system_, const ARPManager& manager_)
    : ServiceFramework{system_, "arp:r"}, manager{manager_} {
    // clang-format off
        static const FunctionInfo functions[] = {
            {0, &ARP_R::GetApplicationLaunchProperty, "GetApplicationLaunchProperty"},
            {1, &ARP_R::GetApplicationLaunchPropertyWithApplicationId, "GetApplicationLaunchPropertyWithApplicationId"},
            {2, &ARP_R::GetApplicationControlProperty, "GetApplicationControlProperty"},
            {3, &ARP_R::GetApplicationControlPropertyWithApplicationId, "GetApplicationControlPropertyWithApplicationId"},
            {4, nullptr, "GetApplicationInstanceUnregistrationNotifier"},
            {5, nullptr, "ListApplicationInstanceId"},
            {6, nullptr, "GetMicroApplicationInstanceId"},
            {7, nullptr, "GetApplicationCertificate"},
            {9998, nullptr, "GetPreomiaApplicationLaunchProperty"},
            {9999, nullptr, "GetPreomiaApplicationControlProperty"},
        };
    // clang-format on

    RegisterHandlers(functions);
}

ARP_R::~ARP_R() = default;

void ARP_R::GetApplicationLaunchProperty(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto process_id = rp.PopRaw<u64>();

    LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id);

    const auto title_id = GetTitleIDForProcessID(system, process_id);
    if (!title_id.has_value()) {
        LOG_ERROR(Service_ARP, "Failed to get title ID for process ID!");
        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(Glue::ResultProcessIdNotRegistered);
        return;
    }

    ApplicationLaunchProperty launch_property{};
    const auto res = manager.GetLaunchProperty(&launch_property, *title_id);

    if (res != ResultSuccess) {
        LOG_ERROR(Service_ARP, "Failed to get launch property!");
        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(res);
        return;
    }

    IPC::ResponseBuilder rb{ctx, 6};
    rb.Push(ResultSuccess);
    rb.PushRaw(launch_property);
}

void ARP_R::GetApplicationLaunchPropertyWithApplicationId(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto title_id = rp.PopRaw<u64>();

    LOG_DEBUG(Service_ARP, "called, title_id={:016X}", title_id);

    ApplicationLaunchProperty launch_property{};
    const auto res = manager.GetLaunchProperty(&launch_property, title_id);

    if (res != ResultSuccess) {
        LOG_ERROR(Service_ARP, "Failed to get launch property!");
        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(res);
        return;
    }

    IPC::ResponseBuilder rb{ctx, 6};
    rb.Push(ResultSuccess);
    rb.PushRaw(launch_property);
}

void ARP_R::GetApplicationControlProperty(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto process_id = rp.PopRaw<u64>();

    LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id);

    const auto title_id = GetTitleIDForProcessID(system, process_id);
    if (!title_id.has_value()) {
        LOG_ERROR(Service_ARP, "Failed to get title ID for process ID!");
        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(Glue::ResultProcessIdNotRegistered);
        return;
    }

    std::vector<u8> nacp_data;
    const auto res = manager.GetControlProperty(&nacp_data, *title_id);

    if (res != ResultSuccess) {
        LOG_ERROR(Service_ARP, "Failed to get control property!");
        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(res);
        return;
    }

    ctx.WriteBuffer(nacp_data);

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

void ARP_R::GetApplicationControlPropertyWithApplicationId(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto title_id = rp.PopRaw<u64>();

    LOG_DEBUG(Service_ARP, "called, title_id={:016X}", title_id);

    std::vector<u8> nacp_data;
    const auto res = manager.GetControlProperty(&nacp_data, title_id);

    if (res != ResultSuccess) {
        LOG_ERROR(Service_ARP, "Failed to get control property!");
        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(res);
        return;
    }

    ctx.WriteBuffer(nacp_data);

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

class IRegistrar final : public ServiceFramework<IRegistrar> {
    friend class ARP_W;

public:
    using IssuerFn = std::function<Result(u64, ApplicationLaunchProperty, std::vector<u8>)>;

    explicit IRegistrar(Core::System& system_, IssuerFn&& issuer)
        : ServiceFramework{system_, "IRegistrar"}, issue_process_id{std::move(issuer)} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, &IRegistrar::Issue, "Issue"},
            {1, &IRegistrar::SetApplicationLaunchProperty, "SetApplicationLaunchProperty"},
            {2, &IRegistrar::SetApplicationControlProperty, "SetApplicationControlProperty"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }

private:
    void Issue(HLERequestContext& ctx) {
        IPC::RequestParser rp{ctx};
        const auto process_id = rp.PopRaw<u64>();

        LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id);

        if (process_id == 0) {
            LOG_ERROR(Service_ARP, "Must have non-zero process ID!");
            IPC::ResponseBuilder rb{ctx, 2};
            rb.Push(Glue::ResultInvalidProcessId);
            return;
        }

        if (issued) {
            LOG_ERROR(Service_ARP,
                      "Attempted to issue registrar, but registrar is already issued!");
            IPC::ResponseBuilder rb{ctx, 2};
            rb.Push(Glue::ResultAlreadyBound);
            return;
        }

        issue_process_id(process_id, launch, std::move(control));
        issued = true;

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

    void SetApplicationLaunchProperty(HLERequestContext& ctx) {
        LOG_DEBUG(Service_ARP, "called");

        if (issued) {
            LOG_ERROR(
                Service_ARP,
                "Attempted to set application launch property, but registrar is already issued!");
            IPC::ResponseBuilder rb{ctx, 2};
            rb.Push(Glue::ResultAlreadyBound);
            return;
        }

        IPC::RequestParser rp{ctx};
        launch = rp.PopRaw<ApplicationLaunchProperty>();

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

    void SetApplicationControlProperty(HLERequestContext& ctx) {
        LOG_DEBUG(Service_ARP, "called");

        if (issued) {
            LOG_ERROR(
                Service_ARP,
                "Attempted to set application control property, but registrar is already issued!");
            IPC::ResponseBuilder rb{ctx, 2};
            rb.Push(Glue::ResultAlreadyBound);
            return;
        }

        // TODO: Can this be a span?
        control = ctx.ReadBufferCopy();

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

    IssuerFn issue_process_id;
    bool issued = false;
    ApplicationLaunchProperty launch{};
    std::vector<u8> control;
};

ARP_W::ARP_W(Core::System& system_, ARPManager& manager_)
    : ServiceFramework{system_, "arp:w"}, manager{manager_} {
    // clang-format off
        static const FunctionInfo functions[] = {
            {0, &ARP_W::AcquireRegistrar, "AcquireRegistrar"},
            {1, &ARP_W::UnregisterApplicationInstance , "UnregisterApplicationInstance "},
            {2, nullptr, "AcquireUpdater"},
        };
    // clang-format on

    RegisterHandlers(functions);
}

ARP_W::~ARP_W() = default;

void ARP_W::AcquireRegistrar(HLERequestContext& ctx) {
    LOG_DEBUG(Service_ARP, "called");

    registrar = std::make_shared<IRegistrar>(
        system, [this](u64 process_id, ApplicationLaunchProperty launch, std::vector<u8> control) {
            const auto res = GetTitleIDForProcessID(system, process_id);
            if (!res.has_value()) {
                return Glue::ResultProcessIdNotRegistered;
            }

            return manager.Register(*res, launch, std::move(control));
        });

    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
    rb.Push(ResultSuccess);
    rb.PushIpcInterface(registrar);
}

void ARP_W::UnregisterApplicationInstance(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto process_id = rp.PopRaw<u64>();

    LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id);

    if (process_id == 0) {
        LOG_ERROR(Service_ARP, "Must have non-zero process ID!");
        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(Glue::ResultInvalidProcessId);
        return;
    }

    const auto title_id = GetTitleIDForProcessID(system, process_id);

    if (!title_id.has_value()) {
        LOG_ERROR(Service_ARP, "No title ID for process ID!");
        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(Glue::ResultProcessIdNotRegistered);
        return;
    }

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(manager.Unregister(*title_id));
}

} // namespace Service::Glue


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

#pragma once

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

namespace Service::Glue {

class ARPManager;
class IRegistrar;

class ARP_R final : public ServiceFramework<ARP_R> {
public:
    explicit ARP_R(Core::System& system_, const ARPManager& manager_);
    ~ARP_R() override;

private:
    void GetApplicationLaunchProperty(HLERequestContext& ctx);
    void GetApplicationLaunchPropertyWithApplicationId(HLERequestContext& ctx);
    void GetApplicationControlProperty(HLERequestContext& ctx);
    void GetApplicationControlPropertyWithApplicationId(HLERequestContext& ctx);

    const ARPManager& manager;
};

class ARP_W final : public ServiceFramework<ARP_W> {
public:
    explicit ARP_W(Core::System& system_, ARPManager& manager_);
    ~ARP_W() override;

private:
    void AcquireRegistrar(HLERequestContext& ctx);
    void UnregisterApplicationInstance(HLERequestContext& ctx);

    ARPManager& manager;
    std::shared_ptr<IRegistrar> registrar;
};

} // namespace Service::Glue


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

#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/service/glue/bgtc.h"
#include "core/hle/service/ipc_helpers.h"

namespace Service::Glue {

BGTC_T::BGTC_T(Core::System& system_) : ServiceFramework{system_, "bgtc:t"} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {100, &BGTC_T::OpenTaskService, "OpenTaskService"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

BGTC_T::~BGTC_T() = default;

void BGTC_T::OpenTaskService(HLERequestContext& ctx) {
    LOG_DEBUG(Service_BGTC, "called");

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

ITaskService::ITaskService(Core::System& system_) : ServiceFramework{system_, "ITaskService"} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {1, nullptr, "NotifyTaskStarting"},
        {2, nullptr, "NotifyTaskFinished"},
        {3, nullptr, "GetTriggerEvent"},
        {4, nullptr, "IsInHalfAwake"},
        {5, nullptr, "NotifyClientName"},
        {6, nullptr, "IsInFullAwake"},
        {11, nullptr, "ScheduleTask"},
        {12, nullptr, "GetScheduledTaskInterval"},
        {13, nullptr, "UnscheduleTask"},
        {14, nullptr, "GetScheduleEvent"},
        {15, nullptr, "SchedulePeriodicTask"},
        {16, nullptr, "Unknown16"},
        {101, nullptr, "GetOperationMode"},
        {102, nullptr, "WillDisconnectNetworkWhenEnteringSleep"},
        {103, nullptr, "WillStayHalfAwakeInsteadSleep"},
        {200, nullptr, "Unknown200"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

ITaskService::~ITaskService() = default;

BGTC_SC::BGTC_SC(Core::System& system_) : ServiceFramework{system_, "bgtc:sc"} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {1, nullptr, "GetState"},
        {2, nullptr, "GetStateChangedEvent"},
        {3, nullptr, "NotifyEnteringHalfAwake"},
        {4, nullptr, "NotifyLeavingHalfAwake"},
        {5, nullptr, "SetIsUsingSleepUnsupportedDevices"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

BGTC_SC::~BGTC_SC() = default;

} // namespace Service::Glue


code:
// SPDX-FileCopyrightText: Copyright 2019 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::Glue {

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

    void OpenTaskService(HLERequestContext& ctx);
};

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

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

} // namespace Service::Glue


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

#include "core/hle/service/glue/ectx.h"
#include "core/hle/service/ipc_helpers.h"

namespace Service::Glue {

// This is nn::err::context::IContextRegistrar
class IContextRegistrar : public ServiceFramework<IContextRegistrar> {
public:
    IContextRegistrar(Core::System& system_) : ServiceFramework{system_, "IContextRegistrar"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, &IContextRegistrar::Complete, "Complete"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }

    ~IContextRegistrar() override = default;

private:
    void Complete(HLERequestContext& ctx) {
        struct InputParameters {
            u32 unk;
        };
        struct OutputParameters {
            u32 unk;
        };

        IPC::RequestParser rp{ctx};
        [[maybe_unused]] auto input = rp.PopRaw<InputParameters>();
        [[maybe_unused]] auto value = ctx.ReadBuffer();

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

ECTX_AW::ECTX_AW(Core::System& system_) : ServiceFramework{system_, "ectx:aw"} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0, &ECTX_AW::CreateContextRegistrar, "CreateContextRegistrar"},
        {1, nullptr, "CommitContext"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

ECTX_AW::~ECTX_AW() = default;

void ECTX_AW::CreateContextRegistrar(HLERequestContext& ctx) {
    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
    rb.Push(ResultSuccess);
    rb.PushIpcInterface<IContextRegistrar>(std::make_shared<IContextRegistrar>(system));
}

} // namespace Service::Glue


code:
// SPDX-FileCopyrightText: Copyright 2021 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::Glue {

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

private:
    void CreateContextRegistrar(HLERequestContext& ctx);
};

} // namespace Service::Glue


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

#pragma once

#include "core/hle/result.h"

namespace Service::Glue {

constexpr Result ResultInvalidProcessId{ErrorModule::ARP, 31};
constexpr Result ResultAlreadyBound{ErrorModule::ARP, 42};
constexpr Result ResultProcessIdNotRegistered{ErrorModule::ARP, 102};

} // namespace Service::Glue


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

#include <memory>
#include "core/core.h"
#include "core/hle/service/glue/arp.h"
#include "core/hle/service/glue/bgtc.h"
#include "core/hle/service/glue/ectx.h"
#include "core/hle/service/glue/glue.h"
#include "core/hle/service/glue/notif.h"
#include "core/hle/service/glue/time/manager.h"
#include "core/hle/service/glue/time/static.h"
#include "core/hle/service/psc/time/common.h"
#include "core/hle/service/server_manager.h"

namespace Service::Glue {

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

    // ARP
    server_manager->RegisterNamedService("arp:r",
                                         std::make_shared<ARP_R>(system, system.GetARPManager()));
    server_manager->RegisterNamedService("arp:w",
                                         std::make_shared<ARP_W>(system, system.GetARPManager()));

    // BackGround Task Controller
    server_manager->RegisterNamedService("bgtc:t", std::make_shared<BGTC_T>(system));
    server_manager->RegisterNamedService("bgtc:sc", std::make_shared<BGTC_SC>(system));

    // Error Context
    server_manager->RegisterNamedService("ectx:aw", std::make_shared<ECTX_AW>(system));

    // Notification Services
    server_manager->RegisterNamedService(
        "notif:a", std::make_shared<INotificationServicesForApplication>(system));
    server_manager->RegisterNamedService("notif:s",
                                         std::make_shared<INotificationServices>(system));

    // Time
    auto time = std::make_shared<Time::TimeManager>(system);

    server_manager->RegisterNamedService(
        "time:u",
        std::make_shared<Time::StaticService>(
            system, Service::PSC::Time::StaticServiceSetupInfo{0, 0, 0, 0, 0, 0}, time, "time:u"));
    server_manager->RegisterNamedService(
        "time:a",
        std::make_shared<Time::StaticService>(
            system, Service::PSC::Time::StaticServiceSetupInfo{1, 1, 0, 1, 0, 0}, time, "time:a"));
    server_manager->RegisterNamedService(
        "time:r",
        std::make_shared<Time::StaticService>(
            system, Service::PSC::Time::StaticServiceSetupInfo{0, 0, 0, 0, 1, 0}, time, "time:r"));

    ServerManager::RunServer(std::move(server_manager));
}

} // namespace Service::Glue


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

#pragma once

namespace Core {
class System;
} // namespace Core

namespace Service::Glue {

void LoopProcess(Core::System& system);

} // namespace Service::Glue


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

#include "core/hle/service/glue/errors.h"
#include "core/hle/service/glue/glue_manager.h"

namespace Service::Glue {

struct ARPManager::MapEntry {
    ApplicationLaunchProperty launch;
    std::vector<u8> control;
};

ARPManager::ARPManager() = default;

ARPManager::~ARPManager() = default;

Result ARPManager::GetLaunchProperty(ApplicationLaunchProperty* out_launch_property,
                                     u64 title_id) const {
    if (title_id == 0) {
        return Glue::ResultInvalidProcessId;
    }

    const auto iter = entries.find(title_id);
    if (iter == entries.end()) {
        return Glue::ResultProcessIdNotRegistered;
    }

    *out_launch_property = iter->second.launch;
    return ResultSuccess;
}

Result ARPManager::GetControlProperty(std::vector<u8>* out_control_property, u64 title_id) const {
    if (title_id == 0) {
        return Glue::ResultInvalidProcessId;
    }

    const auto iter = entries.find(title_id);
    if (iter == entries.end()) {
        return Glue::ResultProcessIdNotRegistered;
    }

    *out_control_property = iter->second.control;
    return ResultSuccess;
}

Result ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch,
                            std::vector<u8> control) {
    if (title_id == 0) {
        return Glue::ResultInvalidProcessId;
    }

    const auto iter = entries.find(title_id);
    if (iter != entries.end()) {
        return Glue::ResultAlreadyBound;
    }

    entries.insert_or_assign(title_id, MapEntry{launch, std::move(control)});
    return ResultSuccess;
}

Result ARPManager::Unregister(u64 title_id) {
    if (title_id == 0) {
        return Glue::ResultInvalidProcessId;
    }

    const auto iter = entries.find(title_id);
    if (iter == entries.end()) {
        return Glue::ResultProcessIdNotRegistered;
    }

    entries.erase(iter);
    return ResultSuccess;
}

void ARPManager::ResetAll() {
    entries.clear();
}

} // namespace Service::Glue


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

#pragma once

#include <map>
#include <vector>
#include "common/common_types.h"
#include "core/file_sys/romfs_factory.h"
#include "core/hle/result.h"

namespace Service::Glue {

struct ApplicationLaunchProperty {
    u64 title_id;
    u32 version;
    FileSys::StorageId base_game_storage_id;
    FileSys::StorageId update_storage_id;
    u8 program_index;
    u8 reserved;
};
static_assert(sizeof(ApplicationLaunchProperty) == 0x10,
              "ApplicationLaunchProperty has incorrect size.");

// A class to manage state related to the arp:w and arp:r services, specifically the registration
// and unregistration of launch and control properties.
class ARPManager {
public:
    ARPManager();
    ~ARPManager();

    // Returns the ApplicationLaunchProperty corresponding to the provided title ID if it was
    // previously registered, otherwise ResultProcessIdNotRegistered if it was never registered or
    // ResultInvalidProcessId if the title ID is 0.
    Result GetLaunchProperty(ApplicationLaunchProperty* out_launch_property, u64 title_id) const;

    // Returns a vector of the raw bytes of NACP data (necessarily 0x4000 in size) corresponding to
    // the provided title ID if it was previously registered, otherwise ResultProcessIdNotRegistered
    // if it was never registered or ResultInvalidProcessId if the title ID is 0.
    Result GetControlProperty(std::vector<u8>* out_control_property, u64 title_id) const;

    // Adds a new entry to the internal database with the provided parameters, returning
    // ResultProcessIdNotRegistered if attempting to re-register a title ID without an intermediate
    // Unregister step, and ResultInvalidProcessId if the title ID is 0.
    Result Register(u64 title_id, ApplicationLaunchProperty launch, std::vector<u8> control);

    // Removes the registration for the provided title ID from the database, returning
    // ResultProcessIdNotRegistered if it doesn't exist in the database and ResultInvalidProcessId
    // if the title ID is 0.
    Result Unregister(u64 title_id);

    // Removes all entries from the database, always succeeds. Should only be used when resetting
    // system state.
    void ResetAll();

private:
    struct MapEntry;
    std::map<u64, MapEntry> entries;
};

} // namespace Service::Glue


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

#include <algorithm>
#include <cstring>

#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/glue/notif.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/kernel_helpers.h"

namespace Service::Glue {

namespace {

constexpr inline std::size_t MaxAlarms = 8;

}

Result NotificationServiceImpl::RegisterAlarmSetting(AlarmSettingId* out_alarm_setting_id,
                                                     const AlarmSetting& alarm_setting,
                                                     std::span<const u8> application_parameter) {
    if (alarms.size() > MaxAlarms) {
        LOG_ERROR(Service_NOTIF, "Alarm limit reached");
        R_THROW(ResultUnknown);
    }

    ASSERT_MSG(application_parameter.size() <= sizeof(ApplicationParameter),
               "application_parameter_size is bigger than 0x400 bytes");

    AlarmSetting new_alarm = alarm_setting;
    new_alarm.alarm_setting_id = last_alarm_setting_id++;
    alarms.push_back(new_alarm);

    // TODO: Save application parameter data

    LOG_WARNING(Service_NOTIF,
                "(STUBBED) called, application_parameter_size={}, setting_id={}, kind={}, muted={}",
                application_parameter.size(), new_alarm.alarm_setting_id, new_alarm.kind,
                new_alarm.muted);

    *out_alarm_setting_id = new_alarm.alarm_setting_id;
    R_SUCCEED();
}

Result NotificationServiceImpl::UpdateAlarmSetting(const AlarmSetting& alarm_setting,
                                                   std::span<const u8> application_parameter) {
    ASSERT_MSG(application_parameter.size() <= sizeof(ApplicationParameter),
               "application_parameter_size is bigger than 0x400 bytes");

    const auto alarm_it = GetAlarmFromId(alarm_setting.alarm_setting_id);
    if (alarm_it != alarms.end()) {
        LOG_DEBUG(Service_NOTIF, "Alarm updated");
        *alarm_it = alarm_setting;
    }

    LOG_WARNING(Service_NOTIF,
                "(STUBBED) called, application_parameter_size={}, setting_id={}, kind={}, muted={}",
                application_parameter.size(), alarm_setting.alarm_setting_id, alarm_setting.kind,
                alarm_setting.muted);
    R_SUCCEED();
}

Result NotificationServiceImpl::ListAlarmSettings(s32* out_count,
                                                  std::span<AlarmSetting> out_alarms) {
    LOG_INFO(Service_NOTIF, "called, alarm_count={}", alarms.size());

    const auto count = std::min(out_alarms.size(), alarms.size());
    for (size_t i = 0; i < count; i++) {
        out_alarms[i] = alarms[i];
    }

    *out_count = static_cast<s32>(count);
    R_SUCCEED();
}

Result NotificationServiceImpl::LoadApplicationParameter(u32* out_size,
                                                         std::span<u8> out_application_parameter,
                                                         AlarmSettingId alarm_setting_id) {
    const auto alarm_it = GetAlarmFromId(alarm_setting_id);
    if (alarm_it == alarms.end()) {
        LOG_ERROR(Service_NOTIF, "Invalid alarm setting id={}", alarm_setting_id);
        R_THROW(ResultUnknown);
    }

    // TODO: Read application parameter related to this setting id
    ApplicationParameter application_parameter{};

    LOG_WARNING(Service_NOTIF, "(STUBBED) called, alarm_setting_id={}", alarm_setting_id);
    std::memcpy(out_application_parameter.data(), application_parameter.data(),
                std::min(sizeof(application_parameter), out_application_parameter.size()));

    *out_size = static_cast<u32>(application_parameter.size());
    R_SUCCEED();
}

Result NotificationServiceImpl::DeleteAlarmSetting(AlarmSettingId alarm_setting_id) {
    std::erase_if(alarms, [alarm_setting_id](const AlarmSetting& alarm) {
        return alarm.alarm_setting_id == alarm_setting_id;
    });

    LOG_INFO(Service_NOTIF, "called, alarm_setting_id={}", alarm_setting_id);

    R_SUCCEED();
}

Result NotificationServiceImpl::Initialize(u64 aruid) {
    // TODO: Load previous alarms from config

    LOG_WARNING(Service_NOTIF, "(STUBBED) called");
    R_SUCCEED();
}

std::vector<AlarmSetting>::iterator NotificationServiceImpl::GetAlarmFromId(
    AlarmSettingId alarm_setting_id) {
    return std::find_if(alarms.begin(), alarms.end(),
                        [alarm_setting_id](const AlarmSetting& alarm) {
                            return alarm.alarm_setting_id == alarm_setting_id;
                        });
}

INotificationServicesForApplication::INotificationServicesForApplication(Core::System& system_)
    : ServiceFramework{system_, "notif:a"} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {500, D<&INotificationServicesForApplication::RegisterAlarmSetting>, "RegisterAlarmSetting"},
        {510, D<&INotificationServicesForApplication::UpdateAlarmSetting>, "UpdateAlarmSetting"},
        {520, D<&INotificationServicesForApplication::ListAlarmSettings>, "ListAlarmSettings"},
        {530, D<&INotificationServicesForApplication::LoadApplicationParameter>, "LoadApplicationParameter"},
        {540, D<&INotificationServicesForApplication::DeleteAlarmSetting>, "DeleteAlarmSetting"},
        {1000, D<&INotificationServicesForApplication::Initialize>, "Initialize"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

INotificationServicesForApplication::~INotificationServicesForApplication() = default;

Result INotificationServicesForApplication::RegisterAlarmSetting(
    Out<AlarmSettingId> out_alarm_setting_id,
    InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting,
    InBuffer<BufferAttr_HipcMapAlias> application_parameter) {
    R_RETURN(impl.RegisterAlarmSetting(out_alarm_setting_id.Get(), *alarm_setting,
                                       application_parameter));
}

Result INotificationServicesForApplication::UpdateAlarmSetting(
    InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting,
    InBuffer<BufferAttr_HipcMapAlias> application_parameter) {
    R_RETURN(impl.UpdateAlarmSetting(*alarm_setting, application_parameter));
}

Result INotificationServicesForApplication::ListAlarmSettings(
    Out<s32> out_count, OutArray<AlarmSetting, BufferAttr_HipcMapAlias> out_alarms) {
    R_RETURN(impl.ListAlarmSettings(out_count.Get(), out_alarms));
}

Result INotificationServicesForApplication::LoadApplicationParameter(
    Out<u32> out_size, OutBuffer<BufferAttr_HipcMapAlias> out_application_parameter,
    AlarmSettingId alarm_setting_id) {
    R_RETURN(
        impl.LoadApplicationParameter(out_size.Get(), out_application_parameter, alarm_setting_id));
}

Result INotificationServicesForApplication::DeleteAlarmSetting(AlarmSettingId alarm_setting_id) {
    R_RETURN(impl.DeleteAlarmSetting(alarm_setting_id));
}

Result INotificationServicesForApplication::Initialize(ClientAppletResourceUserId aruid) {
    R_RETURN(impl.Initialize(*aruid));
}

class INotificationSystemEventAccessor final
    : public ServiceFramework<INotificationSystemEventAccessor> {
public:
    explicit INotificationSystemEventAccessor(Core::System& system_)
        : ServiceFramework{system_, "INotificationSystemEventAccessor"},
          service_context{system_, "INotificationSystemEventAccessor"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, D<&INotificationSystemEventAccessor::GetSystemEvent>, "GetSystemEvent"},
        };
        // clang-format on

        RegisterHandlers(functions);

        notification_event =
            service_context.CreateEvent("INotificationSystemEventAccessor:NotificationEvent");
    }

    ~INotificationSystemEventAccessor() {
        service_context.CloseEvent(notification_event);
    }

private:
    Result GetSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_readable_event) {
        LOG_WARNING(Service_NOTIF, "(STUBBED) called");

        *out_readable_event = &notification_event->GetReadableEvent();
        R_SUCCEED();
    }

    KernelHelpers::ServiceContext service_context;
    Kernel::KEvent* notification_event;
};

INotificationServices::INotificationServices(Core::System& system_)
    : ServiceFramework{system_, "notif:s"} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {500, D<&INotificationServices::RegisterAlarmSetting>, "RegisterAlarmSetting"},
        {510, D<&INotificationServices::UpdateAlarmSetting>, "UpdateAlarmSetting"},
        {520, D<&INotificationServices::ListAlarmSettings>, "ListAlarmSettings"},
        {530, D<&INotificationServices::LoadApplicationParameter>, "LoadApplicationParameter"},
        {540, D<&INotificationServices::DeleteAlarmSetting>, "DeleteAlarmSetting"},
        {1000, D<&INotificationServices::Initialize>, "Initialize"},
        {1010, nullptr, "ListNotifications"},
        {1020, nullptr, "DeleteNotification"},
        {1030, nullptr, "ClearNotifications"},
        {1040, D<&INotificationServices::OpenNotificationSystemEventAccessor>, "OpenNotificationSystemEventAccessor"},
        {1500, nullptr, "SetNotificationPresentationSetting"},
        {1510, D<&INotificationServices::GetNotificationPresentationSetting>, "GetNotificationPresentationSetting"},
        {2000, nullptr, "GetAlarmSetting"},
        {2001, nullptr, "GetAlarmSettingWithApplicationParameter"},
        {2010, nullptr, "MuteAlarmSetting"},
        {2020, nullptr, "IsAlarmSettingReady"},
        {8000, nullptr, "RegisterAppletResourceUserId"},
        {8010, nullptr, "UnregisterAppletResourceUserId"},
        {8999, nullptr, "GetCurrentTime"},
        {9000, nullptr, "GetAlarmSettingNextNotificationTime"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

INotificationServices::~INotificationServices() = default;

Result INotificationServices::RegisterAlarmSetting(
    Out<AlarmSettingId> out_alarm_setting_id,
    InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting,
    InBuffer<BufferAttr_HipcMapAlias> application_parameter) {
    R_RETURN(impl.RegisterAlarmSetting(out_alarm_setting_id.Get(), *alarm_setting,
                                       application_parameter));
}

Result INotificationServices::UpdateAlarmSetting(
    InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting,
    InBuffer<BufferAttr_HipcMapAlias> application_parameter) {
    R_RETURN(impl.UpdateAlarmSetting(*alarm_setting, application_parameter));
}

Result INotificationServices::ListAlarmSettings(
    Out<s32> out_count, OutArray<AlarmSetting, BufferAttr_HipcMapAlias> out_alarms) {
    R_RETURN(impl.ListAlarmSettings(out_count.Get(), out_alarms));
}

Result INotificationServices::LoadApplicationParameter(
    Out<u32> out_size, OutBuffer<BufferAttr_HipcMapAlias> out_application_parameter,
    AlarmSettingId alarm_setting_id) {
    R_RETURN(
        impl.LoadApplicationParameter(out_size.Get(), out_application_parameter, alarm_setting_id));
}

Result INotificationServices::DeleteAlarmSetting(AlarmSettingId alarm_setting_id) {
    R_RETURN(impl.DeleteAlarmSetting(alarm_setting_id));
}

Result INotificationServices::Initialize(ClientAppletResourceUserId aruid) {
    R_RETURN(impl.Initialize(*aruid));
}

Result INotificationServices::OpenNotificationSystemEventAccessor(
    Out<SharedPointer<INotificationSystemEventAccessor>> out_notification_system_event_accessor) {
    LOG_WARNING(Service_NOTIF, "(STUBBED) called");

    *out_notification_system_event_accessor =
        std::make_shared<INotificationSystemEventAccessor>(system);
    R_SUCCEED();
}

Result INotificationServices::GetNotificationPresentationSetting(
    Out<NotificationPresentationSetting> out_notification_presentation_setting,
    NotificationChannel notification_channel) {
    LOG_WARNING(Service_NOTIF, "(STUBBED) called");

    *out_notification_presentation_setting = {};
    R_SUCCEED();
}

} // namespace Service::Glue


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

#pragma once

#include <array>
#include <vector>

#include "common/uuid.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Service::Glue {

// This is nn::notification::AlarmSettingId
using AlarmSettingId = u16;
static_assert(sizeof(AlarmSettingId) == 0x2, "AlarmSettingId is an invalid size");

using ApplicationParameter = std::array<u8, 0x400>;
static_assert(sizeof(ApplicationParameter) == 0x400, "ApplicationParameter is an invalid size");

struct DailyAlarmSetting {
    s8 hour;
    s8 minute;
};
static_assert(sizeof(DailyAlarmSetting) == 0x2, "DailyAlarmSetting is an invalid size");

struct WeeklyScheduleAlarmSetting {
    INSERT_PADDING_BYTES_NOINIT(0xA);
    std::array<DailyAlarmSetting, 0x7> day_of_week;
};
static_assert(sizeof(WeeklyScheduleAlarmSetting) == 0x18,
              "WeeklyScheduleAlarmSetting is an invalid size");

// This is nn::notification::AlarmSetting
struct AlarmSetting {
    AlarmSettingId alarm_setting_id;
    u8 kind;
    u8 muted;
    INSERT_PADDING_BYTES_NOINIT(0x4);
    Common::UUID account_id;
    u64 application_id;
    INSERT_PADDING_BYTES_NOINIT(0x8);
    WeeklyScheduleAlarmSetting schedule;
};
static_assert(sizeof(AlarmSetting) == 0x40, "AlarmSetting is an invalid size");

enum class NotificationChannel : u8 {
    Unknown0 = 0,
};

struct NotificationPresentationSetting {
    INSERT_PADDING_BYTES_NOINIT(0x10);
};
static_assert(sizeof(NotificationPresentationSetting) == 0x10,
              "NotificationPresentationSetting is an invalid size");

class NotificationServiceImpl {
public:
    Result RegisterAlarmSetting(AlarmSettingId* out_alarm_setting_id,
                                const AlarmSetting& alarm_setting,
                                std::span<const u8> application_parameter);
    Result UpdateAlarmSetting(const AlarmSetting& alarm_setting,
                              std::span<const u8> application_parameter);
    Result ListAlarmSettings(s32* out_count, std::span<AlarmSetting> out_alarms);
    Result LoadApplicationParameter(u32* out_size, std::span<u8> out_application_parameter,
                                    AlarmSettingId alarm_setting_id);
    Result DeleteAlarmSetting(AlarmSettingId alarm_setting_id);
    Result Initialize(u64 aruid);

private:
    std::vector<AlarmSetting>::iterator GetAlarmFromId(AlarmSettingId alarm_setting_id);
    std::vector<AlarmSetting> alarms{};
    AlarmSettingId last_alarm_setting_id{};
};

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

private:
    Result RegisterAlarmSetting(Out<AlarmSettingId> out_alarm_setting_id,
                                InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting,
                                InBuffer<BufferAttr_HipcMapAlias> application_parameter);
    Result UpdateAlarmSetting(InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting,
                              InBuffer<BufferAttr_HipcMapAlias> application_parameter);
    Result ListAlarmSettings(Out<s32> out_count,
                             OutArray<AlarmSetting, BufferAttr_HipcMapAlias> out_alarms);
    Result LoadApplicationParameter(Out<u32> out_size,
                                    OutBuffer<BufferAttr_HipcMapAlias> out_application_parameter,
                                    AlarmSettingId alarm_setting_id);
    Result DeleteAlarmSetting(AlarmSettingId alarm_setting_id);
    Result Initialize(ClientAppletResourceUserId aruid);

    NotificationServiceImpl impl;
};

class INotificationSystemEventAccessor;

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

private:
    Result RegisterAlarmSetting(Out<AlarmSettingId> out_alarm_setting_id,
                                InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting,
                                InBuffer<BufferAttr_HipcMapAlias> application_parameter);
    Result UpdateAlarmSetting(InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting,
                              InBuffer<BufferAttr_HipcMapAlias> application_parameter);
    Result ListAlarmSettings(Out<s32> out_count,
                             OutArray<AlarmSetting, BufferAttr_HipcMapAlias> out_alarms);
    Result LoadApplicationParameter(Out<u32> out_size,
                                    OutBuffer<BufferAttr_HipcMapAlias> out_application_parameter,
                                    AlarmSettingId alarm_setting_id);
    Result DeleteAlarmSetting(AlarmSettingId alarm_setting_id);
    Result Initialize(ClientAppletResourceUserId aruid);
    Result OpenNotificationSystemEventAccessor(Out<SharedPointer<INotificationSystemEventAccessor>>
                                                   out_notification_system_event_accessor);
    Result GetNotificationPresentationSetting(
        Out<NotificationPresentationSetting> out_notification_presentation_setting,
        NotificationChannel notification_channel);

    NotificationServiceImpl impl;
};
} // namespace Service::Glue