// Copyright 2023 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once #include #include #include #include "common/common_types.h" #include "core/hle/result.h" #include "core/loader/loader.h" namespace Kernel { class MappedBuffer; } namespace FileSys { class ArchiveBackend; struct Entry; class Path; } // namespace FileSys namespace Service::BOSS { constexpr u32 BOSS_PAYLOAD_HEADER_LENGTH = 0x28; constexpr u32 BOSS_MAGIC = Loader::MakeMagic('b', 'o', 's', 's'); constexpr u32 BOSS_PAYLOAD_MAGIC = 0x10001; constexpr u64 NEWS_PROG_ID = 0x0004013000003502; constexpr u32 BOSS_CONTENT_HEADER_LENGTH = 0x132; constexpr u32 BOSS_HEADER_WITH_HASH_LENGTH = 0x13C; constexpr u32 BOSS_ENTIRE_HEADER_LENGTH = BOSS_CONTENT_HEADER_LENGTH + BOSS_HEADER_WITH_HASH_LENGTH; constexpr u32 BOSS_EXTDATA_HEADER_LENGTH = 0x18; constexpr u32 BOSS_S_ENTRY_SIZE = 0xC00; constexpr u32 BOSS_SAVE_HEADER_SIZE = 4; constexpr std::size_t TASK_ID_SIZE = 8; constexpr std::size_t URL_SIZE = 0x200; constexpr std::size_t HEADERS_SIZE = 0x360; constexpr std::size_t CERTIDLIST_SIZE = 3; constexpr std::size_t TASKIDLIST_SIZE = 0x400; constexpr std::array boss_system_savedata_id{ 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x01, 0x00, }; #pragma pack(push, 4) struct BossHeader { u8 header_length; std::array zero1; u32_be unknown; u32_be download_date; std::array zero2; u64_be program_id; std::array zero3; u32_be datatype; u32_be payload_size; u32_be ns_data_id; u32_be version; }; #pragma pack(pop) static_assert(sizeof(BossHeader) == 0x34, "BossHeader has incorrect size"); struct NsDataEntry { std::string filename; BossHeader header; }; enum class NsDataHeaderInfoType : u8 { ProgramId = 0, Unknown = 1, Datatype = 2, PayloadSize = 3, NsDataId = 4, Version = 5, Everything = 6, }; struct NsDataHeaderInfo { u64 program_id; INSERT_PADDING_BYTES(4); u32 datatype; u32 payload_size; u32 ns_data_id; u32 version; INSERT_PADDING_BYTES(4); }; static_assert(sizeof(NsDataHeaderInfo) == 0x20, "NsDataHeaderInfo has incorrect size"); enum class PropertyID : u16 { Interval = 0x03, Duration = 0x04, Url = 0x07, Headers = 0x0D, CertId = 0x0E, CertIdList = 0x0F, LoadCert = 0x10, LoadRootCert = 0x11, TotalTasks = 0x35, TaskIdList = 0x36, }; struct BossSVData { INSERT_PADDING_BYTES(0x10); u64 program_id; std::array task_id; }; struct BossSSData { INSERT_PADDING_BYTES(0x21C); std::array url; }; using BossTaskProperty = std::variant, std::vector>; struct BossTaskProperties { bool task_result; std::map properties{ {static_cast(0x00), u8()}, {static_cast(0x01), u8()}, {static_cast(0x02), u32()}, {PropertyID::Interval, u32()}, {PropertyID::Duration, u32()}, {static_cast(0x05), u8()}, {static_cast(0x06), u8()}, {PropertyID::Url, std::vector(URL_SIZE)}, {static_cast(0x08), u32()}, {static_cast(0x09), u8()}, {static_cast(0x0A), std::vector(0x100)}, {static_cast(0x0B), std::vector(0x200)}, {static_cast(0x0C), u32()}, {PropertyID::Headers, std::vector(HEADERS_SIZE)}, {PropertyID::CertId, u32()}, {PropertyID::CertIdList, std::vector(CERTIDLIST_SIZE)}, {PropertyID::LoadCert, u8()}, {PropertyID::LoadRootCert, u8()}, {static_cast(0x12), u8()}, {static_cast(0x13), u32()}, {static_cast(0x14), u32()}, {static_cast(0x15), std::vector(0x40)}, {static_cast(0x16), u32()}, {static_cast(0x18), u8()}, {static_cast(0x19), u8()}, {static_cast(0x1A), u8()}, {static_cast(0x1B), u32()}, {static_cast(0x1C), u32()}, {static_cast(0x1D), u8()}, {static_cast(0x1E), u8()}, {static_cast(0x1F), u8()}, {static_cast(0x20), u8()}, {static_cast(0x21), u8()}, {static_cast(0x22), u8()}, {static_cast(0x23), u32()}, {static_cast(0x24), u8()}, {static_cast(0x25), u32()}, {static_cast(0x26), u32()}, {static_cast(0x27), u32()}, {static_cast(0x28), u64()}, {static_cast(0x29), u64()}, {static_cast(0x2A), u32()}, {static_cast(0x2B), u32()}, {static_cast(0x2C), u8()}, {static_cast(0x2D), u16()}, {static_cast(0x2E), u16()}, {static_cast(0x2F), std::vector(0x40)}, {PropertyID::TotalTasks, u16()}, {PropertyID::TaskIdList, std::vector(TASKIDLIST_SIZE)}, {static_cast(0x3B), u32()}, {static_cast(0x3E), std::vector(0x200)}, {static_cast(0x3F), u8()}, }; template void serialize(Archive& ar, const unsigned int); friend class boost::serialization::access; }; class OnlineService final { public: explicit OnlineService(u64 program_id_, u64 extdata_id_); Result InitializeSession(u64 init_program_id); void RegisterTask(const u32 size, Kernel::MappedBuffer& buffer); Result UnregisterTask(const u32 size, Kernel::MappedBuffer& buffer); void GetTaskIdList(); u16 GetNsDataIdList(const u32 filter, const u32 max_entries, Kernel::MappedBuffer& buffer); std::optional GetNsDataEntryFromId(const u32 ns_data_id); Result GetNsDataHeaderInfo(const u32 ns_data_id, const NsDataHeaderInfoType type, const u32 size, Kernel::MappedBuffer& buffer); ResultVal ReadNsData(const u32 ns_data_id, const u64 offset, const u32 size, Kernel::MappedBuffer& buffer); Result SendProperty(const u16 id, const u32 size, Kernel::MappedBuffer& buffer); Result ReceiveProperty(const u16 id, const u32 size, Kernel::MappedBuffer& buffer); private: std::unique_ptr OpenBossExtData(); std::vector GetBossExtDataFiles(FileSys::ArchiveBackend* boss_archive); FileSys::Path GetBossDataDir(); std::vector GetNsDataEntries(); BossTaskProperties current_props; std::map task_id_list; u64 program_id; u64 extdata_id; // For serialization explicit OnlineService() = default; template void serialize(Archive& ar, const unsigned int); friend class boost::serialization::access; }; } // namespace Service::BOSS