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

#include <memory>

#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/btm/btm_user_core.h"
#include "core/hle/service/cmif_serialization.h"

namespace Service::BTM {

IBtmUserCore::IBtmUserCore(Core::System& system_)
    : ServiceFramework{system_, "IBtmUserCore"}, service_context{system_, "IBtmUserCore"} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0, C<&IBtmUserCore::AcquireBleScanEvent>, "AcquireBleScanEvent"},
        {1, nullptr, "GetBleScanFilterParameter"},
        {2, nullptr, "GetBleScanFilterParameter2"},
        {3, nullptr, "StartBleScanForGeneral"},
        {4, nullptr, "StopBleScanForGeneral"},
        {5, nullptr, "GetBleScanResultsForGeneral"},
        {6, nullptr, "StartBleScanForPaired"},
        {7, nullptr, "StopBleScanForPaired"},
        {8, nullptr, "StartBleScanForSmartDevice"},
        {9, nullptr, "StopBleScanForSmartDevice"},
        {10, nullptr, "GetBleScanResultsForSmartDevice"},
        {17, C<&IBtmUserCore::AcquireBleConnectionEvent>, "AcquireBleConnectionEvent"},
        {18, nullptr, "BleConnect"},
        {19, nullptr, "BleDisconnect"},
        {20, nullptr, "BleGetConnectionState"},
        {21, nullptr, "AcquireBlePairingEvent"},
        {22, nullptr, "BlePairDevice"},
        {23, nullptr, "BleUnPairDevice"},
        {24, nullptr, "BleUnPairDevice2"},
        {25, nullptr, "BleGetPairedDevices"},
        {26, C<&IBtmUserCore::AcquireBleServiceDiscoveryEvent>, "AcquireBleServiceDiscoveryEvent"},
        {27, nullptr, "GetGattServices"},
        {28, nullptr, "GetGattService"},
        {29, nullptr, "GetGattIncludedServices"},
        {30, nullptr, "GetBelongingGattService"},
        {31, nullptr, "GetGattCharacteristics"},
        {32, nullptr, "GetGattDescriptors"},
        {33, C<&IBtmUserCore::AcquireBleMtuConfigEvent>, "AcquireBleMtuConfigEvent"},
        {34, nullptr, "ConfigureBleMtu"},
        {35, nullptr, "GetBleMtu"},
        {36, nullptr, "RegisterBleGattDataPath"},
        {37, nullptr, "UnregisterBleGattDataPath"},
    };
    // clang-format on
    RegisterHandlers(functions);

    scan_event = service_context.CreateEvent("IBtmUserCore:ScanEvent");
    connection_event = service_context.CreateEvent("IBtmUserCore:ConnectionEvent");
    service_discovery_event = service_context.CreateEvent("IBtmUserCore:DiscoveryEvent");
    config_event = service_context.CreateEvent("IBtmUserCore:ConfigEvent");
}

IBtmUserCore::~IBtmUserCore() {
    service_context.CloseEvent(scan_event);
    service_context.CloseEvent(connection_event);
    service_context.CloseEvent(service_discovery_event);
    service_context.CloseEvent(config_event);
}

Result IBtmUserCore::AcquireBleScanEvent(Out<bool> out_is_valid,
                                         OutCopyHandle<Kernel::KReadableEvent> out_event) {
    LOG_WARNING(Service_BTM, "(STUBBED) called");

    *out_is_valid = true;
    *out_event = &scan_event->GetReadableEvent();
    R_SUCCEED();
}

Result IBtmUserCore::AcquireBleConnectionEvent(Out<bool> out_is_valid,
                                               OutCopyHandle<Kernel::KReadableEvent> out_event) {
    LOG_WARNING(Service_BTM, "(STUBBED) called");

    *out_is_valid = true;
    *out_event = &connection_event->GetReadableEvent();
    R_SUCCEED();
}

Result IBtmUserCore::AcquireBleServiceDiscoveryEvent(
    Out<bool> out_is_valid, OutCopyHandle<Kernel::KReadableEvent> out_event) {
    LOG_WARNING(Service_BTM, "(STUBBED) called");

    *out_is_valid = true;
    *out_event = &service_discovery_event->GetReadableEvent();
    R_SUCCEED();
}

Result IBtmUserCore::AcquireBleMtuConfigEvent(Out<bool> out_is_valid,
                                              OutCopyHandle<Kernel::KReadableEvent> out_event) {
    LOG_WARNING(Service_BTM, "(STUBBED) called");

    *out_is_valid = true;
    *out_event = &config_event->GetReadableEvent();
    R_SUCCEED();
}

} // namespace Service::BTM


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

#pragma once

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

namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel

namespace Core {
class System;
}

namespace Service::BTM {

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

private:
    Result AcquireBleScanEvent(Out<bool> out_is_valid,
                               OutCopyHandle<Kernel::KReadableEvent> out_event);

    Result AcquireBleConnectionEvent(Out<bool> out_is_valid,
                                     OutCopyHandle<Kernel::KReadableEvent> out_event);

    Result AcquireBleServiceDiscoveryEvent(Out<bool> out_is_valid,
                                           OutCopyHandle<Kernel::KReadableEvent> out_event);

    Result AcquireBleMtuConfigEvent(Out<bool> out_is_valid,
                                    OutCopyHandle<Kernel::KReadableEvent> out_event);

    KernelHelpers::ServiceContext service_context;

    Kernel::KEvent* scan_event;
    Kernel::KEvent* connection_event;
    Kernel::KEvent* service_discovery_event;
    Kernel::KEvent* config_event;
};

} // namespace Service::BTM

repo_name: litucks/torzu

license: mit


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

#include "core/hle/service/caps/caps.h"
#include "core/hle/service/caps/caps_a.h"
#include "core/hle/service/caps/caps_c.h"
#include "core/hle/service/caps/caps_manager.h"
#include "core/hle/service/caps/caps_sc.h"
#include "core/hle/service/caps/caps_ss.h"
#include "core/hle/service/caps/caps_su.h"
#include "core/hle/service/caps/caps_u.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"

namespace Service::Capture {

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

    server_manager->RegisterNamedService(
        "caps:a", std::make_shared<IAlbumAccessorService>(system, album_manager));
    server_manager->RegisterNamedService(
        "caps:c", std::make_shared<IAlbumControlService>(system, album_manager));
    server_manager->RegisterNamedService(
        "caps:u", std::make_shared<IAlbumApplicationService>(system, album_manager));

    server_manager->RegisterNamedService(
        "caps:ss", std::make_shared<IScreenShotService>(system, album_manager));
    server_manager->RegisterNamedService("caps:sc",
                                         std::make_shared<IScreenShotControlService>(system));
    server_manager->RegisterNamedService(
        "caps:su", std::make_shared<IScreenShotApplicationService>(system, album_manager));

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

} // namespace Service::Capture


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

#pragma once

namespace Core {
class System;
}

namespace Service::Capture {

void LoopProcess(Core::System& system);

} // namespace Service::Capture


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

#include "common/logging/log.h"
#include "core/hle/service/caps/caps_a.h"
#include "core/hle/service/caps/caps_manager.h"
#include "core/hle/service/caps/caps_result.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ipc_helpers.h"

namespace Service::Capture {

IAlbumAccessorService::IAlbumAccessorService(Core::System& system_,
                                             std::shared_ptr<AlbumManager> album_manager)
    : ServiceFramework{system_, "caps:a"}, manager{album_manager} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0, nullptr, "GetAlbumFileCount"},
        {1, C<&IAlbumAccessorService::GetAlbumFileList>, "GetAlbumFileList"},
        {2, nullptr, "LoadAlbumFile"},
        {3, C<&IAlbumAccessorService::DeleteAlbumFile>, "DeleteAlbumFile"},
        {4, nullptr, "StorageCopyAlbumFile"},
        {5, C<&IAlbumAccessorService::IsAlbumMounted>, "IsAlbumMounted"},
        {6, nullptr, "GetAlbumUsage"},
        {7, nullptr, "GetAlbumFileSize"},
        {8, nullptr, "LoadAlbumFileThumbnail"},
        {9, nullptr, "LoadAlbumScreenShotImage"},
        {10, nullptr, "LoadAlbumScreenShotThumbnailImage"},
        {11, nullptr, "GetAlbumEntryFromApplicationAlbumEntry"},
        {12, nullptr, "LoadAlbumScreenShotImageEx"},
        {13, nullptr, "LoadAlbumScreenShotThumbnailImageEx"},
        {14, nullptr, "LoadAlbumScreenShotImageEx0"},
        {15, nullptr, "GetAlbumUsage3"},
        {16, nullptr, "GetAlbumMountResult"},
        {17, nullptr, "GetAlbumUsage16"},
        {18, C<&IAlbumAccessorService::Unknown18>, "Unknown18"},
        {19, nullptr, "Unknown19"},
        {100, nullptr, "GetAlbumFileCountEx0"},
        {101, C<&IAlbumAccessorService::GetAlbumFileListEx0>, "GetAlbumFileListEx0"},
        {202, nullptr, "SaveEditedScreenShot"},
        {301, nullptr, "GetLastThumbnail"},
        {302, nullptr, "GetLastOverlayMovieThumbnail"},
        {401,  C<&IAlbumAccessorService::GetAutoSavingStorage>, "GetAutoSavingStorage"},
        {501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"},
        {1001, nullptr, "LoadAlbumScreenShotThumbnailImageEx0"},
        {1002, C<&IAlbumAccessorService::LoadAlbumScreenShotImageEx1>, "LoadAlbumScreenShotImageEx1"},
        {1003, C<&IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1>, "LoadAlbumScreenShotThumbnailImageEx1"},
        {8001, nullptr, "ForceAlbumUnmounted"},
        {8002, nullptr, "ResetAlbumMountStatus"},
        {8011, nullptr, "RefreshAlbumCache"},
        {8012, nullptr, "GetAlbumCache"},
        {8013, nullptr, "GetAlbumCacheEx"},
        {8021, nullptr, "GetAlbumEntryFromApplicationAlbumEntryAruid"},
        {10011, nullptr, "SetInternalErrorConversionEnabled"},
        {50000, nullptr, "LoadMakerNoteInfoForDebug"},
        {60002, nullptr, "OpenAccessorSession"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

IAlbumAccessorService::~IAlbumAccessorService() = default;

Result IAlbumAccessorService::GetAlbumFileList(
    Out<u64> out_count, AlbumStorage storage,
    OutArray<AlbumEntry, BufferAttr_HipcMapAlias> out_entries) {
    LOG_INFO(Service_Capture, "called, storage={}", storage);

    const Result result = manager->GetAlbumFileList(out_entries, *out_count, storage, 0);
    R_RETURN(TranslateResult(result));
}

Result IAlbumAccessorService::DeleteAlbumFile(AlbumFileId file_id) {
    LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}",
             file_id.application_id, file_id.storage, file_id.type);

    const Result result = manager->DeleteAlbumFile(file_id);
    R_RETURN(TranslateResult(result));
}

Result IAlbumAccessorService::IsAlbumMounted(Out<bool> out_is_mounted, AlbumStorage storage) {
    LOG_INFO(Service_Capture, "called, storage={}", storage);

    const Result result = manager->IsAlbumMounted(storage);
    *out_is_mounted = result.IsSuccess();
    R_RETURN(TranslateResult(result));
}

