From b04e39107fd947d59b76214747a81b8cb6fe92a8 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 28 Oct 2018 14:55:32 -0400 Subject: control_metadata: Add GetRawBytes function to NACP Returns the raw bytes of the NACP file. Needed for GetApplicationControlData which returns the raw, unprocessed NACP to the game. --- src/core/file_sys/control_metadata.cpp | 6 ++++++ src/core/file_sys/control_metadata.h | 1 + 2 files changed, 7 insertions(+) diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index a012c2be9..c8fa912bf 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp @@ -66,4 +66,10 @@ std::string NACP::GetVersionString() const { return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), raw->version_string.size()); } + +std::vector NACP::GetRawBytes() const { + std::vector out(sizeof(RawNACP)); + std::memcpy(out.data(), raw.get(), sizeof(RawNACP)); + return out; +} } // namespace FileSys diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index 141f7e056..bfaad46b4 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h @@ -81,6 +81,7 @@ public: u64 GetTitleId() const; u64 GetDLCBaseTitleId() const; std::string GetVersionString() const; + std::vector GetRawBytes() const; private: std::unique_ptr raw; -- cgit v1.2.3 From f2f679bf3f823592aa9d13294e76c24c47288305 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 28 Oct 2018 14:56:12 -0400 Subject: loader/nro: Call RegisterRomFS from Load Allows NRO homebrew to use the RomFS in the ASET section. --- src/core/loader/nro.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index bc8e402a8..c8e491fec 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -12,10 +12,12 @@ #include "common/swap.h" #include "core/core.h" #include "core/file_sys/control_metadata.h" +#include "core/file_sys/romfs_factory.h" #include "core/file_sys/vfs_offset.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/vm_manager.h" +#include "core/hle/service/filesystem/filesystem.h" #include "core/loader/nro.h" #include "core/loader/nso.h" #include "core/memory.h" @@ -208,6 +210,9 @@ ResultStatus AppLoader_NRO::Load(Kernel::Process& process) { return ResultStatus::ErrorLoadingNRO; } + if (romfs != nullptr) + Service::FileSystem::RegisterRomFS(std::make_unique(*this)); + process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); is_loaded = true; -- cgit v1.2.3 From df264d2ccb39a4d7ffd4efd3158e66db3aa6952a Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 28 Oct 2018 14:56:43 -0400 Subject: savedata_factory: Expose accessors for SaveDataSpace --- src/core/file_sys/savedata_factory.cpp | 32 +++++++++++++++----------- src/core/file_sys/savedata_factory.h | 3 +++ src/core/hle/service/filesystem/filesystem.cpp | 10 ++++++++ src/core/hle/service/filesystem/filesystem.h | 1 + 4 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index ef1aaebbb..1ec54c78f 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -83,28 +83,32 @@ ResultVal SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr return MakeResult(std::move(out)); } -std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, - u128 user_id, u64 save_id) { - // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should - // be interpreted as the title id of the current process. - if (type == SaveDataType::SaveData && title_id == 0) - title_id = Core::CurrentProcess()->GetTitleID(); - - std::string out; +VirtualDir SaveDataFactory::GetSaveDataSpaceDirectory(SaveDataSpaceId space) { + return dir->GetDirectoryRelative(GetSaveDataSpaceIdPath(space)); +} +std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) { switch (space) { case SaveDataSpaceId::NandSystem: - out = "/system/"; - break; + return "/system/"; case SaveDataSpaceId::NandUser: - out = "/user/"; - break; + return "/user/"; case SaveDataSpaceId::TemporaryStorage: - out = "/temp/"; - break; + return "/temp/"; default: ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast(space)); + return "/unrecognized/"; ///< To prevent corruption when ignoring asserts. } +} + +std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, + u128 user_id, u64 save_id) { + // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should + // be interpreted as the title id of the current process. + if (type == SaveDataType::SaveData && title_id == 0) + title_id = Core::CurrentProcess()->GetTitleID(); + + std::string out = GetSaveDataSpaceIdPath(space); switch (type) { case SaveDataType::SystemSaveData: diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index d69ef6741..024a305d3 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h @@ -52,6 +52,9 @@ public: ResultVal Open(SaveDataSpaceId space, SaveDataDescriptor meta); + VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space); + + static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space); static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, u64 save_id); diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index e32a7c48e..ea8fd965a 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -309,6 +309,16 @@ ResultVal OpenSaveData(FileSys::SaveDataSpaceId space, return save_data_factory->Open(space, save_struct); } +ResultVal OpenSaveDataSpace(FileSys::SaveDataSpaceId space) { + LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast(space)); + + if (save_data_factory == nullptr) { + return ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound); + } + + return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space)); +} + ResultVal OpenSDMC() { LOG_TRACE(Service_FS, "Opening SDMC"); diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 6ca5c5636..2cbb70c87 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -45,6 +45,7 @@ ResultVal OpenRomFS(u64 title_id, FileSys::StorageId stora FileSys::ContentRecordType type); ResultVal OpenSaveData(FileSys::SaveDataSpaceId space, FileSys::SaveDataDescriptor save_struct); +ResultVal OpenSaveDataSpace(FileSys::SaveDataSpaceId space); ResultVal OpenSDMC(); std::unique_ptr GetUnionContents(); -- cgit v1.2.3 From 2e8177f0c93768e1d1a7d6a830e9c68111c2afd6 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 28 Oct 2018 14:58:09 -0400 Subject: fsp_srv: Implement command 61: OpenSaveDataInfoReaderBySaveDataSpaceId Needed by Checkpoint. Returns an object that can iterate through all savedata on the system. --- src/core/hle/service/filesystem/fsp_srv.cpp | 13 ++++++++++++- src/core/hle/service/filesystem/fsp_srv.h | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index c1c83a11d..56102a3db 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -452,6 +452,7 @@ private: }; FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { + // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "MountContent"}, {1, &FSP_SRV::Initialize, "Initialize"}, @@ -485,7 +486,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { {58, nullptr, "ReadSaveDataFileSystemExtraData"}, {59, nullptr, "WriteSaveDataFileSystemExtraData"}, {60, nullptr, "OpenSaveDataInfoReader"}, - {61, nullptr, "OpenSaveDataInfoReaderBySaveDataSpaceId"}, + {61, &FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId, "OpenSaveDataInfoReaderBySaveDataSpaceId"}, {62, nullptr, "OpenCacheStorageList"}, {64, nullptr, "OpenSaveDataInternalStorageFileSystem"}, {65, nullptr, "UpdateSaveDataMacForDebug"}, @@ -544,6 +545,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { {1009, nullptr, "GetAndClearMemoryReportInfo"}, {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, }; + // clang-format on RegisterHandlers(functions); } @@ -618,6 +620,15 @@ void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) { MountSaveData(ctx); } +void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto space = rp.PopRaw(); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface(std::make_shared(space)); +} + void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_FS, "(STUBBED) called"); diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index 4aa0358cb..e7abec0a3 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h @@ -25,6 +25,7 @@ private: void CreateSaveData(Kernel::HLERequestContext& ctx); void MountSaveData(Kernel::HLERequestContext& ctx); void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx); + void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx); void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx); -- cgit v1.2.3 From 5ee19add1bf221bd5259e4df76089252f04ce060 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 28 Oct 2018 14:59:17 -0400 Subject: fsp_srv: Implement ISaveDataInfoReader An object to read SaveDataInfo objects, which describe a unique save on the system. This implementation iterates through all the directories in the save data space and uses the paths to reconstruct the metadata. --- src/core/hle/service/filesystem/fsp_srv.cpp | 144 ++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 56102a3db..3d1c2ecda 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -11,6 +11,7 @@ #include "common/assert.h" #include "common/common_types.h" +#include "common/hex_util.h" #include "common/logging/log.h" #include "common/string_util.h" #include "core/file_sys/directory.h" @@ -451,6 +452,149 @@ private: VfsDirectoryServiceWrapper backend; }; +class ISaveDataInfoReader final : public ServiceFramework { +public: + explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space) + : ServiceFramework("ISaveDataInfoReader") { + static const FunctionInfo functions[] = { + {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"}, + }; + RegisterHandlers(functions); + + FindAllSaves(space); + } + + void ReadSaveDataInfo(Kernel::HLERequestContext& ctx) { + // Calculate how many entries we can fit in the output buffer + const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(SaveDataInfo); + + // Cap at total number of entries. + const u64 actual_entries = std::min(count_entries, info.size() - next_entry_index); + + // Determine data start and end + const auto* begin = reinterpret_cast(info.data() + next_entry_index); + const auto* end = reinterpret_cast(info.data() + next_entry_index + actual_entries); + const auto range_size = static_cast(std::distance(begin, end)); + + next_entry_index += actual_entries; + + // Write the data to memory + ctx.WriteBuffer(begin, range_size); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(actual_entries); + } + +private: + static u64 stoull_be(std::string_view str) { + if (str.size() != 16) + return 0; + + const auto bytes = Common::HexStringToArray<0x8>(str); + u64 out{}; + std::memcpy(&out, bytes.data(), sizeof(u64)); + + return Common::swap64(out); + } + + static std::array ArraySwap(const std::array& in) { + std::array out; + for (std::size_t i = 0; i < in.size(); ++i) { + out[0xF - i] = in[i]; + } + + return out; + } + + void FindAllSaves(FileSys::SaveDataSpaceId space) { + const auto save_root = OpenSaveDataSpace(space); + ASSERT(save_root.Succeeded()); + + for (const auto& type : (*save_root)->GetSubdirectories()) { + if (type->GetName() == "save") { + for (const auto& save_id : type->GetSubdirectories()) { + for (const auto& user_id : save_id->GetSubdirectories()) { + const auto save_id_numeric = stoull_be(save_id->GetName()); + const auto user_id_numeric = + ArraySwap(Common::HexStringToArray<0x10>(user_id->GetName())); + if (save_id_numeric != 0) { + // System Save Data + info.emplace_back(SaveDataInfo{ + 0, + space, + FileSys::SaveDataType::SystemSaveData, + {}, + user_id_numeric, + save_id_numeric, + 0, + user_id->GetSize(), + {}, + }); + + continue; + } + + for (const auto& title_id : user_id->GetSubdirectories()) { + const auto device = + std::all_of(user_id_numeric.begin(), user_id_numeric.end(), + [](u8 val) { return val == 0; }); + info.emplace_back(SaveDataInfo{ + 0, + space, + device ? FileSys::SaveDataType::DeviceSaveData + : FileSys::SaveDataType::SaveData, + {}, + user_id_numeric, + save_id_numeric, + stoull_be(title_id->GetName()), + title_id->GetSize(), + {}, + }); + } + } + } + } else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) { + // Temporary Storage + for (const auto& user_id : type->GetSubdirectories()) { + for (const auto& title_id : user_id->GetSubdirectories()) { + if (!title_id->GetFiles().empty() || + !title_id->GetSubdirectories().empty()) { + info.emplace_back(SaveDataInfo{ + 0, + space, + FileSys::SaveDataType::TemporaryStorage, + {}, + Common::HexStringToArray<0x10, true>(user_id->GetName()), + stoull_be(type->GetName()), + stoull_be(title_id->GetName()), + title_id->GetSize(), + {}, + }); + } + } + } + } + } + } + + struct SaveDataInfo { + u64_le save_id_unknown; + FileSys::SaveDataSpaceId space; + FileSys::SaveDataType type; + INSERT_PADDING_BYTES(0x6); + std::array user_id; + u64_le save_id; + u64_le title_id; + u64_le save_image_size; + INSERT_PADDING_BYTES(0x28); + }; + static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); + + std::vector info; + u64 next_entry_index = 0; +}; + FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { // clang-format off static const FunctionInfo functions[] = { -- cgit v1.2.3 From bdaa76c0dbcf6811ae83bbca61ae103e06e93c27 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Mon, 29 Oct 2018 16:20:10 -0400 Subject: ns: Implement command 400: GetApplicationControlData Returns the raw NACP bytes and the raw icon bytes into a title-provided buffer. Pulls from Registration Cache for control data, returning all zeros should it not exist. --- src/core/file_sys/savedata_factory.cpp | 2 +- src/core/file_sys/savedata_factory.h | 2 +- src/core/hle/service/filesystem/fsp_srv.cpp | 24 +++++------ src/core/hle/service/ns/ns.cpp | 64 ++++++++++++++++++++++++++++- 4 files changed, 75 insertions(+), 17 deletions(-) diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index 1ec54c78f..5434f2149 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -83,7 +83,7 @@ ResultVal SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr return MakeResult(std::move(out)); } -VirtualDir SaveDataFactory::GetSaveDataSpaceDirectory(SaveDataSpaceId space) { +VirtualDir SaveDataFactory::GetSaveDataSpaceDirectory(SaveDataSpaceId space) const { return dir->GetDirectoryRelative(GetSaveDataSpaceIdPath(space)); } diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index 024a305d3..2a0088040 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h @@ -52,7 +52,7 @@ public: ResultVal Open(SaveDataSpaceId space, SaveDataDescriptor meta); - VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space); + VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space); static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 3d1c2ecda..b9a1d5105 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -481,9 +481,9 @@ public: // Write the data to memory ctx.WriteBuffer(begin, range_size); - IPC::ResponseBuilder rb{ctx, 4}; + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(actual_entries); + rb.Push(static_cast(actual_entries)); } private: @@ -498,15 +498,6 @@ private: return Common::swap64(out); } - static std::array ArraySwap(const std::array& in) { - std::array out; - for (std::size_t i = 0; i < in.size(); ++i) { - out[0xF - i] = in[i]; - } - - return out; - } - void FindAllSaves(FileSys::SaveDataSpaceId space) { const auto save_root = OpenSaveDataSpace(space); ASSERT(save_root.Succeeded()); @@ -516,8 +507,9 @@ private: for (const auto& save_id : type->GetSubdirectories()) { for (const auto& user_id : save_id->GetSubdirectories()) { const auto save_id_numeric = stoull_be(save_id->GetName()); - const auto user_id_numeric = - ArraySwap(Common::HexStringToArray<0x10>(user_id->GetName())); + auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName()); + std::reverse(user_id_numeric.begin(), user_id_numeric.end()); + if (save_id_numeric != 0) { // System Save Data info.emplace_back(SaveDataInfo{ @@ -560,12 +552,16 @@ private: for (const auto& title_id : user_id->GetSubdirectories()) { if (!title_id->GetFiles().empty() || !title_id->GetSubdirectories().empty()) { + auto user_id_numeric = + Common::HexStringToArray<0x10>(user_id->GetName()); + std::reverse(user_id_numeric.begin(), user_id_numeric.end()); + info.emplace_back(SaveDataInfo{ 0, space, FileSys::SaveDataType::TemporaryStorage, {}, - Common::HexStringToArray<0x10, true>(user_id->GetName()), + user_id_numeric, stoull_be(type->GetName()), stoull_be(title_id->GetName()), title_id->GetSize(), diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 07c1381fe..1d2978f24 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -2,6 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/patch_manager.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/ns/ns.h" @@ -118,7 +121,7 @@ public: {305, nullptr, "TerminateSystemApplet"}, {306, nullptr, "LaunchOverlayApplet"}, {307, nullptr, "TerminateOverlayApplet"}, - {400, nullptr, "GetApplicationControlData"}, + {400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"}, {401, nullptr, "InvalidateAllApplicationControlCache"}, {402, nullptr, "RequestDownloadApplicationControlData"}, {403, nullptr, "GetMaxApplicationControlCacheCount"}, @@ -243,6 +246,65 @@ public: RegisterHandlers(functions); } + + void GetApplicationControlData(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto flag = rp.PopRaw(); + LOG_DEBUG(Service_NS, "called with flag={:016X}", flag); + + const auto title_id = rp.PopRaw(); + + const auto size = ctx.GetWriteBufferSize(); + + const FileSys::PatchManager pm{title_id}; + const auto control = pm.GetControlMetadata(); + + std::vector out; + + if (control.first != nullptr) { + if (size < 0x4000) { + LOG_ERROR(Service_NS, + "output buffer is too small! (actual={:016X}, expected_min=0x4000)", + size); + IPC::ResponseBuilder rb{ctx, 2}; + // TODO(DarkLordZach): Find a better error code for this. + rb.Push(ResultCode(-1)); + return; + } + + out.resize(0x4000); + const auto bytes = control.first->GetRawBytes(); + std::memcpy(out.data(), bytes.data(), bytes.size()); + } else { + LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.", + title_id); + out.resize(std::min(0x4000, size)); + } + + if (control.second != nullptr) { + if (size < 0x4000 + control.second->GetSize()) { + LOG_ERROR(Service_NS, + "output buffer is too small! (actual={:016X}, expected_min={:016X})", + size, 0x4000 + control.second->GetSize()); + IPC::ResponseBuilder rb{ctx, 2}; + // TODO(DarkLordZach): Find a better error code for this. + rb.Push(ResultCode(-1)); + return; + } + + out.resize(0x4000 + control.second->GetSize()); + control.second->Read(out.data() + 0x4000, control.second->GetSize()); + } else { + LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.", + title_id); + } + + ctx.WriteBuffer(out); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast(out.size())); + } }; class IApplicationVersionInterface final : public ServiceFramework { -- cgit v1.2.3