Result IAlbumAccessorService::Unknown18(
    Out<u32> out_buffer_size,
    OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_buffer) {
    LOG_WARNING(Service_Capture, "(STUBBED) called");
    *out_buffer_size = 0;
    R_SUCCEED();
}

Result IAlbumAccessorService::GetAlbumFileListEx0(
    Out<u64> out_entries_size, AlbumStorage storage, u8 flags,
    OutArray<AlbumEntry, BufferAttr_HipcMapAlias> out_entries) {
    LOG_INFO(Service_Capture, "called, storage={}, flags={}", storage, flags);

    const Result result = manager->GetAlbumFileList(out_entries, *out_entries_size, storage, flags);
    R_RETURN(TranslateResult(result));
}

Result IAlbumAccessorService::GetAutoSavingStorage(Out<bool> out_is_autosaving) {
    LOG_WARNING(Service_Capture, "(STUBBED) called");

    const Result result = manager->GetAutoSavingStorage(*out_is_autosaving);
    R_RETURN(TranslateResult(result));
}

Result IAlbumAccessorService::LoadAlbumScreenShotImageEx1(
    const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options,
    OutLargeData<LoadAlbumScreenShotImageOutput, BufferAttr_HipcMapAlias> out_image_output,
    OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image,
    OutArray<u8, BufferAttr_HipcMapAlias> out_buffer) {
    LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}",
             file_id.application_id, file_id.storage, file_id.type, decoder_options.flags);

    const Result result =
        manager->LoadAlbumScreenShotImage(*out_image_output, out_image, file_id, decoder_options);
    R_RETURN(TranslateResult(result));
}

Result IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1(
    const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options,
    OutLargeData<LoadAlbumScreenShotImageOutput, BufferAttr_HipcMapAlias> out_image_output,
    OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image,
    OutArray<u8, BufferAttr_HipcMapAlias> out_buffer) {
    LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}",
             file_id.application_id, file_id.storage, file_id.type, decoder_options.flags);

    const Result result = manager->LoadAlbumScreenShotThumbnail(*out_image_output, out_image,
                                                                file_id, decoder_options);
    R_RETURN(TranslateResult(result));
}

Result IAlbumAccessorService::TranslateResult(Result in_result) {
    if (in_result.IsSuccess()) {
        return in_result;
    }

    if ((in_result.raw & 0x3801ff) == ResultUnknown1024.raw) {
        if (in_result.GetDescription() - 0x514 < 100) {
            return ResultInvalidFileData;
        }
        if (in_result.GetDescription() - 0x5dc < 100) {
            return ResultInvalidFileData;
        }

        if (in_result.GetDescription() - 0x578 < 100) {
            if (in_result == ResultFileCountLimit) {
                return ResultUnknown22;
            }
            return ResultUnknown25;
        }

        if (in_result.raw < ResultUnknown1801.raw) {
            if (in_result == ResultUnknown1202) {
                return ResultUnknown810;
            }
            if (in_result == ResultUnknown1203) {
                return ResultUnknown810;
            }
            if (in_result == ResultUnknown1701) {
                return ResultUnknown5;
            }
        } else if (in_result.raw < ResultUnknown1803.raw) {
            if (in_result == ResultUnknown1801) {
                return ResultUnknown5;
            }
            if (in_result == ResultUnknown1802) {
                return ResultUnknown6;
            }
        } else {
            if (in_result == ResultUnknown1803) {
                return ResultUnknown7;
            }
            if (in_result == ResultUnknown1804) {
                return ResultOutOfRange;
            }
        }
        return ResultUnknown1024;
    }

    if (in_result.GetModule() == ErrorModule::FS) {
        if ((in_result.GetDescription() >> 0xc < 0x7d) ||
            (in_result.GetDescription() - 1000 < 2000) ||
            (((in_result.GetDescription() - 3000) >> 3) < 0x271)) {
            // TODO: Translate FS error
            return in_result;
        }
    }

    return in_result;
}

} // namespace Service::Capture


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

#pragma once

#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Service::Capture {
class AlbumManager;

class IAlbumAccessorService final : public ServiceFramework<IAlbumAccessorService> {
public:
    explicit IAlbumAccessorService(Core::System& system_,
                                   std::shared_ptr<AlbumManager> album_manager);
    ~IAlbumAccessorService() override;

private:
    Result GetAlbumFileList(Out<u64> out_count, AlbumStorage storage,
                            OutArray<AlbumEntry, BufferAttr_HipcMapAlias> out_entries);

    Result DeleteAlbumFile(AlbumFileId file_id);

    Result IsAlbumMounted(Out<bool> out_is_mounted, AlbumStorage storage);

    Result Unknown18(
        Out<u32> out_buffer_size,
        OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure>
            out_buffer);

    Result GetAlbumFileListEx0(Out<u64> out_entries_size, AlbumStorage storage, u8 flags,
                               OutArray<AlbumEntry, BufferAttr_HipcMapAlias> out_entries);

    Result GetAutoSavingStorage(Out<bool> out_is_autosaving);

    Result LoadAlbumScreenShotImageEx1(
        const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options,
        OutLargeData<LoadAlbumScreenShotImageOutput, BufferAttr_HipcMapAlias> out_image_output,
        OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image,
        OutArray<u8, BufferAttr_HipcMapAlias> out_buffer);

    Result LoadAlbumScreenShotThumbnailImageEx1(
        const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options,
        OutLargeData<LoadAlbumScreenShotImageOutput, BufferAttr_HipcMapAlias> out_image_output,
        OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image,
        OutArray<u8, BufferAttr_HipcMapAlias> out_buffer);

    Result TranslateResult(Result in_result);

    std::shared_ptr<AlbumManager> manager = nullptr;
};

} // namespace Service::Capture


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

#include "common/logging/log.h"
#include "core/hle/service/caps/caps_c.h"
#include "core/hle/service/caps/caps_manager.h"
#include "core/hle/service/caps/caps_result.h"
#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ipc_helpers.h"

namespace Service::Capture {

IAlbumControlService::IAlbumControlService(Core::System& system_,
                                           std::shared_ptr<AlbumManager> album_manager)
    : ServiceFramework{system_, "caps:c"}, manager{album_manager} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {1, nullptr, "CaptureRawImage"},
        {2, nullptr, "CaptureRawImageWithTimeout"},
        {33, C<&IAlbumControlService::SetShimLibraryVersion>, "SetShimLibraryVersion"},
        {1001, nullptr, "RequestTakingScreenShot"},
        {1002, nullptr, "RequestTakingScreenShotWithTimeout"},
        {1011, nullptr, "NotifyTakingScreenShotRefused"},
        {2001, nullptr, "NotifyAlbumStorageIsAvailable"},
        {2002, nullptr, "NotifyAlbumStorageIsUnavailable"},
        {2011, nullptr, "RegisterAppletResourceUserId"},
        {2012, nullptr, "UnregisterAppletResourceUserId"},
        {2013, nullptr, "GetApplicationIdFromAruid"},
        {2014, nullptr, "CheckApplicationIdRegistered"},
        {2101, nullptr, "GenerateCurrentAlbumFileId"},
        {2102, nullptr, "GenerateApplicationAlbumEntry"},
        {2201, nullptr, "SaveAlbumScreenShotFile"},
        {2202, nullptr, "SaveAlbumScreenShotFileEx"},
        {2301, nullptr, "SetOverlayScreenShotThumbnailData"},
        {2302, nullptr, "SetOverlayMovieThumbnailData"},
        {60001, nullptr, "OpenControlSession"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

IAlbumControlService::~IAlbumControlService() = default;

Result IAlbumControlService::SetShimLibraryVersion(ShimLibraryVersion library_version,
                                                   ClientAppletResourceUserId aruid) {
    LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}",
                library_version, aruid.pid);
    R_SUCCEED();
}

} // namespace Service::Capture


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

#pragma once

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

namespace Core {
class System;
}

namespace Service::Capture {
class AlbumManager;
enum class ShimLibraryVersion : u64;

class IAlbumControlService final : public ServiceFramework<IAlbumControlService> {
public:
    explicit IAlbumControlService(Core::System& system_,
                                  std::shared_ptr<AlbumManager> album_manager);
    ~IAlbumControlService() override;

private:
    Result SetShimLibraryVersion(ShimLibraryVersion library_version,
                                 ClientAppletResourceUserId aruid);

    std::shared_ptr<AlbumManager> manager = nullptr;
};

} // namespace Service::Capture


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

#include <sstream>

#include "common/fs/file.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#include "common/stb.h"
#include "core/core.h"
#include "core/hle/service/caps/caps_manager.h"
#include "core/hle/service/caps/caps_result.h"
#include "core/hle/service/glue/time/static.h"
#include "core/hle/service/psc/time/system_clock.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"

namespace Service::Capture {

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

AlbumManager::~AlbumManager() = default;

Result AlbumManager::DeleteAlbumFile(const AlbumFileId& file_id) {
    if (file_id.storage > AlbumStorage::Sd) {
        return ResultInvalidStorage;
    }

    if (!is_mounted) {
        return ResultIsNotMounted;
    }

    std::filesystem::path path;
    const auto result = GetFile(path, file_id);

    if (result.IsError()) {
        return result;
    }

    if (!Common::FS::RemoveFile(path)) {
        return ResultFileNotFound;
    }

    return ResultSuccess;
}

Result AlbumManager::IsAlbumMounted(AlbumStorage storage) {
    if (storage > AlbumStorage::Sd) {
        return ResultInvalidStorage;
    }

    is_mounted = true;

    if (storage == AlbumStorage::Sd) {
        FindScreenshots();
    }

    return is_mounted ? ResultSuccess : ResultIsNotMounted;
}

Result AlbumManager::GetAlbumFileList(std::span<AlbumEntry> out_entries, u64& out_entries_count,
                                      AlbumStorage storage, u8 flags) const {
    if (storage > AlbumStorage::Sd) {
        return ResultInvalidStorage;
    }

    if (!is_mounted) {
        return ResultIsNotMounted;
    }

    for (auto& [file_id, path] : album_files) {
        if (file_id.storage != storage) {
            continue;
        }
        if (out_entries_count >= SdAlbumFileLimit) {
            break;
        }
        if (out_entries_count >= out_entries.size()) {
            break;
        }

        const auto entry_size = Common::FS::GetSize(path);
        out_entries[out_entries_count++] = {
            .entry_size = entry_size,
            .file_id = file_id,
        };
    }

    return ResultSuccess;
}

Result AlbumManager::GetAlbumFileList(std::span<ApplicationAlbumFileEntry> out_entries,
                                      u64& out_entries_count, ContentType content_type,
                                      s64 start_posix_time, s64 end_posix_time, u64 aruid) const {
    if (!is_mounted) {
        return ResultIsNotMounted;
    }

    std::vector<ApplicationAlbumEntry> album_entries(out_entries.size());
    const auto start_date = ConvertToAlbumDateTime(start_posix_time);
    const auto end_date = ConvertToAlbumDateTime(end_posix_time);
    const auto result = GetAlbumFileList(album_entries, out_entries_count, content_type, start_date,
                                         end_date, aruid);

    if (result.IsError()) {
        return result;
    }

    for (std::size_t i = 0; i < out_entries_count; i++) {
        out_entries[i] = {
            .entry = album_entries[i],
            .datetime = album_entries[i].datetime,
            .unknown = {},
        };
    }

    return ResultSuccess;
}

Result AlbumManager::GetAlbumFileList(std::span<ApplicationAlbumEntry> out_entries,
                                      u64& out_entries_count, ContentType content_type,
                                      AlbumFileDateTime start_date, AlbumFileDateTime end_date,
                                      u64 aruid) const {
    if (!is_mounted) {
        return ResultIsNotMounted;
    }

    for (auto& [file_id, path] : album_files) {
        if (file_id.type != content_type) {
            continue;
        }
        if (file_id.date > start_date) {
            continue;
        }
        if (file_id.date < end_date) {
            continue;
        }
        if (out_entries_count >= SdAlbumFileLimit) {
            break;
        }
        if (out_entries_count >= out_entries.size()) {
            break;
        }

        const auto entry_size = Common::FS::GetSize(path);
        out_entries[out_entries_count++] = {
            .size = entry_size,
            .hash{},
            .datetime = file_id.date,
            .storage = file_id.storage,
            .content = content_type,
            .unknown = 1,
        };
    }

    return ResultSuccess;
}

Result AlbumManager::GetAutoSavingStorage(bool& out_is_autosaving) const {
    out_is_autosaving = false;
    return ResultSuccess;
}

Result AlbumManager::LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output,
                                              std::span<u8> out_image, const AlbumFileId& file_id,
                                              const ScreenShotDecodeOption& decoder_options) const {
    if (file_id.storage > AlbumStorage::Sd) {
        return ResultInvalidStorage;
    }

    if (!is_mounted) {
        return ResultIsNotMounted;
    }

    out_image_output = {
        .width = 1280,
        .height = 720,
        .attribute =
            {
                .unknown_0{},
                .orientation = AlbumImageOrientation::None,
                .unknown_1{},
                .unknown_2{},
                .pad163{},
            },
        .pad179{},
    };

    std::filesystem::path path;
    const auto result = GetFile(path, file_id);

    if (result.IsError()) {
        return result;
    }

    return LoadImage(out_image, path, static_cast<int>(out_image_output.width),
                     +static_cast<int>(out_image_output.height), decoder_options.flags);
}

Result AlbumManager::LoadAlbumScreenShotThumbnail(
    LoadAlbumScreenShotImageOutput& out_image_output, std::span<u8> out_image,
    const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options) const {
    if (file_id.storage > AlbumStorage::Sd) {
        return ResultInvalidStorage;
    }

    if (!is_mounted) {
        return ResultIsNotMounted;
    }

    out_image_output = {
        .width = 320,
        .height = 180,
        .attribute =
            {
                .unknown_0{},
                .orientation = AlbumImageOrientation::None,
                .unknown_1{},
                .unknown_2{},
                .pad163{},
            },
        .pad179{},
    };

    std::filesystem::path path;
    const auto result = GetFile(path, file_id);

    if (result.IsError()) {
        return result;
    }

    return LoadImage(out_image, path, static_cast<int>(out_image_output.width),
                     +static_cast<int>(out_image_output.height), decoder_options.flags);
}

Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry,
                                    const ScreenShotAttribute& attribute,
                                    AlbumReportOption report_option, std::span<const u8> image_data,
                                    u64 aruid) {
    return SaveScreenShot(out_entry, attribute, report_option, {}, image_data, aruid);
}

Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry,
                                    const ScreenShotAttribute& attribute,
                                    AlbumReportOption report_option,
                                    const ApplicationData& app_data, std::span<const u8> image_data,
                                    u64 aruid) {
    const u64 title_id = system.GetApplicationProcessProgramID();

    auto static_service =
        system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);

    std::shared_ptr<Service::PSC::Time::SystemClock> user_clock{};
    static_service->GetStandardUserSystemClock(&user_clock);

    s64 posix_time{};
    auto result = user_clock->GetCurrentTime(&posix_time);

    if (result.IsError()) {
        return result;
    }

    const auto date = ConvertToAlbumDateTime(posix_time);

    return SaveImage(out_entry, image_data, title_id, date);
}

Result AlbumManager::SaveEditedScreenShot(ApplicationAlbumEntry& out_entry,
                                          const ScreenShotAttribute& attribute,
                                          const AlbumFileId& file_id,
                                          std::span<const u8> image_data) {
    auto static_service =
        system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);

    std::shared_ptr<Service::PSC::Time::SystemClock> user_clock{};
    static_service->GetStandardUserSystemClock(&user_clock);

    s64 posix_time{};
    auto result = user_clock->GetCurrentTime(&posix_time);

    if (result.IsError()) {
        return result;
    }

    const auto date = ConvertToAlbumDateTime(posix_time);

    return SaveImage(out_entry, image_data, file_id.application_id, date);
}

Result AlbumManager::GetFile(std::filesystem::path& out_path, const AlbumFileId& file_id) const {
    const auto file = album_files.find(file_id);

    if (file == album_files.end()) {
        return ResultFileNotFound;
    }

    out_path = file->second;
    return ResultSuccess;
}

void AlbumManager::FindScreenshots() {
    is_mounted = false;
    album_files.clear();

    // TODO: Swap this with a blocking operation.
    const auto screenshots_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ScreenshotsDir);
    Common::FS::IterateDirEntries(
        screenshots_dir,
        [this](const std::filesystem::path& full_path) {
            AlbumEntry entry;
            if (GetAlbumEntry(entry, full_path).IsError()) {
                return true;
            }
            while (album_files.contains(entry.file_id)) {
                if (++entry.file_id.date.unique_id == 0) {
                    break;
                }
            }
            album_files[entry.file_id] = full_path;
            return true;
        },
        Common::FS::DirEntryFilter::File);

    is_mounted = true;
}

Result AlbumManager::GetAlbumEntry(AlbumEntry& out_entry, const std::filesystem::path& path) const {
    std::istringstream line_stream(path.filename().string());
    std::string date;
    std::string application;
    std::string time;

    // Parse filename to obtain entry properties
    std::getline(line_stream, application, '_');
    std::getline(line_stream, date, '_');
    std::getline(line_stream, time, '_');

    std::istringstream date_stream(date);
    std::istringstream time_stream(time);
    std::string year;
    std::string month;
    std::string day;
    std::string hour;
    std::string minute;
    std::string second;

    std::getline(date_stream, year, '-');
    std::getline(date_stream, month, '-');
    std::getline(date_stream, day, '-');

    std::getline(time_stream, hour, '-');
    std::getline(time_stream, minute, '-');
    std::getline(time_stream, second, '-');

    try {
        out_entry = {
            .entry_size = 1,
            .file_id{
                .application_id = static_cast<u64>(std::stoll(application, 0, 16)),
                .date =
                    {
                        .year = static_cast<s16>(std::stoi(year)),
                        .month = static_cast<s8>(std::stoi(month)),
                        .day = static_cast<s8>(std::stoi(day)),
                        .hour = static_cast<s8>(std::stoi(hour)),
                        .minute = static_cast<s8>(std::stoi(minute)),
                        .second = static_cast<s8>(std::stoi(second)),
                        .unique_id = 0,
                    },
                .storage = AlbumStorage::Sd,
                .type = ContentType::Screenshot,
                .unknown = 1,
            },
        };
    } catch (const std::invalid_argument&) {
        return ResultUnknown;
    } catch (const std::out_of_range&) {
        return ResultUnknown;
    } catch (const std::exception&) {
        return ResultUnknown;
    }

    return ResultSuccess;
}

Result AlbumManager::LoadImage(std::span<u8> out_image, const std::filesystem::path& path,
                               int width, int height, ScreenShotDecoderFlag flag) const {
    if (out_image.size() != static_cast<std::size_t>(width * height * STBI_rgb_alpha)) {
        return ResultUnknown;
    }

    const Common::FS::IOFile db_file{path, Common::FS::FileAccessMode::Read,
                                     Common::FS::FileType::BinaryFile};

    std::vector<u8> raw_file(db_file.GetSize());
    if (db_file.Read(raw_file) != raw_file.size()) {
        return ResultUnknown;
    }

    int filter_flag = STBIR_FILTER_DEFAULT;
    int original_width, original_height, color_channels;
    const auto dbi_image =
        stbi_load_from_memory(raw_file.data(), static_cast<int>(raw_file.size()), &original_width,
                              &original_height, &color_channels, STBI_rgb_alpha);

    if (dbi_image == nullptr) {
        return ResultUnknown;
    }

    switch (flag) {
    case ScreenShotDecoderFlag::EnableFancyUpsampling:
        filter_flag = STBIR_FILTER_TRIANGLE;
        break;
    case ScreenShotDecoderFlag::EnableBlockSmoothing:
        filter_flag = STBIR_FILTER_BOX;
        break;
    default:
        filter_flag = STBIR_FILTER_DEFAULT;
        break;
    }

    stbir_resize_uint8_srgb(dbi_image, original_width, original_height, 0, out_image.data(), width,
                            height, 0, STBI_rgb_alpha, 3, filter_flag);

    return ResultSuccess;
}

void AlbumManager::FlipVerticallyOnWrite(bool flip) {
    stbi_flip_vertically_on_write(flip);
}

static void PNGToMemory(void* context, void* data, int len) {
    std::vector<u8>* png_image = static_cast<std::vector<u8>*>(context);
    unsigned char* png = static_cast<unsigned char*>(data);
    png_image->insert(png_image->end(), png, png + len);
}

Result AlbumManager::SaveImage(ApplicationAlbumEntry& out_entry, std::span<const u8> image,
                               u64 title_id, const AlbumFileDateTime& date) const {
    const auto screenshot_path =
        Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir);
    const std::string formatted_date =
        fmt::format("{:04}-{:02}-{:02}_{:02}-{:02}-{:02}-{:03}", date.year, date.month, date.day,
                    date.hour, date.minute, date.second, 0);
    const std::string file_path =
        fmt::format("{}/{:016x}_{}.png", screenshot_path, title_id, formatted_date);

    const Common::FS::IOFile db_file{file_path, Common::FS::FileAccessMode::Write,
                                     Common::FS::FileType::BinaryFile};

    std::vector<u8> png_image;
    if (!stbi_write_png_to_func(PNGToMemory, &png_image, 1280, 720, STBI_rgb_alpha, image.data(),
                                0)) {
        return ResultFileCountLimit;
    }

    if (db_file.Write(png_image) != png_image.size()) {
        return ResultFileCountLimit;
    }

    out_entry = {
        .size = png_image.size(),
        .hash = {},
        .datetime = date,
        .storage = AlbumStorage::Sd,
        .content = ContentType::Screenshot,
        .unknown = 1,
    };

    return ResultSuccess;
}

AlbumFileDateTime AlbumManager::ConvertToAlbumDateTime(u64 posix_time) const {
    auto static_service =
        system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);

    std::shared_ptr<Service::Glue::Time::TimeZoneService> timezone_service{};
    static_service->GetTimeZoneService(&timezone_service);

    Service::PSC::Time::CalendarTime calendar_time{};
    Service::PSC::Time::CalendarAdditionalInfo additional_info{};
    timezone_service->ToCalendarTimeWithMyRule(&calendar_time, &additional_info, posix_time);

    return {
        .year = calendar_time.year,
        .month = calendar_time.month,
        .day = calendar_time.day,
        .hour = calendar_time.hour,
        .minute = calendar_time.minute,
        .second = calendar_time.second,
        .unique_id = 0,
    };
}

} // namespace Service::Capture


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

#pragma once

#include <unordered_map>

#include "common/fs/fs.h"
#include "core/hle/result.h"
#include "core/hle/service/caps/caps_types.h"

namespace Core {
class System;
}

namespace std {
// Hash used to create lists from AlbumFileId data
template <>
struct hash<Service::Capture::AlbumFileId> {
    size_t operator()(const Service::Capture::AlbumFileId& pad_id) const noexcept {
        u64 hash_value = (static_cast<u64>(pad_id.date.year) << 8);
        hash_value ^= (static_cast<u64>(pad_id.date.month) << 7);
        hash_value ^= (static_cast<u64>(pad_id.date.day) << 6);
        hash_value ^= (static_cast<u64>(pad_id.date.hour) << 5);
        hash_value ^= (static_cast<u64>(pad_id.date.minute) << 4);
        hash_value ^= (static_cast<u64>(pad_id.date.second) << 3);
        hash_value ^= (static_cast<u64>(pad_id.date.unique_id) << 2);
        hash_value ^= (static_cast<u64>(pad_id.storage) << 1);
        hash_value ^= static_cast<u64>(pad_id.type);
        return static_cast<size_t>(hash_value);
    }
};

} // namespace std

namespace Service::Capture {

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

    Result DeleteAlbumFile(const AlbumFileId& file_id);
    Result IsAlbumMounted(AlbumStorage storage);
    Result GetAlbumFileList(std::span<AlbumEntry> out_entries, u64& out_entries_count,
                            AlbumStorage storage, u8 flags) const;
    Result GetAlbumFileList(std::span<ApplicationAlbumFileEntry> out_entries,
                            u64& out_entries_count, ContentType content_type, s64 start_posix_time,
                            s64 end_posix_time, u64 aruid) const;
    Result GetAlbumFileList(std::span<ApplicationAlbumEntry> out_entries, u64& out_entries_count,
                            ContentType content_type, AlbumFileDateTime start_date,
                            AlbumFileDateTime end_date, u64 aruid) const;
    Result GetAutoSavingStorage(bool& out_is_autosaving) const;
    Result LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output,
                                    std::span<u8> out_image, const AlbumFileId& file_id,
                                    const ScreenShotDecodeOption& decoder_options) const;
    Result LoadAlbumScreenShotThumbnail(LoadAlbumScreenShotImageOutput& out_image_output,
                                        std::span<u8> out_image, const AlbumFileId& file_id,
                                        const ScreenShotDecodeOption& decoder_options) const;

    Result SaveScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute,
                          AlbumReportOption report_option, std::span<const u8> image_data,
                          u64 aruid);
    Result SaveScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute,
                          AlbumReportOption report_option, const ApplicationData& app_data,
                          std::span<const u8> image_data, u64 aruid);
    Result SaveEditedScreenShot(ApplicationAlbumEntry& out_entry,
                                const ScreenShotAttribute& attribute, const AlbumFileId& file_id,
                                std::span<const u8> image_data);

    void FlipVerticallyOnWrite(bool flip);

private:
    static constexpr std::size_t NandAlbumFileLimit = 1000;
    static constexpr std::size_t SdAlbumFileLimit = 10000;

    void FindScreenshots();
    Result GetFile(std::filesystem::path& out_path, const AlbumFileId& file_id) const;
    Result GetAlbumEntry(AlbumEntry& out_entry, const std::filesystem::path& path) const;
    Result LoadImage(std::span<u8> out_image, const std::filesystem::path& path, int width,
                     int height, ScreenShotDecoderFlag flag) const;
    Result SaveImage(ApplicationAlbumEntry& out_entry, std::span<const u8> image, u64 title_id,
                     const AlbumFileDateTime& date) const;

    AlbumFileDateTime ConvertToAlbumDateTime(u64 posix_time) const;

    bool is_mounted{};
    std::unordered_map<AlbumFileId, std::filesystem::path> album_files;

    Core::System& system;
};

} // namespace Service::Capture


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

#pragma once

#include "core/hle/result.h"

namespace Service::Capture {

constexpr Result ResultWorkMemoryError(ErrorModule::Capture, 3);
constexpr Result ResultUnknown5(ErrorModule::Capture, 5);
constexpr Result ResultUnknown6(ErrorModule::Capture, 6);
constexpr Result ResultUnknown7(ErrorModule::Capture, 7);
constexpr Result ResultOutOfRange(ErrorModule::Capture, 8);
constexpr Result ResultInvalidTimestamp(ErrorModule::Capture, 12);
constexpr Result ResultInvalidStorage(ErrorModule::Capture, 13);
constexpr Result ResultInvalidFileContents(ErrorModule::Capture, 14);
constexpr Result ResultIsNotMounted(ErrorModule::Capture, 21);
constexpr Result ResultUnknown22(ErrorModule::Capture, 22);
constexpr Result ResultFileNotFound(ErrorModule::Capture, 23);
constexpr Result ResultInvalidFileData(ErrorModule::Capture, 24);
constexpr Result ResultUnknown25(ErrorModule::Capture, 25);
constexpr Result ResultReadBufferShortage(ErrorModule::Capture, 30);
constexpr Result ResultUnknown810(ErrorModule::Capture, 810);
constexpr Result ResultUnknown1024(ErrorModule::Capture, 1024);
constexpr Result ResultUnknown1202(ErrorModule::Capture, 1202);
constexpr Result ResultUnknown1203(ErrorModule::Capture, 1203);
constexpr Result ResultFileCountLimit(ErrorModule::Capture, 1401);
constexpr Result ResultUnknown1701(ErrorModule::Capture, 1701);
constexpr Result ResultUnknown1801(ErrorModule::Capture, 1801);
constexpr Result ResultUnknown1802(ErrorModule::Capture, 1802);
constexpr Result ResultUnknown1803(ErrorModule::Capture, 1803);
constexpr Result ResultUnknown1804(ErrorModule::Capture, 1804);

} // namespace Service::Capture


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

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

namespace Service::Capture {

IScreenShotControlService::IScreenShotControlService(Core::System& system_)
    : ServiceFramework{system_, "caps:sc"} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {1, nullptr, "CaptureRawImage"},
        {2, nullptr, "CaptureRawImageWithTimeout"},
        {3, nullptr, "AttachSharedBuffer"},
        {5, nullptr, "CaptureRawImageToAttachedSharedBuffer"},
        {210, nullptr, "Unknown210"},
        {1001, nullptr, "RequestTakingScreenShot"},
        {1002, nullptr, "RequestTakingScreenShotWithTimeout"},
        {1003, nullptr, "RequestTakingScreenShotEx"},
        {1004, nullptr, "RequestTakingScreenShotEx1"},
        {1009, nullptr, "CancelTakingScreenShot"},
        {1010, nullptr, "SetTakingScreenShotCancelState"},
        {1011, nullptr, "NotifyTakingScreenShotRefused"},
        {1012, nullptr, "NotifyTakingScreenShotFailed"},
        {1101, nullptr, "SetupOverlayMovieThumbnail"},
        {1106, nullptr, "Unknown1106"},
        {1107, nullptr, "Unknown1107"},
        {1201, nullptr, "OpenRawScreenShotReadStream"},
        {1202, nullptr, "CloseRawScreenShotReadStream"},
        {1203, nullptr, "ReadRawScreenShotReadStream"},
        {1204, nullptr, "Unknown1204"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

IScreenShotControlService::~IScreenShotControlService() = default;

} // namespace Service::Capture


code:
// SPDX-FileCopyrightText: Copyright 2020 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::Capture {

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

} // namespace Service::Capture


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

#include "common/logging/log.h"
#include "core/hle/service/caps/caps_manager.h"
#include "core/hle/service/caps/caps_ss.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ipc_helpers.h"

namespace Service::Capture {

IScreenShotService::IScreenShotService(Core::System& system_,
                                       std::shared_ptr<AlbumManager> album_manager)
    : ServiceFramework{system_, "caps:ss"}, manager{album_manager} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {201, nullptr, "SaveScreenShot"},
        {202, nullptr, "SaveEditedScreenShot"},
        {203, C<&IScreenShotService::SaveScreenShotEx0>, "SaveScreenShotEx0"},
        {204, nullptr, "SaveEditedScreenShotEx0"},
        {206, C<&IScreenShotService::SaveEditedScreenShotEx1>, "SaveEditedScreenShotEx1"},
        {208, nullptr, "SaveScreenShotOfMovieEx1"},
        {1000, nullptr, "Unknown1000"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

IScreenShotService::~IScreenShotService() = default;

Result IScreenShotService::SaveScreenShotEx0(
    Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute,
    AlbumReportOption report_option, ClientAppletResourceUserId aruid,
    InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
        image_data_buffer) {
    LOG_INFO(Service_Capture,
             "called, report_option={}, image_data_buffer_size={}, applet_resource_user_id={}",
             report_option, image_data_buffer.size(), aruid.pid);

    manager->FlipVerticallyOnWrite(false);
    R_RETURN(manager->SaveScreenShot(*out_entry, attribute, report_option, image_data_buffer,
                                     aruid.pid));
}

Result IScreenShotService::SaveEditedScreenShotEx1(
    Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute, u64 width,
    u64 height, u64 thumbnail_width, u64 thumbnail_height, const AlbumFileId& file_id,
    const InLargeData<std::array<u8, 0x400>, BufferAttr_HipcMapAlias> application_data_buffer,
    const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
        image_data_buffer,
    const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
        thumbnail_image_data_buffer) {
    LOG_INFO(Service_Capture,
             "called, width={}, height={}, thumbnail_width={}, thumbnail_height={}, "
             "application_id={:016x},  storage={},  type={}, "
             "image_data_buffer_size={}, thumbnail_image_buffer_size={}",
             width, height, thumbnail_width, thumbnail_height, file_id.application_id,
             file_id.storage, file_id.type, image_data_buffer.size(),
             thumbnail_image_data_buffer.size());

    manager->FlipVerticallyOnWrite(false);
    R_RETURN(manager->SaveEditedScreenShot(*out_entry, attribute, file_id, image_data_buffer));
}

} // namespace Service::Capture


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

#pragma once

#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Service::Capture {

class IScreenShotService final : public ServiceFramework<IScreenShotService> {
public:
    explicit IScreenShotService(Core::System& system_, std::shared_ptr<AlbumManager> album_manager);
    ~IScreenShotService() override;

private:
    Result SaveScreenShotEx0(
        Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute,
        AlbumReportOption report_option, ClientAppletResourceUserId aruid,
        InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
            image_data_buffer);

    Result SaveEditedScreenShotEx1(
        Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute, u64 width,
        u64 height, u64 thumbnail_width, u64 thumbnail_height, const AlbumFileId& file_id,
        const InLargeData<std::array<u8, 0x400>, BufferAttr_HipcMapAlias> application_data_buffer,
        const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
            image_data_buffer,
        const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
            thumbnail_image_data_buffer);

    std::shared_ptr<AlbumManager> manager;
};

} // namespace Service::Capture


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

#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/service/caps/caps_manager.h"
#include "core/hle/service/caps/caps_su.h"
#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ipc_helpers.h"
#include "video_core/renderer_base.h"

namespace Service::Capture {

IScreenShotApplicationService::IScreenShotApplicationService(
    Core::System& system_, std::shared_ptr<AlbumManager> album_manager)
    : ServiceFramework{system_, "caps:su"}, manager{album_manager} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {32, C<&IScreenShotApplicationService::SetShimLibraryVersion>, "SetShimLibraryVersion"},
        {201, nullptr, "SaveScreenShot"},
        {203, C<&IScreenShotApplicationService::SaveScreenShotEx0>, "SaveScreenShotEx0"},
        {205, C<&IScreenShotApplicationService::SaveScreenShotEx1>, "SaveScreenShotEx1"},
        {210, nullptr, "SaveScreenShotEx2"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

IScreenShotApplicationService::~IScreenShotApplicationService() = default;

Result IScreenShotApplicationService::SetShimLibraryVersion(ShimLibraryVersion library_version,
                                                            ClientAppletResourceUserId aruid) {
    LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}",
                library_version, aruid.pid);
    R_SUCCEED();
}

Result IScreenShotApplicationService::SaveScreenShotEx0(
    Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute,
    AlbumReportOption report_option, ClientAppletResourceUserId aruid,
    InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
        image_data_buffer) {
    LOG_INFO(Service_Capture,
             "called, report_option={}, image_data_buffer_size={}, applet_resource_user_id={}",
             report_option, image_data_buffer.size(), aruid.pid);

    manager->FlipVerticallyOnWrite(false);
    R_RETURN(manager->SaveScreenShot(*out_entry, attribute, report_option, image_data_buffer,
                                     aruid.pid));
}

Result IScreenShotApplicationService::SaveScreenShotEx1(
    Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute,
    AlbumReportOption report_option, ClientAppletResourceUserId aruid,
    const InLargeData<ApplicationData, BufferAttr_HipcMapAlias> app_data_buffer,
    const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
        image_data_buffer) {
    LOG_INFO(Service_Capture,
             "called, report_option={}, image_data_buffer_size={}, applet_resource_user_id={}",
             report_option, image_data_buffer.size(), aruid.pid);

    manager->FlipVerticallyOnWrite(false);
    R_RETURN(manager->SaveScreenShot(*out_entry, attribute, report_option, *app_data_buffer,
                                     image_data_buffer, aruid.pid));
}

void IScreenShotApplicationService::CaptureAndSaveScreenshot(AlbumReportOption report_option) {
    auto& renderer = system.Renderer();
    Layout::FramebufferLayout layout =
        Layout::DefaultFrameLayout(screenshot_width, screenshot_height);

    const Capture::ScreenShotAttribute attribute{
        .unknown_0{},
        .orientation = Capture::AlbumImageOrientation::None,
        .unknown_1{},
        .unknown_2{},
        .pad163{},
    };

    renderer.RequestScreenshot(
        image_data.data(),
        [attribute, report_option, this](bool invert_y) {
            // Convert from BGRA to RGBA
            for (std::size_t i = 0; i < image_data.size(); i += bytes_per_pixel) {
                const u8 temp = image_data[i];
                image_data[i] = image_data[i + 2];
                image_data[i + 2] = temp;
            }

            Capture::ApplicationAlbumEntry entry{};
            manager->FlipVerticallyOnWrite(invert_y);
            manager->SaveScreenShot(entry, attribute, report_option, image_data, {});
        },
        layout);
}

} // namespace Service::Capture


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

#pragma once

#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Service::Capture {
enum class AlbumReportOption : s32;
class AlbumManager;

class IScreenShotApplicationService final : public ServiceFramework<IScreenShotApplicationService> {
public:
    explicit IScreenShotApplicationService(Core::System& system_,
                                           std::shared_ptr<AlbumManager> album_manager);
    ~IScreenShotApplicationService() override;

    void CaptureAndSaveScreenshot(AlbumReportOption report_option);

private:
    static constexpr std::size_t screenshot_width = 1280;
    static constexpr std::size_t screenshot_height = 720;
    static constexpr std::size_t bytes_per_pixel = 4;

    Result SetShimLibraryVersion(ShimLibraryVersion library_version,
                                 ClientAppletResourceUserId aruid);
    Result SaveScreenShotEx0(
        Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute,
        AlbumReportOption report_option, ClientAppletResourceUserId aruid,
        InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
            image_data_buffer);
    Result SaveScreenShotEx1(
        Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute,
        AlbumReportOption report_option, ClientAppletResourceUserId aruid,
        const InLargeData<ApplicationData, BufferAttr_HipcMapAlias> app_data_buffer,
        const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias>
            image_data_buffer);

    std::array<u8, screenshot_width * screenshot_height * bytes_per_pixel> image_data;

    std::shared_ptr<AlbumManager> manager;
};

} // namespace Service::Capture


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

#pragma once

#include "common/common_funcs.h"
#include "common/common_types.h"

namespace Service::Capture {

// This is nn::album::ImageOrientation
enum class AlbumImageOrientation {
    None,
    Rotate90,
    Rotate180,
    Rotate270,
};

// This is nn::album::AlbumReportOption
enum class AlbumReportOption : s32 {
    Disable,
    Enable,
    Unknown2,
    Unknown3,
};

enum class ContentType : u8 {
    Screenshot = 0,
    Movie = 1,
    ExtraMovie = 3,
};

enum class AlbumStorage : u8 {
    Nand,
    Sd,
};

enum class ScreenShotDecoderFlag : u64 {
    None = 0,
    EnableFancyUpsampling = 1 << 0,
    EnableBlockSmoothing = 1 << 1,
};

enum class ShimLibraryVersion : u64 {
    Version1 = 1,
};

// This is nn::capsrv::AlbumFileDateTime
struct AlbumFileDateTime {
    s16 year{};
    s8 month{};
    s8 day{};
    s8 hour{};
    s8 minute{};
    s8 second{};
    s8 unique_id{};

    friend constexpr bool operator==(const AlbumFileDateTime&, const AlbumFileDateTime&) = default;
    friend constexpr bool operator>(const AlbumFileDateTime& a, const AlbumFileDateTime& b) {
        if (a.year > b.year) {
            return true;
        }
        if (a.month > b.month) {
            return true;
        }
        if (a.day > b.day) {
            return true;
        }
        if (a.hour > b.hour) {
            return true;
        }
        if (a.minute > b.minute) {
            return true;
        }
        return a.second > b.second;
    };
    friend constexpr bool operator<(const AlbumFileDateTime& a, const AlbumFileDateTime& b) {
        if (a.year < b.year) {
            return true;
        }
        if (a.month < b.month) {
            return true;
        }
        if (a.day < b.day) {
            return true;
        }
        if (a.hour < b.hour) {
            return true;
        }
        if (a.minute < b.minute) {
            return true;
        }
        return a.second < b.second;
    };
};
static_assert(sizeof(AlbumFileDateTime) == 0x8, "AlbumFileDateTime has incorrect size.");

// This is nn::album::AlbumEntry
struct AlbumFileEntry {
    u64 size{}; // Size of the entry
    u64 hash{}; // AES256 with hardcoded key over AlbumEntry
    AlbumFileDateTime datetime{};
    AlbumStorage storage{};
    ContentType content{};
    INSERT_PADDING_BYTES(5);
    u8 unknown{}; // Set to 1 on official SW
};
static_assert(sizeof(AlbumFileEntry) == 0x20, "AlbumFileEntry has incorrect size.");

struct AlbumFileId {
    u64 application_id{};
    AlbumFileDateTime date{};
    AlbumStorage storage{};
    ContentType type{};
    INSERT_PADDING_BYTES(0x5);
    u8 unknown{};

    friend constexpr bool operator==(const AlbumFileId&, const AlbumFileId&) = default;
};
static_assert(sizeof(AlbumFileId) == 0x18, "AlbumFileId is an invalid size");

// This is nn::capsrv::AlbumEntry
struct AlbumEntry {
    u64 entry_size{};
    AlbumFileId file_id{};
};
static_assert(sizeof(AlbumEntry) == 0x20, "AlbumEntry has incorrect size.");

// This is nn::capsrv::ApplicationAlbumEntry
struct ApplicationAlbumEntry {
    u64 size{}; // Size of the entry
    u64 hash{}; // AES256 with hardcoded key over AlbumEntry
    AlbumFileDateTime datetime{};
    AlbumStorage storage{};
    ContentType content{};
    INSERT_PADDING_BYTES(5);
    u8 unknown{1}; // Set to 1 on official SW
};
static_assert(sizeof(ApplicationAlbumEntry) == 0x20, "ApplicationAlbumEntry has incorrect size.");

// This is nn::capsrv::ApplicationAlbumFileEntry
struct ApplicationAlbumFileEntry {
    ApplicationAlbumEntry entry{};
    AlbumFileDateTime datetime{};
    u64 unknown{};
};
static_assert(sizeof(ApplicationAlbumFileEntry) == 0x30,
              "ApplicationAlbumFileEntry has incorrect size.");

struct ApplicationData {
    std::array<u8, 0x400> data;
    u32 data_size;
};
static_assert(sizeof(ApplicationData) == 0x404, "ApplicationData is an invalid size");
static_assert(std::is_trivial_v<ApplicationData>,
              "ApplicationData type must be trivially copyable.");

struct ScreenShotAttribute {
    u32 unknown_0;
    AlbumImageOrientation orientation;
    u32 unknown_1;
    u32 unknown_2;
    INSERT_PADDING_BYTES_NOINIT(0x30);
};
static_assert(sizeof(ScreenShotAttribute) == 0x40, "ScreenShotAttribute is an invalid size");
static_assert(std::is_trivial_v<ScreenShotAttribute>,
              "ScreenShotAttribute type must be trivially copyable.");

struct ScreenShotDecodeOption {
    ScreenShotDecoderFlag flags{};
    INSERT_PADDING_BYTES(0x18);
};
static_assert(sizeof(ScreenShotDecodeOption) == 0x20, "ScreenShotDecodeOption is an invalid size");

struct LoadAlbumScreenShotImageOutput {
    s64 width;
    s64 height;
    ScreenShotAttribute attribute;
    INSERT_PADDING_BYTES_NOINIT(0x400);
};
static_assert(sizeof(LoadAlbumScreenShotImageOutput) == 0x450,
              "LoadAlbumScreenShotImageOutput is an invalid size");
static_assert(std::is_trivial_v<LoadAlbumScreenShotImageOutput>,
              "LoadAlbumScreenShotImageOutput type must be trivially copyable.");

struct LoadAlbumScreenShotImageOutputForApplication {
    s64 width{};
    s64 height{};
    ScreenShotAttribute attribute{};
    ApplicationData data{};
    INSERT_PADDING_BYTES(0xAC);
};
static_assert(sizeof(LoadAlbumScreenShotImageOutputForApplication) == 0x500,
              "LoadAlbumScreenShotImageOutput is an invalid size");

} // namespace Service::Capture


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

#include "common/logging/log.h"
#include "core/hle/service/caps/caps_manager.h"
#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/caps/caps_u.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ipc_helpers.h"

namespace Service::Capture {

IAlbumApplicationService::IAlbumApplicationService(Core::System& system_,
                                                   std::shared_ptr<AlbumManager> album_manager)
    : ServiceFramework{system_, "caps:u"}, manager{album_manager} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {32, C<&IAlbumApplicationService::SetShimLibraryVersion>, "SetShimLibraryVersion"},
        {102, C<&IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated>, "GetAlbumFileList0AafeAruidDeprecated"},
        {103, nullptr, "DeleteAlbumFileByAruid"},
        {104, nullptr, "GetAlbumFileSizeByAruid"},
        {105, nullptr, "DeleteAlbumFileByAruidForDebug"},
        {110, nullptr, "LoadAlbumScreenShotImageByAruid"},
        {120, nullptr, "LoadAlbumScreenShotThumbnailImageByAruid"},
        {130, nullptr, "PrecheckToCreateContentsByAruid"},
        {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"},
        {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"},
        {142, C<&IAlbumApplicationService::GetAlbumFileList3AaeAruid>, "GetAlbumFileList3AaeAruid"},
        {143, nullptr, "GetAlbumFileList4AaeUidAruid"},
        {144, nullptr, "GetAllAlbumFileList3AaeAruid"},
        {60002, nullptr, "OpenAccessorSessionForApplication"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

IAlbumApplicationService::~IAlbumApplicationService() = default;

Result IAlbumApplicationService::SetShimLibraryVersion(ShimLibraryVersion library_version,
                                                       ClientAppletResourceUserId aruid) {
    LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}",
                library_version, aruid.pid);
    R_SUCCEED();
}

Result IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated(
    Out<u64> out_entries_count, ContentType content_type, s64 start_posix_time, s64 end_posix_time,
    ClientAppletResourceUserId aruid,
    OutArray<ApplicationAlbumFileEntry, BufferAttr_HipcMapAlias> out_entries) {
    LOG_WARNING(Service_Capture,
                "(STUBBED) called. content_type={}, start_posix_time={}, end_posix_time={}, "
                "applet_resource_user_id={}",
                content_type, start_posix_time, end_posix_time, aruid.pid);

    R_TRY(manager->IsAlbumMounted(AlbumStorage::Sd));
    R_RETURN(manager->GetAlbumFileList(out_entries, *out_entries_count, content_type,
                                       start_posix_time, end_posix_time, aruid.pid));
}

Result IAlbumApplicationService::GetAlbumFileList3AaeAruid(
    Out<u64> out_entries_count, ContentType content_type, AlbumFileDateTime start_date_time,
    AlbumFileDateTime end_date_time, ClientAppletResourceUserId aruid,
    OutArray<ApplicationAlbumEntry, BufferAttr_HipcMapAlias> out_entries) {
    LOG_WARNING(Service_Capture,
                "(STUBBED) called. content_type={}, start_date={}/{}/{}, "
                "end_date={}/{}/{}, applet_resource_user_id={}",
                content_type, start_date_time.year, start_date_time.month, start_date_time.day,
                end_date_time.year, end_date_time.month, end_date_time.day, aruid.pid);

    R_TRY(manager->IsAlbumMounted(AlbumStorage::Sd));
    R_RETURN(manager->GetAlbumFileList(out_entries, *out_entries_count, content_type,
                                       start_date_time, end_date_time, aruid.pid));
}

} // namespace Service::Capture


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

#pragma once

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

namespace Core {
class System;
}

namespace Service::Capture {
class AlbumManager;

class IAlbumApplicationService final : public ServiceFramework<IAlbumApplicationService> {
public:
    explicit IAlbumApplicationService(Core::System& system_,
                                      std::shared_ptr<AlbumManager> album_manager);
    ~IAlbumApplicationService() override;

private:
    Result SetShimLibraryVersion(ShimLibraryVersion library_version,
                                 ClientAppletResourceUserId aruid);

    Result GetAlbumFileList0AafeAruidDeprecated(
        Out<u64> out_entries_count, ContentType content_type, s64 start_posix_time,
        s64 end_posix_time, ClientAppletResourceUserId aruid,
        OutArray<ApplicationAlbumFileEntry, BufferAttr_HipcMapAlias> out_entries);

    Result GetAlbumFileList3AaeAruid(
        Out<u64> out_entries_count, ContentType content_type, AlbumFileDateTime start_date_time,
        AlbumFileDateTime end_date_time, ClientAppletResourceUserId aruid,
        OutArray<ApplicationAlbumEntry, BufferAttr_HipcMapAlias> out_entries);

    std::shared_ptr<AlbumManager> manager = nullptr;
};

} // namespace Service::Capture


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

#pragma once

#include "common/div_ceil.h"

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

namespace Service {

// clang-format off
template <typename T>
struct UnwrapArg {
    using Type = std::remove_cvref_t<T>;
};

template <typename T, int A>
struct UnwrapArg<InLargeData<T, A>> {
    using Type = std::remove_cv_t<typename InLargeData<T, A>::Type>;
};

template <typename T>
struct UnwrapArg<Out<T>> {
    using Type = AutoOut<typename Out<T>::Type>;
};

template <typename T>
struct UnwrapArg<OutCopyHandle<T>> {
    using Type = AutoOut<typename OutCopyHandle<T>::Type>;
};

template <typename T>
struct UnwrapArg<OutMoveHandle<T>> {
    using Type = AutoOut<typename OutMoveHandle<T>::Type>;
};

template <typename T, int A>
struct UnwrapArg<OutLargeData<T, A>> {
    using Type = AutoOut<typename OutLargeData<T, A>::Type>;
};

enum class ArgumentType {
    InProcessId,
    InData,
    InInterface,
    InCopyHandle,
    OutData,
    OutInterface,
    OutCopyHandle,
    OutMoveHandle,
    InBuffer,
    InLargeData,
    OutBuffer,
    OutLargeData,
};

template <typename T>
struct ArgumentTraits;

template <>
struct ArgumentTraits<ClientProcessId> {
    static constexpr ArgumentType Type = ArgumentType::InProcessId;
};

template <typename T>
struct ArgumentTraits<SharedPointer<T>> {
    static constexpr ArgumentType Type = ArgumentType::InInterface;
};

template <typename T>
struct ArgumentTraits<InCopyHandle<T>> {
    static constexpr ArgumentType Type = ArgumentType::InCopyHandle;
};

template <typename T>
struct ArgumentTraits<Out<SharedPointer<T>>> {
    static constexpr ArgumentType Type = ArgumentType::OutInterface;
};

template <typename T>
struct ArgumentTraits<Out<T>> {
    static constexpr ArgumentType Type = ArgumentType::OutData;
};

template <typename T>
struct ArgumentTraits<OutCopyHandle<T>> {
    static constexpr ArgumentType Type = ArgumentType::OutCopyHandle;
};

template <typename T>
struct ArgumentTraits<OutMoveHandle<T>> {
    static constexpr ArgumentType Type = ArgumentType::OutMoveHandle;
};

template <typename T, int A>
struct ArgumentTraits<Buffer<T, A>> {
    static constexpr ArgumentType Type = (A & BufferAttr_In) == 0 ? ArgumentType::OutBuffer : ArgumentType::InBuffer;
};

template <typename T, int A>
struct ArgumentTraits<InLargeData<T, A>> {
    static constexpr ArgumentType Type = ArgumentType::InLargeData;
};

template <typename T, int A>
struct ArgumentTraits<OutLargeData<T, A>> {
    static constexpr ArgumentType Type = ArgumentType::OutLargeData;
};

template <typename T>
struct ArgumentTraits {
    static constexpr ArgumentType Type = ArgumentType::InData;
};

template <typename... Ts>
consteval bool ConstIfReference() {
    return ((!std::is_reference_v<Ts> || std::is_const_v<std::remove_reference_t<Ts>>) && ... && true);
}

struct RequestLayout {
    u32 copy_handle_count;
    u32 move_handle_count;
    u32 cmif_raw_data_size;
    u32 domain_interface_count;
};

template <typename MethodArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t ArgIndex = 0>
constexpr u32 GetInRawDataSize() {
    if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) {
        return static_cast<u32>(DataOffset);
    } else {
        using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;

        if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InData || ArgumentTraits<ArgType>::Type == ArgumentType::InProcessId) {
            constexpr size_t ArgAlign = alignof(ArgType);
            constexpr size_t ArgSize = sizeof(ArgType);

            static_assert(PrevAlign <= ArgAlign, "Input argument is not ordered by alignment");

            constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
            constexpr size_t ArgEnd = ArgOffset + ArgSize;

            return GetInRawDataSize<MethodArguments, ArgAlign, ArgEnd, ArgIndex + 1>();
        } else {
            return GetInRawDataSize<MethodArguments, PrevAlign, DataOffset, ArgIndex + 1>();
        }
    }
}

template <typename MethodArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t ArgIndex = 0>
constexpr u32 GetOutRawDataSize() {
    if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) {
        return static_cast<u32>(DataOffset);
    } else {
        using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;

        if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutData) {
            using RawArgType = typename ArgType::Type;
            constexpr size_t ArgAlign = alignof(RawArgType);
            constexpr size_t ArgSize = sizeof(RawArgType);

            static_assert(PrevAlign <= ArgAlign, "Output argument is not ordered by alignment");

            constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
            constexpr size_t ArgEnd = ArgOffset + ArgSize;

            return GetOutRawDataSize<MethodArguments, ArgAlign, ArgEnd, ArgIndex + 1>();
        } else {
            return GetOutRawDataSize<MethodArguments, PrevAlign, DataOffset, ArgIndex + 1>();
        }
    }
}

template <ArgumentType DataType, typename MethodArguments, size_t ArgCount = 0, size_t ArgIndex = 0>
constexpr u32 GetArgumentTypeCount() {
    if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) {
        return static_cast<u32>(ArgCount);
    } else {
        using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;

        if constexpr (ArgumentTraits<ArgType>::Type == DataType) {
            return GetArgumentTypeCount<DataType, MethodArguments, ArgCount + 1, ArgIndex + 1>();
        } else {
            return GetArgumentTypeCount<DataType, MethodArguments, ArgCount, ArgIndex + 1>();
        }
    }
}

template <typename MethodArguments>
constexpr RequestLayout GetNonDomainReplyInLayout() {
    return RequestLayout{
        .copy_handle_count = GetArgumentTypeCount<ArgumentType::InCopyHandle, MethodArguments>(),
        .move_handle_count = 0,
        .cmif_raw_data_size = GetInRawDataSize<MethodArguments>(),
        .domain_interface_count = 0,
    };
}

template <typename MethodArguments>
constexpr RequestLayout GetDomainReplyInLayout() {
    return RequestLayout{
        .copy_handle_count = GetArgumentTypeCount<ArgumentType::InCopyHandle, MethodArguments>(),
        .move_handle_count = 0,
        .cmif_raw_data_size = GetInRawDataSize<MethodArguments>(),
        .domain_interface_count = GetArgumentTypeCount<ArgumentType::InInterface, MethodArguments>(),
    };
}

template <typename MethodArguments>
constexpr RequestLayout GetNonDomainReplyOutLayout() {
    return RequestLayout{
        .copy_handle_count = GetArgumentTypeCount<ArgumentType::OutCopyHandle, MethodArguments>(),
        .move_handle_count = GetArgumentTypeCount<ArgumentType::OutMoveHandle, MethodArguments>() + GetArgumentTypeCount<ArgumentType::OutInterface, MethodArguments>(),
        .cmif_raw_data_size = GetOutRawDataSize<MethodArguments>(),
        .domain_interface_count = 0,
    };
}

template <typename MethodArguments>
constexpr RequestLayout GetDomainReplyOutLayout() {
    return RequestLayout{
        .copy_handle_count = GetArgumentTypeCount<ArgumentType::OutCopyHandle, MethodArguments>(),
        .move_handle_count = GetArgumentTypeCount<ArgumentType::OutMoveHandle, MethodArguments>(),
        .cmif_raw_data_size = GetOutRawDataSize<MethodArguments>(),
        .domain_interface_count = GetArgumentTypeCount<ArgumentType::OutInterface, MethodArguments>(),
    };
}

template <typename MethodArguments>
constexpr RequestLayout GetReplyInLayout(bool is_domain) {
    return is_domain ? GetDomainReplyInLayout<MethodArguments>() : GetNonDomainReplyInLayout<MethodArguments>();
}

template <typename MethodArguments>
constexpr RequestLayout GetReplyOutLayout(bool is_domain) {
    return is_domain ? GetDomainReplyOutLayout<MethodArguments>() : GetNonDomainReplyOutLayout<MethodArguments>();
}

using OutTemporaryBuffers = std::array<Common::ScratchBuffer<u8>, 3>;

template <typename MethodArguments, typename CallArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t HandleIndex = 0, size_t InBufferIndex = 0, size_t OutBufferIndex = 0, bool RawDataFinished = false, size_t ArgIndex = 0>
void ReadInArgument(bool is_domain, CallArguments& args, const u8* raw_data, HLERequestContext& ctx, OutTemporaryBuffers& temp) {
    if constexpr (ArgIndex >= std::tuple_size_v<CallArguments>) {
        return;
    } else {
        using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;

        if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InData || ArgumentTraits<ArgType>::Type == ArgumentType::InProcessId) {
            constexpr size_t ArgAlign = alignof(ArgType);
            constexpr size_t ArgSize = sizeof(ArgType);

            static_assert(PrevAlign <= ArgAlign, "Input argument is not ordered by alignment");
            static_assert(!RawDataFinished, "All input interface arguments must appear after raw data");
            static_assert(!std::is_pointer_v<ArgType>, "Input raw data must not be a pointer");
            static_assert(std::is_trivially_copyable_v<ArgType>, "Input raw data must be trivially copyable");

            constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
            constexpr size_t ArgEnd = ArgOffset + ArgSize;

            if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InProcessId) {
                // TODO: abort parsing if PID is not provided?
                // TODO: validate against raw data value?
                std::get<ArgIndex>(args).pid = ctx.GetPID();
            } else {
                std::memcpy(&std::get<ArgIndex>(args), raw_data + ArgOffset, ArgSize);
            }

            return ReadInArgument<MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, false, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
        } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InInterface) {
            constexpr size_t ArgAlign = alignof(u32);
            constexpr size_t ArgSize = sizeof(u32);
            constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
            constexpr size_t ArgEnd = ArgOffset + ArgSize;

            ASSERT(is_domain);
            ASSERT(ctx.GetDomainMessageHeader().input_object_count > 0);

            u32 value{};
            std::memcpy(&value, raw_data + ArgOffset, ArgSize);
            std::get<ArgIndex>(args) = ctx.GetDomainHandler<typename ArgType::element_type>(value - 1);

            return ReadInArgument<MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, true, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
        } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InCopyHandle) {
            std::get<ArgIndex>(args) = ctx.GetObjectFromHandle<typename ArgType::Type>(ctx.GetCopyHandle(HandleIndex)).GetPointerUnsafe();

            return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex + 1, InBufferIndex, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
        } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InLargeData) {
            constexpr size_t BufferSize = sizeof(typename ArgType::Type);

            // Clear the existing data.
            std::memset(&std::get<ArgIndex>(args), 0, BufferSize);

            std::span<const u8> buffer{};

            ASSERT(ctx.CanReadBuffer(InBufferIndex));
            if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
                buffer = ctx.ReadBuffer(InBufferIndex);
            } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
                buffer = ctx.ReadBufferA(InBufferIndex);
            } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
                buffer = ctx.ReadBufferX(InBufferIndex);
            }

            std::memcpy(&std::get<ArgIndex>(args), buffer.data(), std::min(BufferSize, buffer.size()));

            return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex + 1, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
        } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InBuffer) {
            using ElementType = typename ArgType::Type;

            std::span<const u8> buffer{};

            if (ctx.CanReadBuffer(InBufferIndex)) {
                if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
                    buffer = ctx.ReadBuffer(InBufferIndex);
                } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
                    buffer = ctx.ReadBufferA(InBufferIndex);
                } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
                    buffer = ctx.ReadBufferX(InBufferIndex);
                }
            }

            ElementType* ptr = (ElementType*) buffer.data();
            size_t size = buffer.size() / sizeof(ElementType);

            std::get<ArgIndex>(args) = std::span(ptr, size);

            return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex + 1, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
        } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutLargeData) {
            constexpr size_t BufferSize = sizeof(typename ArgType::Type);

            // Clear the existing data.
            std::memset(&std::get<ArgIndex>(args).raw, 0, BufferSize);

            return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
        } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutBuffer) {
            using ElementType = typename ArgType::Type;

            // Set up scratch buffer.
            auto& buffer = temp[OutBufferIndex];
            if (ctx.CanWriteBuffer(OutBufferIndex)) {
                buffer.resize_destructive(ctx.GetWriteBufferSize(OutBufferIndex));
            } else {
                buffer.resize_destructive(0);
            }

            ElementType* ptr = (ElementType*) buffer.data();
            size_t size = buffer.size() / sizeof(ElementType);

            std::get<ArgIndex>(args) = std::span(ptr, size);

            return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
        } else {
            return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
        }
    }
}

template <typename MethodArguments, typename CallArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t OutBufferIndex = 0, bool RawDataFinished = false, size_t ArgIndex = 0>
void WriteOutArgument(bool is_domain, CallArguments& args, u8* raw_data, HLERequestContext& ctx, OutTemporaryBuffers& temp) {
    if constexpr (ArgIndex >= std::tuple_size_v<CallArguments>) {
        return;
    } else {
        using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>;

        if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutData) {
            using RawArgType = decltype(std::get<ArgIndex>(args).raw);
            constexpr size_t ArgAlign = alignof(RawArgType);
            constexpr size_t ArgSize = sizeof(RawArgType);

            static_assert(PrevAlign <= ArgAlign, "Output argument is not ordered by alignment");
            static_assert(!RawDataFinished, "All output interface arguments must appear after raw data");
            static_assert(!std::is_pointer_v<ArgType>, "Output raw data must not be a pointer");
            static_assert(!std::is_pointer_v<RawArgType>, "Output raw data must not be a pointer");
            static_assert(std::is_trivially_copyable_v<RawArgType>, "Output raw data must be trivially copyable");

            constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign);
            constexpr size_t ArgEnd = ArgOffset + ArgSize;

            std::memcpy(raw_data + ArgOffset, &std::get<ArgIndex>(args).raw, ArgSize);

            return WriteOutArgument<MethodArguments, CallArguments, ArgAlign, ArgEnd, OutBufferIndex, false, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
        } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutInterface) {
            if (is_domain) {
                ctx.AddDomainObject(std::get<ArgIndex>(args).raw);
            } else {
                ctx.AddMoveInterface(std::get<ArgIndex>(args).raw);
            }

            return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, true, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
        } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutCopyHandle) {
            ctx.AddCopyObject(std::get<ArgIndex>(args).raw);

            return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
        } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutMoveHandle) {
            ctx.AddMoveObject(std::get<ArgIndex>(args).raw);

            return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
        } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutLargeData) {
            constexpr size_t BufferSize = sizeof(typename ArgType::Type);

            ASSERT(ctx.CanWriteBuffer(OutBufferIndex));
            if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
                ctx.WriteBuffer(std::get<ArgIndex>(args), OutBufferIndex);
            } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
                ctx.WriteBufferB(&std::get<ArgIndex>(args), BufferSize, OutBufferIndex);
            } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
                ctx.WriteBufferC(&std::get<ArgIndex>(args), BufferSize, OutBufferIndex);
            }

            return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
        } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutBuffer) {
            auto& buffer = temp[OutBufferIndex];
            const size_t size = buffer.size();

            if (size > 0 && ctx.CanWriteBuffer(OutBufferIndex)) {
                if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) {
                    ctx.WriteBuffer(buffer.data(), size, OutBufferIndex);
                } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) {
                    ctx.WriteBufferB(buffer.data(), size, OutBufferIndex);
                } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ {
                    ctx.WriteBufferC(buffer.data(), size, OutBufferIndex);
                }
            }

            return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
        } else {
            return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp);
        }
    }
}

template <bool Domain, typename T, typename... A>
void CmifReplyWrapImpl(HLERequestContext& ctx, T& t, Result (T::*f)(A...)) {
    // Verify domain state.
    if constexpr (!Domain) {
        ASSERT_MSG(!ctx.GetManager()->IsDomain(), "Non-domain reply used on domain session");
    }
    const bool is_domain = Domain ? ctx.GetManager()->IsDomain() : false;

    static_assert(ConstIfReference<A...>(), "Arguments taken by reference must be const");
    using MethodArguments = std::tuple<std::remove_cvref_t<A>...>;

    OutTemporaryBuffers buffers{};
    auto call_arguments = std::tuple<typename UnwrapArg<A>::Type...>();

    // Read inputs.
    const size_t offset_plus_command_id = ctx.GetDataPayloadOffset() + 2;
    ReadInArgument<MethodArguments>(is_domain, call_arguments, reinterpret_cast<u8*>(ctx.CommandBuffer() + offset_plus_command_id), ctx, buffers);

    // Call.
    const auto Callable = [&]<typename... CallArgs>(CallArgs&... args) {
        return (t.*f)(args...);
    };
    const Result res = std::apply(Callable, call_arguments);

    // Write result.
    const RequestLayout layout = GetReplyOutLayout<MethodArguments>(is_domain);
    IPC::ResponseBuilder rb{ctx, 2 + Common::DivCeil(layout.cmif_raw_data_size, sizeof(u32)), layout.copy_handle_count, layout.move_handle_count + layout.domain_interface_count};
    rb.Push(res);

    // Write out arguments.
    WriteOutArgument<MethodArguments>(is_domain, call_arguments, reinterpret_cast<u8*>(ctx.CommandBuffer() + rb.GetCurrentOffset()), ctx, buffers);
}
// clang-format on

template <typename Self>
template <bool Domain, auto F>
inline void ServiceFramework<Self>::CmifReplyWrap(HLERequestContext& ctx) {
    return CmifReplyWrapImpl<Domain>(ctx, *static_cast<Self*>(this), F);
}

} // namespace Service


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

#pragma once

#include <memory>
#include <span>

#include "common/common_types.h"

namespace Service {

// clang-format off
template <typename T>
struct AutoOut {
    T raw;
};

template <typename T>
class Out {
public:
    using Type = T;

    /* implicit */ Out(const Out& t) : raw(t.raw) {}
    /* implicit */ Out(AutoOut<Type>& t) : raw(&t.raw) {}
    /* implicit */ Out(Type* t) : raw(t) {}
    Out& operator=(const Out&) = delete;

    Type* Get() const {
        return raw;
    }

    Type& operator*() const {
        return *raw;
    }

    Type* operator->() const {
        return raw;
    }

    operator Type*() const {
        return raw;
    }

private:
    Type* raw;
};

template <typename T>
using SharedPointer = std::shared_ptr<T>;

template <typename T>
using OutInterface = Out<SharedPointer<T>>;

struct ClientProcessId {
    explicit operator bool() const {
        return pid != 0;
    }

    const u64& operator*() const {
        return pid;
    }

    u64 pid;
};

struct ProcessId {
    explicit ProcessId() : pid() {}
    explicit ProcessId(u64 p) : pid(p) {}
    /* implicit */ ProcessId(const ClientProcessId& c) : pid(c.pid) {}

    bool operator==(const ProcessId& rhs) const {
        return pid == rhs.pid;
    }

    explicit operator bool() const {
        return pid != 0;
    }

    const u64& operator*() const {
        return pid;
    }

    u64 pid;
};

using ClientAppletResourceUserId = ClientProcessId;
using AppletResourceUserId = ProcessId;

template <typename T>
class InCopyHandle {
public:
    using Type = T;

    /* implicit */ InCopyHandle(Type* t) : raw(t) {}
    /* implicit */ InCopyHandle() : raw() {}
    ~InCopyHandle() = default;

    InCopyHandle& operator=(Type* rhs) {
        raw = rhs;
        return *this;
    }

    Type* Get() const {
        return raw;
    }

    Type& operator*() const {
        return *raw;
    }

    Type* operator->() const {
        return raw;
    }

    explicit operator bool() const {
        return raw != nullptr;
    }

private:
    Type* raw;
};

template <typename T>
class OutCopyHandle {
public:
    using Type = T*;

    /* implicit */ OutCopyHandle(const OutCopyHandle& t) : raw(t.raw) {}
    /* implicit */ OutCopyHandle(AutoOut<Type>& t) : raw(&t.raw) {}
    /* implicit */ OutCopyHandle(Type* t) : raw(t) {}
    OutCopyHandle& operator=(const OutCopyHandle&) = delete;

    Type* Get() const {
        return raw;
    }

    Type& operator*() const {
        return *raw;
    }

    Type* operator->() const {
        return raw;
    }

    operator Type*() const {
        return raw;
    }

private:
    Type* raw;
};

template <typename T>
class OutMoveHandle {
public:
    using Type = T*;

    /* implicit */ OutMoveHandle(const OutMoveHandle& t) : raw(t.raw) {}
    /* implicit */ OutMoveHandle(AutoOut<Type>& t) : raw(&t.raw) {}
    /* implicit */ OutMoveHandle(Type* t) : raw(t) {}
    OutMoveHandle& operator=(const OutMoveHandle&) = delete;

    Type* Get() const {
        return raw;
    }

    Type& operator*() const {
        return *raw;
    }

    Type* operator->() const {
        return raw;
    }

    operator Type*() const {
        return raw;
    }

private:
    Type* raw;
};

enum BufferAttr : int {
    /* 0x01 */ BufferAttr_In = (1U << 0),
    /* 0x02 */ BufferAttr_Out = (1U << 1),
    /* 0x04 */ BufferAttr_HipcMapAlias = (1U << 2),
    /* 0x08 */ BufferAttr_HipcPointer = (1U << 3),
    /* 0x10 */ BufferAttr_FixedSize = (1U << 4),
    /* 0x20 */ BufferAttr_HipcAutoSelect = (1U << 5),
    /* 0x40 */ BufferAttr_HipcMapTransferAllowsNonSecure = (1U << 6),
    /* 0x80 */ BufferAttr_HipcMapTransferAllowsNonDevice = (1U << 7),
};

template <typename T, int A>
struct Buffer : public std::span<T> {
    static_assert(std::is_trivially_copyable_v<T>, "Buffer type must be trivially copyable");
    static_assert((A & BufferAttr_FixedSize) == 0, "Buffer attr must not contain FixedSize");
    static_assert(((A & BufferAttr_In) == 0) ^ ((A & BufferAttr_Out) == 0), "Buffer attr must be In or Out");
    static constexpr BufferAttr Attr = static_cast<BufferAttr>(A);
    using Type = T;

    /* implicit */ Buffer(const std::span<T>& rhs) : std::span<T>(rhs) {}
    /* implicit */ Buffer() = default;

    Buffer& operator=(const std::span<T>& rhs) {
        std::span<T>::operator=(rhs);
        return *this;
    }

    T& operator*() const {
        return *this->data();
    }

    explicit operator bool() const {
        return this->size() > 0;
    }
};

template <int A>
using InBuffer = Buffer<const u8, BufferAttr_In | A>;

template <typename T, int A>
using InArray = Buffer<T, BufferAttr_In | A>;

template <int A>
using OutBuffer = Buffer<u8, BufferAttr_Out | A>;

template <typename T, int A>
using OutArray = Buffer<T, BufferAttr_Out | A>;

template <typename T, int A>
class InLargeData {
public:
    static_assert(std::is_trivially_copyable_v<T>, "LargeData type must be trivially copyable");
    static_assert((A & BufferAttr_Out) == 0, "InLargeData attr must not be Out");
    static constexpr BufferAttr Attr = static_cast<BufferAttr>(A | BufferAttr_In | BufferAttr_FixedSize);
    using Type = const T;

    /* implicit */ InLargeData(Type& t) : raw(&t) {}
    ~InLargeData() = default;

    InLargeData& operator=(Type* rhs) {
        raw = rhs;
        return *this;
    }

    Type* Get() const {
        return raw;
    }

    Type& operator*() const {
        return *raw;
    }

    Type* operator->() const {
        return raw;
    }

    explicit operator bool() const {
        return raw != nullptr;
    }

private:
    Type* raw;
};

template <typename T, int A>
class OutLargeData {
public:
    static_assert(std::is_trivially_copyable_v<T>, "LargeData type must be trivially copyable");
    static_assert((A & BufferAttr_In) == 0, "OutLargeData attr must not be In");
    static constexpr BufferAttr Attr = static_cast<BufferAttr>(A | BufferAttr_Out | BufferAttr_FixedSize);
    using Type = T;

    /* implicit */ OutLargeData(const OutLargeData& t) : raw(t.raw) {}
    /* implicit */ OutLargeData(Type* t) : raw(t) {}
    /* implicit */ OutLargeData(AutoOut<T>& t) : raw(&t.raw) {}
    OutLargeData& operator=(const OutLargeData&) = delete;

    Type* Get() const {
        return raw;
    }

    Type& operator*() const {
        return *raw;
    }

    Type* operator->() const {
        return raw;
    }

    operator Type*() const {
        return raw;
    }

private:
    Type* raw;
};
// clang-format on

} // namespace Service


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/hle/service/cmif_serialization.h"
#include "core/hle/service/erpt/erpt.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"

namespace Service::ERPT {

class ErrorReportContext final : public ServiceFramework<ErrorReportContext> {
public:
    explicit ErrorReportContext(Core::System& system_) : ServiceFramework{system_, "erpt:c"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, C<&ErrorReportContext::SubmitContext>, "SubmitContext"},
            {1, C<&ErrorReportContext::CreateReportV0>, "CreateReportV0"},
            {2, nullptr, "SetInitialLaunchSettingsCompletionTime"},
            {3, nullptr, "ClearInitialLaunchSettingsCompletionTime"},
            {4, nullptr, "UpdatePowerOnTime"},
            {5, nullptr, "UpdateAwakeTime"},
            {6, nullptr, "SubmitMultipleCategoryContext"},
            {7, nullptr, "UpdateApplicationLaunchTime"},
            {8, nullptr, "ClearApplicationLaunchTime"},
            {9, nullptr, "SubmitAttachment"},
            {10, nullptr, "CreateReportWithAttachments"},
            {11, C<&ErrorReportContext::CreateReportV1>, "CreateReportV1"},
            {12, C<&ErrorReportContext::CreateReport>, "CreateReport"},
            {20, nullptr, "RegisterRunningApplet"},
            {21, nullptr, "UnregisterRunningApplet"},
            {22, nullptr, "UpdateAppletSuspendedDuration"},
            {30, nullptr, "InvalidateForcedShutdownDetection"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }

private:
    Result SubmitContext(InBuffer<BufferAttr_HipcMapAlias> context_entry,
                         InBuffer<BufferAttr_HipcMapAlias> field_list) {
        LOG_WARNING(Service_SET, "(STUBBED) called, context_entry_size={}, field_list_size={}",
                    context_entry.size(), field_list.size());
        R_SUCCEED();
    }

    Result CreateReportV0(u32 report_type, InBuffer<BufferAttr_HipcMapAlias> context_entry,
                          InBuffer<BufferAttr_HipcMapAlias> report_list,
                          InBuffer<BufferAttr_HipcMapAlias> report_meta_data) {
        LOG_WARNING(Service_SET, "(STUBBED) called, report_type={:#x}", report_type);
        R_SUCCEED();
    }

    Result CreateReportV1(u32 report_type, u32 unknown,
                          InBuffer<BufferAttr_HipcMapAlias> context_entry,
                          InBuffer<BufferAttr_HipcMapAlias> report_list,
                          InBuffer<BufferAttr_HipcMapAlias> report_meta_data) {
        LOG_WARNING(Service_SET, "(STUBBED) called, report_type={:#x}, unknown={:#x}", report_type,
                    unknown);
        R_SUCCEED();
    }

    Result CreateReport(u32 report_type, u32 unknown, u32 create_report_option_flag,
                        InBuffer<BufferAttr_HipcMapAlias> context_entry,
                        InBuffer<BufferAttr_HipcMapAlias> report_list,
                        InBuffer<BufferAttr_HipcMapAlias> report_meta_data) {
        LOG_WARNING(
            Service_SET,
            "(STUBBED) called, report_type={:#x}, unknown={:#x}, create_report_option_flag={:#x}",
            report_type, unknown, create_report_option_flag);
        R_SUCCEED();
    }
};

class ErrorReportSession final : public ServiceFramework<ErrorReportSession> {
public:
    explicit ErrorReportSession(Core::System& system_) : ServiceFramework{system_, "erpt:r"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, nullptr, "OpenReport"},
            {1, nullptr, "OpenManager"},
            {2, nullptr, "OpenAttachment"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }
};

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

    server_manager->RegisterNamedService("erpt:c", std::make_shared<ErrorReportContext>(system));
    server_manager->RegisterNamedService("erpt:r", std::make_shared<ErrorReportSession>(system));

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

} // namespace Service::ERPT


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

#pragma once

namespace Core {
class System;
}

namespace Service::ERPT {

void LoopProcess(Core::System& system);

} // namespace Service::ERPT