From a3dcb40e826e8c670824397ce8a4661db9f16285 Mon Sep 17 00:00:00 2001 From: citrabot Date: Wed, 22 Nov 2023 00:25:59 +0000 Subject: [PATCH] Merge PR 6883 --- src/core/CMakeLists.txt | 2 + src/core/file_sys/archive_backend.h | 1 + src/core/file_sys/archive_extsavedata.cpp | 7 +- src/core/file_sys/archive_extsavedata.h | 4 +- src/core/hle/service/boss/boss.cpp | 210 +++++----- src/core/hle/service/boss/boss.h | 5 +- src/core/hle/service/boss/online_service.cpp | 384 +++++++++++++++++++ src/core/hle/service/boss/online_service.h | 159 ++++++++ src/core/hle/service/fs/archive.cpp | 4 + src/core/hle/service/fs/archive.h | 1 + 10 files changed, 682 insertions(+), 95 deletions(-) create mode 100644 src/core/hle/service/boss/online_service.cpp create mode 100644 src/core/hle/service/boss/online_service.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 15c532c87..228260b31 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -236,6 +236,8 @@ add_library(citra_core STATIC hle/service/boss/boss_p.h hle/service/boss/boss_u.cpp hle/service/boss/boss_u.h + hle/service/boss/online_service.cpp + hle/service/boss/online_service.h hle/service/cam/cam.cpp hle/service/cam/cam.h hle/service/cam/cam_c.cpp diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/archive_backend.h index 0fada0f23..8dc0f14d8 100644 --- a/src/core/file_sys/archive_backend.h +++ b/src/core/file_sys/archive_backend.h @@ -41,6 +41,7 @@ class Path { public: Path() : type(LowPathType::Invalid) {} Path(const char* path) : type(LowPathType::Char), string(path) {} + Path(std::string path) : type(LowPathType::Char), string(std::move(path)) {} Path(std::vector binary_data) : type(LowPathType::Binary), binary(std::move(binary_data)) {} template Path(const std::array& binary_data) diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp index 7ab54d39c..23af4b5fa 100644 --- a/src/core/file_sys/archive_extsavedata.cpp +++ b/src/core/file_sys/archive_extsavedata.cpp @@ -216,8 +216,8 @@ Path ConstructExtDataBinaryPath(u32 media_type, u32 high, u32 low) { } ArchiveFactory_ExtSaveData::ArchiveFactory_ExtSaveData(const std::string& mount_location, - bool shared) - : shared(shared), mount_point(GetExtDataContainerPath(mount_location, shared)) { + bool shared, bool boss) + : shared(shared), boss(boss), mount_point(GetExtDataContainerPath(mount_location, shared)) { LOG_DEBUG(Service_FS, "Directory {} set as base for ExtSaveData.", mount_point); } @@ -242,7 +242,8 @@ Path ArchiveFactory_ExtSaveData::GetCorrectedPath(const Path& path) { ResultVal> ArchiveFactory_ExtSaveData::Open(const Path& path, u64 program_id) { - std::string fullpath = GetExtSaveDataPath(mount_point, GetCorrectedPath(path)) + "user/"; + std::string fullpath = + GetExtSaveDataPath(mount_point, GetCorrectedPath(path)) + (boss ? "boss/" : "user/"); if (!FileUtil::Exists(fullpath)) { // TODO(Subv): Verify the archive behavior of SharedExtSaveData compared to ExtSaveData. // ExtSaveData seems to return FS_NotFound (120) when the archive doesn't exist. diff --git a/src/core/file_sys/archive_extsavedata.h b/src/core/file_sys/archive_extsavedata.h index dec84ade9..e8059adaf 100644 --- a/src/core/file_sys/archive_extsavedata.h +++ b/src/core/file_sys/archive_extsavedata.h @@ -18,7 +18,7 @@ namespace FileSys { /// File system interface to the ExtSaveData archive class ArchiveFactory_ExtSaveData final : public ArchiveFactory { public: - ArchiveFactory_ExtSaveData(const std::string& mount_point, bool shared); + ArchiveFactory_ExtSaveData(const std::string& mount_point, bool shared, bool boss = false); std::string GetName() const override { return "ExtSaveData"; @@ -44,6 +44,7 @@ public: private: bool shared; ///< Whether this archive represents an ExtSaveData archive or a SharedExtSaveData /// archive + bool boss; ///< Whether this archive represents a Spotpass archive /** * This holds the full directory path for this archive, it is only set after a successful call @@ -61,6 +62,7 @@ private: ar& boost::serialization::base_object(*this); ar& shared; ar& mount_point; + ar& boss; } friend class boost::serialization::access; }; diff --git a/src/core/hle/service/boss/boss.cpp b/src/core/hle/service/boss/boss.cpp index ea29786cd..b520a5c4d 100644 --- a/src/core/hle/service/boss/boss.cpp +++ b/src/core/hle/service/boss/boss.cpp @@ -14,13 +14,15 @@ namespace Service::BOSS { void Module::Interface::InitializeSession(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); - const u64 programID = rp.Pop(); + const u64 program_id = rp.Pop(); rp.PopPID(); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(RESULT_SUCCESS); + const auto result = online_service.InitializeSession(program_id); - LOG_WARNING(Service_BOSS, "(STUBBED) programID={:#018X}", programID); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(result); + + LOG_DEBUG(Service_BOSS, "called, program_id={:#018x}", program_id); } void Module::Interface::SetStorageInfo(Kernel::HLERequestContext& ctx) { @@ -33,7 +35,7 @@ void Module::Interface::SetStorageInfo(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_BOSS, - "(STUBBED) extdata_id={:#018X}, boss_size={:#010X}, extdata_type={:#04X}", + "(STUBBED) extdata_id={:#018x}, boss_size={:#010x}, extdata_type={:#04x}", extdata_id, boss_size, extdata_type); } @@ -80,7 +82,7 @@ void Module::Interface::RegisterPrivateClientCert(Kernel::HLERequestContext& ctx rb.PushMappedBuffer(buffer1); rb.PushMappedBuffer(buffer2); - LOG_WARNING(Service_BOSS, "(STUBBED) buffer1_size={:#010X}, buffer2_size={:#010X}, ", + LOG_WARNING(Service_BOSS, "(STUBBED) buffer1_size={:#010x}, buffer2_size={:#010x}, ", buffer1_size, buffer2_size); } @@ -131,12 +133,14 @@ void Module::Interface::RegisterTask(Kernel::HLERequestContext& ctx) { const u8 unk_param3 = rp.Pop(); auto& buffer = rp.PopMappedBuffer(); + online_service.RegisterTask(size, buffer); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(RESULT_SUCCESS); rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}, unk_param2={:#04X}, unk_param3={:#04X}", - size, unk_param2, unk_param3); + LOG_DEBUG(Service_BOSS, "called, size={:#010x}, unk_param2={:#04x}, unk_param3={:#04x}", size, + unk_param2, unk_param3); } void Module::Interface::UnregisterTask(Kernel::HLERequestContext& ctx) { @@ -145,11 +149,13 @@ void Module::Interface::UnregisterTask(Kernel::HLERequestContext& ctx) { const u8 unk_param2 = rp.Pop(); auto& buffer = rp.PopMappedBuffer(); + const auto result = online_service.UnregisterTask(size, buffer); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); - rb.Push(RESULT_SUCCESS); + rb.Push(result); rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}, unk_param2={:#04X}", size, unk_param2); + LOG_DEBUG(Service_BOSS, "called, size={:#010x}, unk_param2={:#04x}", size, unk_param2); } void Module::Interface::ReconfigureTask(Kernel::HLERequestContext& ctx) { @@ -162,16 +168,18 @@ void Module::Interface::ReconfigureTask(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}, unk_param2={:#04X}", size, unk_param2); + LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010x}, unk_param2={:#04x}", size, unk_param2); } void Module::Interface::GetTaskIdList(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); + online_service.GetTaskIdList(); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_BOSS, "(STUBBED) called"); + LOG_DEBUG(Service_BOSS, "called"); } void Module::Interface::GetStepIdList(Kernel::HLERequestContext& ctx) { @@ -183,7 +191,7 @@ void Module::Interface::GetStepIdList(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}", size); + LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010x}", size); } void Module::Interface::GetNsDataIdList(Kernel::HLERequestContext& ctx) { @@ -194,16 +202,18 @@ void Module::Interface::GetNsDataIdList(Kernel::HLERequestContext& ctx) { const u32 start_ns_data_id = rp.Pop(); auto& buffer = rp.PopMappedBuffer(); + const u16 entries_count = online_service.GetNsDataIdList(filter, max_entries, buffer); + IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); rb.Push(RESULT_SUCCESS); - rb.Push(0); /// Actual number of output entries - rb.Push(0); /// Last word-index copied to output in the internal NsDataId list. + rb.Push(entries_count); /// Actual number of output entries + rb.Push(0); /// Last word-index copied to output in the internal NsDataId list. rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, - "(STUBBED) filter={:#010X}, max_entries={:#010X}, " - "word_index_start={:#06X}, start_ns_data_id={:#010X}", - filter, max_entries, word_index_start, start_ns_data_id); + LOG_DEBUG(Service_BOSS, + "filter={:#010x}, max_entries={:#010x}, " + "word_index_start={:#06x}, start_ns_data_id={:#010x}", + filter, max_entries, word_index_start, start_ns_data_id); } void Module::Interface::GetNsDataIdList1(Kernel::HLERequestContext& ctx) { @@ -214,16 +224,18 @@ void Module::Interface::GetNsDataIdList1(Kernel::HLERequestContext& ctx) { const u32 start_ns_data_id = rp.Pop(); auto& buffer = rp.PopMappedBuffer(); + const u16 entries_count = online_service.GetNsDataIdList(filter, max_entries, buffer); + IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); rb.Push(RESULT_SUCCESS); - rb.Push(0); /// Actual number of output entries - rb.Push(0); /// Last word-index copied to output in the internal NsDataId list. + rb.Push(entries_count); /// Actual number of output entries + rb.Push(0); /// Last word-index copied to output in the internal NsDataId list. rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, - "(STUBBED) filter={:#010X}, max_entries={:#010X}, " - "word_index_start={:#06X}, start_ns_data_id={:#010X}", - filter, max_entries, word_index_start, start_ns_data_id); + LOG_DEBUG(Service_BOSS, + "filter={:#010x}, max_entries={:#010x}, " + "word_index_start={:#06x}, start_ns_data_id={:#010x}", + filter, max_entries, word_index_start, start_ns_data_id); } void Module::Interface::GetNsDataIdList2(Kernel::HLERequestContext& ctx) { @@ -234,16 +246,18 @@ void Module::Interface::GetNsDataIdList2(Kernel::HLERequestContext& ctx) { const u32 start_ns_data_id = rp.Pop(); auto& buffer = rp.PopMappedBuffer(); + const u16 entries_count = online_service.GetNsDataIdList(filter, max_entries, buffer); + IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); rb.Push(RESULT_SUCCESS); - rb.Push(0); /// Actual number of output entries - rb.Push(0); /// Last word-index copied to output in the internal NsDataId list. + rb.Push(entries_count); /// Actual number of output entries + rb.Push(0); /// Last word-index copied to output in the internal NsDataId list. rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, - "(STUBBED) filter={:#010X}, max_entries={:#010X}, " - "word_index_start={:#06X}, start_ns_data_id={:#010X}", - filter, max_entries, word_index_start, start_ns_data_id); + LOG_DEBUG(Service_BOSS, + "filter={:#010x}, max_entries={:#010x}, " + "word_index_start={:#06x}, start_ns_data_id={:#010x}", + filter, max_entries, word_index_start, start_ns_data_id); } void Module::Interface::GetNsDataIdList3(Kernel::HLERequestContext& ctx) { @@ -254,16 +268,18 @@ void Module::Interface::GetNsDataIdList3(Kernel::HLERequestContext& ctx) { const u32 start_ns_data_id = rp.Pop(); auto& buffer = rp.PopMappedBuffer(); + const u16 entries_count = online_service.GetNsDataIdList(filter, max_entries, buffer); + IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); rb.Push(RESULT_SUCCESS); - rb.Push(0); /// Actual number of output entries - rb.Push(0); /// Last word-index copied to output in the internal NsDataId list. + rb.Push(entries_count); /// Actual number of output entries + rb.Push(0); /// Last word-index copied to output in the internal NsDataId list. rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, - "(STUBBED) filter={:#010X}, max_entries={:#010X}, " - "word_index_start={:#06X}, start_ns_data_id={:#010X}", - filter, max_entries, word_index_start, start_ns_data_id); + LOG_DEBUG(Service_BOSS, + "filter={:#010x}, max_entries={:#010x}, " + "word_index_start={:#06x}, start_ns_data_id={:#010x}", + filter, max_entries, word_index_start, start_ns_data_id); } void Module::Interface::SendProperty(Kernel::HLERequestContext& ctx) { @@ -272,11 +288,13 @@ void Module::Interface::SendProperty(Kernel::HLERequestContext& ctx) { const u32 size = rp.Pop(); auto& buffer = rp.PopMappedBuffer(); + const auto result = online_service.SendProperty(property_id, size, buffer); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); - rb.Push(RESULT_SUCCESS); + rb.Push(result); rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) property_id={:#06X}, size={:#010X}", property_id, size); + LOG_DEBUG(Service_BOSS, "property_id={:#06x}, size={:#010x}", property_id, size); } void Module::Interface::SendPropertyHandle(Kernel::HLERequestContext& ctx) { @@ -287,7 +305,7 @@ void Module::Interface::SendPropertyHandle(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_BOSS, "(STUBBED) property_id={:#06X}", property_id); + LOG_WARNING(Service_BOSS, "(STUBBED) property_id={:#06x}", property_id); } void Module::Interface::ReceiveProperty(Kernel::HLERequestContext& ctx) { @@ -296,12 +314,14 @@ void Module::Interface::ReceiveProperty(Kernel::HLERequestContext& ctx) { const u32 size = rp.Pop(); auto& buffer = rp.PopMappedBuffer(); + const auto result = online_service.ReceiveProperty(property_id, size, buffer); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); - rb.Push(RESULT_SUCCESS); - rb.Push(size); /// Should be actual read size + rb.Push(result); + rb.Push(size); rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) property_id={:#06X}, size={:#010X}", property_id, size); + LOG_DEBUG(Service_BOSS, "property_id={:#06x}, size={:#010x}", property_id, size); } void Module::Interface::UpdateTaskInterval(Kernel::HLERequestContext& ctx) { @@ -314,7 +334,7 @@ void Module::Interface::UpdateTaskInterval(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}, unk_param2={:#06X}", size, unk_param2); + LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010x}, unk_param2={:#06x}", size, unk_param2); } void Module::Interface::UpdateTaskCount(Kernel::HLERequestContext& ctx) { @@ -323,11 +343,14 @@ void Module::Interface::UpdateTaskCount(Kernel::HLERequestContext& ctx) { const u32 unk_param2 = rp.Pop(); auto& buffer = rp.PopMappedBuffer(); + std::string task_id(size, 0); + buffer.Read(task_id.data(), 0, size); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(RESULT_SUCCESS); rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}, unk_param2={:#010X}", size, unk_param2); + LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010x}, unk_param2={:#010x}", size, unk_param2); } void Module::Interface::GetTaskInterval(Kernel::HLERequestContext& ctx) { @@ -340,7 +363,7 @@ void Module::Interface::GetTaskInterval(Kernel::HLERequestContext& ctx) { rb.Push(0); // stub 0 ( 32bit value) rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}", size); + LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010x}", size); } void Module::Interface::GetTaskCount(Kernel::HLERequestContext& ctx) { @@ -348,12 +371,15 @@ void Module::Interface::GetTaskCount(Kernel::HLERequestContext& ctx) { const u32 size = rp.Pop(); auto& buffer = rp.PopMappedBuffer(); + std::string task_id(size, 0); + buffer.Read(task_id.data(), 0, size); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); rb.Push(RESULT_SUCCESS); rb.Push(0); // stub 0 ( 32bit value) rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}", size); + LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010x}", size); } void Module::Interface::GetTaskServiceStatus(Kernel::HLERequestContext& ctx) { @@ -361,12 +387,16 @@ void Module::Interface::GetTaskServiceStatus(Kernel::HLERequestContext& ctx) { const u32 size = rp.Pop(); auto& buffer = rp.PopMappedBuffer(); + // Not sure what this is but it's not the task status. Maybe it's the status of the service + // after running the task? + constexpr u8 task_service_status = 1; + IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); rb.Push(RESULT_SUCCESS); - rb.Push(0); // stub 0 ( 8bit value) + rb.Push(task_service_status); rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}", size); + LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010x}", size); } void Module::Interface::StartTask(Kernel::HLERequestContext& ctx) { @@ -378,7 +408,7 @@ void Module::Interface::StartTask(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}", size); + LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010x}", size); } void Module::Interface::StartTaskImmediate(Kernel::HLERequestContext& ctx) { @@ -390,7 +420,7 @@ void Module::Interface::StartTaskImmediate(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}", size); + LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010x}", size); } void Module::Interface::CancelTask(Kernel::HLERequestContext& ctx) { @@ -402,7 +432,7 @@ void Module::Interface::CancelTask(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}", size); + LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010x}", size); } void Module::Interface::GetTaskFinishHandle(Kernel::HLERequestContext& ctx) { @@ -428,7 +458,7 @@ void Module::Interface::GetTaskState(Kernel::HLERequestContext& ctx) { rb.Push(0); /// unknown, usually 0 rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}, state={:#06X}", size, state); + LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010x}, state={:#06x}", size, state); } void Module::Interface::GetTaskResult(Kernel::HLERequestContext& ctx) { @@ -443,7 +473,7 @@ void Module::Interface::GetTaskResult(Kernel::HLERequestContext& ctx) { rb.Push(0); // stub 0 (8 bit value) rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}", size); + LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010x}", size); } void Module::Interface::GetTaskCommErrorCode(Kernel::HLERequestContext& ctx) { @@ -458,7 +488,7 @@ void Module::Interface::GetTaskCommErrorCode(Kernel::HLERequestContext& ctx) { rb.Push(0); // stub 0 (8 bit value) rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}", size); + LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010x}", size); } void Module::Interface::GetTaskStatus(Kernel::HLERequestContext& ctx) { @@ -473,7 +503,7 @@ void Module::Interface::GetTaskStatus(Kernel::HLERequestContext& ctx) { rb.Push(0); // stub 0 (8 bit value) rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}, unk_param2={:#04X}, unk_param3={:#04X}", + LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010x}, unk_param2={:#04x}, unk_param3={:#04x}", size, unk_param2, unk_param3); } @@ -488,7 +518,7 @@ void Module::Interface::GetTaskError(Kernel::HLERequestContext& ctx) { rb.Push(0); // stub 0 (8 bit value) rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}, unk_param2={:#04X}", size, unk_param2); + LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010x}, unk_param2={:#04x}", size, unk_param2); } void Module::Interface::GetTaskInfo(Kernel::HLERequestContext& ctx) { @@ -501,7 +531,7 @@ void Module::Interface::GetTaskInfo(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}, unk_param2={:#04X}", size, unk_param2); + LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010x}, unk_param2={:#04x}", size, unk_param2); } void Module::Interface::DeleteNsData(Kernel::HLERequestContext& ctx) { @@ -511,7 +541,7 @@ void Module::Interface::DeleteNsData(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_BOSS, "(STUBBED) ns_data_id={:#010X}", ns_data_id); + LOG_WARNING(Service_BOSS, "(STUBBED) ns_data_id={:#010x}", ns_data_id); } void Module::Interface::GetNsDataHeaderInfo(Kernel::HLERequestContext& ctx) { @@ -525,7 +555,7 @@ void Module::Interface::GetNsDataHeaderInfo(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) ns_data_id={:#010X}, type={:#04X}, size={:#010X}", + LOG_WARNING(Service_BOSS, "(STUBBED) ns_data_id={:#010x}, type={:#04x}, size={:#010x}", ns_data_id, type, size); } @@ -542,7 +572,7 @@ void Module::Interface::ReadNsData(Kernel::HLERequestContext& ctx) { rb.Push(0); /// unknown rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) ns_data_id={:#010X}, offset={:#018X}, size={:#010X}", + LOG_WARNING(Service_BOSS, "(STUBBED) ns_data_id={:#010x}, offset={:#018x}, size={:#010x}", ns_data_id, offset, size); } @@ -554,7 +584,7 @@ void Module::Interface::SetNsDataAdditionalInfo(Kernel::HLERequestContext& ctx) IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1={:#010X}, unk_param2={:#010X}", unk_param1, + LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1={:#010x}, unk_param2={:#010x}", unk_param1, unk_param2); } @@ -566,7 +596,7 @@ void Module::Interface::GetNsDataAdditionalInfo(Kernel::HLERequestContext& ctx) rb.Push(RESULT_SUCCESS); rb.Push(0); // stub 0 (32bit value) - LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1={:#010X}", unk_param1); + LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1={:#010x}", unk_param1); } void Module::Interface::SetNsDataNewFlag(Kernel::HLERequestContext& ctx) { @@ -577,7 +607,7 @@ void Module::Interface::SetNsDataNewFlag(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1={:#010X}, ns_data_new_flag={:#04X}", unk_param1, + LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1={:#010x}, ns_data_new_flag={:#04x}", unk_param1, ns_data_new_flag); } @@ -589,7 +619,7 @@ void Module::Interface::GetNsDataNewFlag(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.Push(ns_data_new_flag); - LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1={:#010X}, ns_data_new_flag={:#04X}", unk_param1, + LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1={:#010x}, ns_data_new_flag={:#04x}", unk_param1, ns_data_new_flag); } @@ -602,7 +632,7 @@ void Module::Interface::GetNsDataLastUpdate(Kernel::HLERequestContext& ctx) { rb.Push(0); // stub 0 (32bit value) rb.Push(0); // stub 0 (32bit value) - LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1={:#010X}", unk_param1); + LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1={:#010x}", unk_param1); } void Module::Interface::GetErrorCode(Kernel::HLERequestContext& ctx) { @@ -613,7 +643,7 @@ void Module::Interface::GetErrorCode(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.Push(0); /// output value - LOG_WARNING(Service_BOSS, "(STUBBED) input={:#010X}", input); + LOG_WARNING(Service_BOSS, "(STUBBED) input={:#010x}", input); } void Module::Interface::RegisterStorageEntry(Kernel::HLERequestContext& ctx) { @@ -628,8 +658,8 @@ void Module::Interface::RegisterStorageEntry(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_BOSS, - "(STUBBED) unk_param1={:#010X}, unk_param2={:#010X}, unk_param3={:#010X}, " - "unk_param4={:#010X}, unk_param5={:#04X}", + "(STUBBED) unk_param1={:#010x}, unk_param2={:#010x}, unk_param3={:#010x}, " + "unk_param4={:#010x}, unk_param5={:#04x}", unk_param1, unk_param2, unk_param3, unk_param4, unk_param5); } @@ -655,8 +685,8 @@ void Module::Interface::SetStorageOption(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_BOSS, - "(STUBBED) unk_param1={:#04X}, unk_param2={:#010X}, " - "unk_param3={:#08X}, unk_param4={:#08X}", + "(STUBBED) unk_param1={:#04x}, unk_param2={:#010x}, " + "unk_param3={:#08x}, unk_param4={:#08x}", unk_param1, unk_param2, unk_param3, unk_param4); } @@ -682,7 +712,7 @@ void Module::Interface::StartBgImmediate(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}", size); + LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010x}", size); } void Module::Interface::GetTaskProperty0(Kernel::HLERequestContext& ctx) { @@ -695,7 +725,7 @@ void Module::Interface::GetTaskProperty0(Kernel::HLERequestContext& ctx) { rb.Push(0); /// current state of PropertyID 0x0 stub 0 (8bit value) rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}", size); + LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010x}", size); } void Module::Interface::RegisterImmediateTask(Kernel::HLERequestContext& ctx) { @@ -709,7 +739,7 @@ void Module::Interface::RegisterImmediateTask(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}, unk_param2={:#04X}, unk_param3={:#04X}", + LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010x}, unk_param2={:#04x}, unk_param3={:#04x}", size, unk_param2, unk_param3); } @@ -725,7 +755,7 @@ void Module::Interface::SetTaskQuery(Kernel::HLERequestContext& ctx) { rb.PushMappedBuffer(buffer1); rb.PushMappedBuffer(buffer2); - LOG_WARNING(Service_BOSS, "(STUBBED) buffer1_size={:#010X}, buffer2_size={:#010X}", + LOG_WARNING(Service_BOSS, "(STUBBED) buffer1_size={:#010x}, buffer2_size={:#010x}", buffer1_size, buffer2_size); } @@ -741,7 +771,7 @@ void Module::Interface::GetTaskQuery(Kernel::HLERequestContext& ctx) { rb.PushMappedBuffer(buffer1); rb.PushMappedBuffer(buffer2); - LOG_WARNING(Service_BOSS, "(STUBBED) buffer1_size={:#010X}, buffer2_size={:#010X}", + LOG_WARNING(Service_BOSS, "(STUBBED) buffer1_size={:#010x}, buffer2_size={:#010x}", buffer1_size, buffer2_size); } @@ -753,7 +783,7 @@ void Module::Interface::InitializeSessionPrivileged(Kernel::HLERequestContext& c IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_BOSS, "(STUBBED) programID={:#018X}", programID); + LOG_WARNING(Service_BOSS, "(STUBBED) programID={:#018x}", programID); } void Module::Interface::GetAppNewFlag(Kernel::HLERequestContext& ctx) { @@ -764,7 +794,7 @@ void Module::Interface::GetAppNewFlag(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.Push(0); // 0 = nothing new, 1 = new content - LOG_WARNING(Service_BOSS, "(STUBBED) programID={:#018X}", programID); + LOG_WARNING(Service_BOSS, "(STUBBED) programID={:#018x}", programID); } void Module::Interface::GetNsDataIdListPrivileged(Kernel::HLERequestContext& ctx) { @@ -783,8 +813,8 @@ void Module::Interface::GetNsDataIdListPrivileged(Kernel::HLERequestContext& ctx rb.PushMappedBuffer(buffer); LOG_WARNING(Service_BOSS, - "(STUBBED) programID={:#018X}, filter={:#010X}, max_entries={:#010X}, " - "word_index_start={:#06X}, start_ns_data_id={:#010X}", + "(STUBBED) programID={:#018x}, filter={:#010x}, max_entries={:#010x}, " + "word_index_start={:#06x}, start_ns_data_id={:#010x}", programID, filter, max_entries, word_index_start, start_ns_data_id); } @@ -804,8 +834,8 @@ void Module::Interface::GetNsDataIdListPrivileged1(Kernel::HLERequestContext& ct rb.PushMappedBuffer(buffer); LOG_WARNING(Service_BOSS, - "(STUBBED) programID={:#018X}, filter={:#010X}, max_entries={:#010X}, " - "word_index_start={:#06X}, start_ns_data_id={:#010X}", + "(STUBBED) programID={:#018x}, filter={:#010x}, max_entries={:#010x}, " + "word_index_start={:#06x}, start_ns_data_id={:#010x}", programID, filter, max_entries, word_index_start, start_ns_data_id); } @@ -819,7 +849,7 @@ void Module::Interface::SendPropertyPrivileged(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); rb.PushMappedBuffer(buffer); - LOG_WARNING(Service_BOSS, "(STUBBED) property_id={:#06X}, size={:#010X}", property_id, size); + LOG_WARNING(Service_BOSS, "(STUBBED) property_id={:#06x}, size={:#010x}", property_id, size); } void Module::Interface::DeleteNsDataPrivileged(Kernel::HLERequestContext& ctx) { @@ -830,7 +860,7 @@ void Module::Interface::DeleteNsDataPrivileged(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_BOSS, "(STUBBED) programID={:#018X}, ns_data_id={:#010X}", programID, + LOG_WARNING(Service_BOSS, "(STUBBED) programID={:#018x}, ns_data_id={:#010x}", programID, ns_data_id); } @@ -847,7 +877,7 @@ void Module::Interface::GetNsDataHeaderInfoPrivileged(Kernel::HLERequestContext& rb.PushMappedBuffer(buffer); LOG_WARNING(Service_BOSS, - "(STUBBED) programID={:#018X} ns_data_id={:#010X}, type={:#04X}, size={:#010X}", + "(STUBBED) programID={:#018x} ns_data_id={:#010x}, type={:#04x}, size={:#010x}", programID, ns_data_id, type, size); } @@ -866,7 +896,7 @@ void Module::Interface::ReadNsDataPrivileged(Kernel::HLERequestContext& ctx) { rb.PushMappedBuffer(buffer); LOG_WARNING(Service_BOSS, - "(STUBBED) programID={:#018X}, ns_data_id={:#010X}, offset={:#018X}, size={:#010X}", + "(STUBBED) programID={:#018x}, ns_data_id={:#010x}, offset={:#018x}, size={:#010x}", programID, ns_data_id, offset, size); } @@ -881,7 +911,7 @@ void Module::Interface::SetNsDataNewFlagPrivileged(Kernel::HLERequestContext& ct LOG_WARNING( Service_BOSS, - "(STUBBED) programID={:#018X}, unk_param1={:#010X}, ns_data_new_flag_privileged={:#04X}", + "(STUBBED) programID={:#018x}, unk_param1={:#010x}, ns_data_new_flag_privileged={:#04x}", programID, unk_param1, ns_data_new_flag_privileged); } @@ -896,14 +926,14 @@ void Module::Interface::GetNsDataNewFlagPrivileged(Kernel::HLERequestContext& ct LOG_WARNING( Service_BOSS, - "(STUBBED) programID={:#018X}, unk_param1={:#010X}, ns_data_new_flag_privileged={:#04X}", + "(STUBBED) programID={:#018x}, unk_param1={:#010x}, ns_data_new_flag_privileged={:#04x}", programID, unk_param1, ns_data_new_flag_privileged); } Module::Interface::Interface(std::shared_ptr boss, const char* name, u32 max_session) - : ServiceFramework(name, max_session), boss(std::move(boss)) {} + : ServiceFramework(name, max_session), online_service(boss->system), boss(std::move(boss)) {} -Module::Module(Core::System& system) { +Module::Module(Core::System& system_) : system(system_) { using namespace Kernel; // TODO: verify ResetType task_finish_event = diff --git a/src/core/hle/service/boss/boss.h b/src/core/hle/service/boss/boss.h index e4000e851..7dcba90d6 100644 --- a/src/core/hle/service/boss/boss.h +++ b/src/core/hle/service/boss/boss.h @@ -8,6 +8,7 @@ #include #include "core/global.h" #include "core/hle/kernel/event.h" +#include "core/hle/service/boss/online_service.h" #include "core/hle/service/service.h" namespace Core { @@ -18,7 +19,7 @@ namespace Service::BOSS { class Module final { public: - explicit Module(Core::System& system); + explicit Module(Core::System& system_); ~Module() = default; class Interface : public ServiceFramework { @@ -955,6 +956,7 @@ public: void GetNsDataNewFlagPrivileged(Kernel::HLERequestContext& ctx); protected: + OnlineService online_service; std::shared_ptr boss; private: @@ -974,6 +976,7 @@ public: }; private: + Core::System& system; std::shared_ptr task_finish_event; template diff --git a/src/core/hle/service/boss/online_service.cpp b/src/core/hle/service/boss/online_service.cpp new file mode 100644 index 000000000..56f33460d --- /dev/null +++ b/src/core/hle/service/boss/online_service.cpp @@ -0,0 +1,384 @@ +// Copyright 2023 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/file_util.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/file_sys/archive_backend.h" +#include "core/file_sys/archive_extsavedata.h" +#include "core/file_sys/archive_systemsavedata.h" +#include "core/file_sys/directory_backend.h" +#include "core/file_sys/errors.h" +#include "core/file_sys/file_backend.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/boss/online_service.h" + +namespace Service::BOSS { + +OnlineService::OnlineService(Core::System& system_) : system{system_} { + system.GetAppLoader().ReadProgramId(program_id); + system.GetAppLoader().ReadExtdataId(extdata_id); +}; + +ResultCode OnlineService::InitializeSession(u64 init_program_id) { + // The BOSS service uses three databases: + // BOSS_A: Archive? A list of program ids and some properties that are keyed on program + // BOSS_SS: Saved Strings? Includes the url and the other string properties, and also some other + // properties?, keyed on task_id + // BOSS_SV: Saved Values? Includes task id and most properties keyed on task_id + + // It saves data in its databases in the following format: + // A four byte header (always 00 80 34 12?) followed by any number of 0x800(BOSS_A) and + // 0xC00(BOSS_SS and BOSS_SV) entries. + + current_props = BossTaskProperties(); + + if (init_program_id == 0) { + init_program_id = program_id; + } + + const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); + FileSys::ArchiveFactory_SystemSaveData systemsavedata_factory(nand_directory); + + // Open the SystemSaveData archive 0x00010034 + const FileSys::Path archive_path(boss_system_savedata_id); + auto archive_result = systemsavedata_factory.Open(archive_path, 0); + + std::unique_ptr boss_system_save_data_archive; + if (archive_result.Succeeded()) { + boss_system_save_data_archive = std::move(archive_result).Unwrap(); + } else if (archive_result.Code() == FileSys::ERROR_NOT_FOUND) { + // If the archive didn't exist, create the files inside + systemsavedata_factory.Format(archive_path, FileSys::ArchiveFormatInfo(), 0); + + // Open it again to get a valid archive now that the folder exists + auto create_archive_result = systemsavedata_factory.Open(archive_path, 0); + if (!create_archive_result.Succeeded()) { + LOG_ERROR(Service_BOSS, "Could not open BOSS savedata"); + return ResultCode(1); + } + boss_system_save_data_archive = std::move(create_archive_result).Unwrap(); + } else { + LOG_ERROR(Service_BOSS, "Could not open BOSS savedata"); + return ResultCode(1); + } + + FileSys::Mode open_mode = {}; + open_mode.read_flag.Assign(1); + + // TODO: Read data from BOSS_A.db + + auto boss_sv_result = + boss_system_save_data_archive->OpenFile(FileSys::Path("/BOSS_SV.db"), open_mode); + auto boss_ss_result = + boss_system_save_data_archive->OpenFile(FileSys::Path("/BOSS_SS.db"), open_mode); + if (!boss_sv_result.Succeeded() || !boss_ss_result.Succeeded()) { + LOG_ERROR(Service_BOSS, "Could not open BOSS database."); + return RESULT_SUCCESS; + } + + auto boss_sv = std::move(boss_sv_result).Unwrap(); + auto boss_ss = std::move(boss_ss_result).Unwrap(); + if (!(boss_sv->GetSize() > BOSS_SAVE_HEADER_SIZE && + ((boss_sv->GetSize() - BOSS_SAVE_HEADER_SIZE) % BOSS_S_ENTRY_SIZE) == 0 && + boss_sv->GetSize() == boss_ss->GetSize())) { + LOG_ERROR(Service_BOSS, "BOSS database has incorrect size."); + return RESULT_SUCCESS; + } + + // Read the files if they already exist + const u64 num_entries = (boss_sv->GetSize() - BOSS_SAVE_HEADER_SIZE) / BOSS_S_ENTRY_SIZE; + for (u64 i = 0; i < num_entries; i++) { + const u64 entry_offset = i * BOSS_S_ENTRY_SIZE + BOSS_SAVE_HEADER_SIZE; + + BossSVData sv_data; + boss_sv->Read(entry_offset, sizeof(BossSVData), reinterpret_cast(&sv_data)); + + BossSSData ss_data; + boss_ss->Read(entry_offset, sizeof(BossSSData), reinterpret_cast(&ss_data)); + + if (sv_data.program_id != init_program_id) { + continue; + } + + std::vector url_vector(URL_SIZE); + std::memcpy(url_vector.data(), ss_data.url.data(), URL_SIZE); + current_props.properties[PropertyID::Url] = url_vector; + + const auto task_id = std::string(sv_data.task_id.data()); + if (task_id_list.contains(task_id)) { + LOG_WARNING(Service_BOSS, "TaskId already in list, will be replaced"); + task_id_list.erase(task_id); + } + + task_id_list.emplace(task_id, std::move(current_props)); + current_props = BossTaskProperties(); + } + + return RESULT_SUCCESS; +} + +void OnlineService::RegisterTask(const u32 size, Kernel::MappedBuffer& buffer) { + std::string task_id(size, 0); + buffer.Read(task_id.data(), 0, size); + + if (task_id_list.contains(task_id)) { + LOG_WARNING(Service_BOSS, "TaskId already in list, will be replaced"); + task_id_list.erase(task_id); + } + task_id_list.emplace(task_id, std::move(current_props)); + current_props = BossTaskProperties(); +} + +ResultCode OnlineService::UnregisterTask(const u32 size, Kernel::MappedBuffer& buffer) { + if (size > TASK_ID_SIZE) { + LOG_WARNING(Service_BOSS, "TaskId cannot be longer than 8"); + return ResultCode(1); + } + + std::string task_id(size, 0); + buffer.Read(task_id.data(), 0, size); + if (task_id_list.erase(task_id) == 0) { + LOG_WARNING(Service_BOSS, "TaskId not in list"); + return ResultCode(1); + } + + return RESULT_SUCCESS; +} + +void OnlineService::GetTaskIdList() { + current_props.properties[PropertyID::TotalTasks] = static_cast(task_id_list.size()); + + const auto num_task_ids = TASKIDLIST_SIZE / TASK_ID_SIZE; + std::vector> task_ids(num_task_ids); + + u16 num_returned_task_ids = 0; + for (const auto& iter : task_id_list) { + const std::string current_task_id = iter.first; + if (current_task_id.size() > TASK_ID_SIZE || num_returned_task_ids >= num_task_ids) { + LOG_WARNING(Service_BOSS, "TaskId {} too long or too many TaskIds", current_task_id); + continue; + } + std::memcpy(task_ids[num_returned_task_ids++].data(), current_task_id.data(), TASK_ID_SIZE); + } + + auto* task_list_prop = + std::get_if>(¤t_props.properties[PropertyID::TaskIdList]); + if (task_list_prop && task_list_prop->size() == TASKIDLIST_SIZE) { + std::memcpy(task_list_prop->data(), task_ids.data(), TASKIDLIST_SIZE); + } +} + +std::vector OnlineService::GetBossExtDataFiles() { + FileSys::ArchiveFactory_ExtSaveData boss_extdata_archive_factory( + FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), false, true); + const FileSys::Path boss_path{GetBossDataDir()}; + auto archive_result = boss_extdata_archive_factory.Open(boss_path, 0); + if (!archive_result.Succeeded()) { + LOG_WARNING(Service_BOSS, "Extdata opening failed"); + return {}; + } + + auto boss_archive = std::move(archive_result).Unwrap(); + auto dir_result = boss_archive->OpenDirectory("/"); + if (!dir_result.Succeeded()) { + LOG_WARNING(Service_BOSS, "Extdata directory opening failed"); + return {}; + } + auto dir = std::move(dir_result).Unwrap(); + + // Keep reading the directory 32 files at a time until all files have been checked + std::vector boss_files; + constexpr u32 files_to_read = 32; + u32 entry_count = 0; + std::size_t i = 0; + do { + boss_files.resize(boss_files.size() + files_to_read); + entry_count = dir->Read(files_to_read, boss_files.data() + (i++ * files_to_read)); + } while (files_to_read <= entry_count); + + boss_files.resize(i * files_to_read + entry_count); + return boss_files; +} + +FileSys::Path OnlineService::GetBossDataDir() { + const u32 high = static_cast(extdata_id >> 32); + const u32 low = static_cast(extdata_id & 0xFFFFFFFF); + return FileSys::ConstructExtDataBinaryPath(1, high, low); +} + +std::vector OnlineService::GetNsDataEntries() { + FileSys::ArchiveFactory_ExtSaveData boss_extdata_archive_factory( + FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), false, true); + const FileSys::Path boss_path{GetBossDataDir()}; + auto archive_result = boss_extdata_archive_factory.Open(boss_path, 0); + if (!archive_result.Succeeded()) { + LOG_WARNING(Service_BOSS, "Extdata opening failed"); + return {}; + } + auto boss_archive = std::move(archive_result).Unwrap().get(); + + std::vector ns_data; + std::vector boss_files = GetBossExtDataFiles(); + for (const auto& current_file : boss_files) { + constexpr u32 boss_header_length = 0x34; + if (current_file.is_directory || current_file.file_size < boss_header_length) { + LOG_WARNING(Service_BOSS, "Spotpass extdata contains directory or file is too short"); + continue; + } + + FileSys::Mode mode{}; + mode.read_flag.Assign(1); + + NsDataEntry entry{.filename = Common::UTF16ToUTF8(current_file.filename)}; + auto file_result = boss_archive->OpenFile("/" + entry.filename, mode); + if (!file_result.Succeeded()) { + LOG_WARNING(Service_BOSS, "Opening Spotpass file failed."); + continue; + } + + auto file = std::move(file_result).Unwrap(); + file->Read(0, boss_header_length, reinterpret_cast(&entry.header)); + if (entry.header.header_length != BOSS_EXTDATA_HEADER_LENGTH) { + LOG_WARNING( + Service_BOSS, + "Incorrect header length or non-spotpass file; expected {:#010x}, found {:#010x}", + BOSS_EXTDATA_HEADER_LENGTH, entry.header.header_length); + continue; + } + + if (entry.header.program_id != program_id) { + LOG_WARNING(Service_BOSS, + "Mismatched program ID in spotpass data. Was expecting " + "{:#018x}, found {:#018x}", + program_id, static_cast(entry.header.program_id)); + continue; + } + + // Check the payload size is correct, excluding header + if (entry.header.payload_size != (current_file.file_size - boss_header_length)) { + LOG_WARNING(Service_BOSS, + "Mismatched file size, was expecting {:#010x}, found {:#010x}", + static_cast(entry.header.payload_size), + current_file.file_size - boss_header_length); + continue; + } + + ns_data.push_back(entry); + } + + return ns_data; +} + +u16 OnlineService::GetNsDataIdList(const u32 filter, const u32 max_entries, + Kernel::MappedBuffer& buffer) { + std::vector ns_data = GetNsDataEntries(); + std::vector output_entries; + for (const auto& current_entry : ns_data) { + const u16 datatype_high = + static_cast(static_cast(current_entry.header.datatype) >> 16); + const u16 datatype_low = + static_cast((static_cast(current_entry.header.datatype) & 0xFFFF)); + const u16 filter_high = static_cast(filter >> 16); + const u16 filter_low = static_cast(filter & 0xFFFF); + if (filter != 0xFFFFFFFF && + (filter_high != datatype_high || (filter_low & datatype_low) == 0)) { + continue; + } + + if (output_entries.size() >= max_entries) { + LOG_WARNING(Service_BOSS, "Reached maximum number of entries"); + break; + } + + output_entries.push_back(current_entry.header.ns_data_id); + } + + buffer.Write(output_entries.data(), 0, sizeof(u32) * output_entries.size()); + return static_cast(output_entries.size()); +} + +template +struct overload : Ts... { + using Ts::operator()...; +}; +template +overload(Ts...) -> overload; + +ResultCode OnlineService::SendProperty(const u16 id, const u32 size, Kernel::MappedBuffer& buffer) { + const auto property_id = static_cast(id); + if (!current_props.properties.contains(property_id)) { + LOG_ERROR(Service_BOSS, "Unknown property with id {:#06x}", property_id); + return ResultCode(1); + } + + auto& prop = current_props.properties[property_id]; + auto read_pod = [&](T& old_prop) { + static_assert(std::is_trivial::value, "Only trivial types are allowed for read_pod"); + if (size != sizeof(old_prop)) { + LOG_ERROR(Service_BOSS, "Unexpected size of property {:#06x}, was expecting {}, got {}", + property_id, sizeof(old_prop), size); + } + T cur_prop = 0; + buffer.Read(&cur_prop, 0, size); + prop = cur_prop; + }; + + auto read_vector = [&](std::vector& old_prop) { + if (size != old_prop.size()) { + LOG_ERROR(Service_BOSS, "Unexpected size of property {:#06x}, was expecting {}, got {}", + property_id, old_prop.size(), size); + } + std::vector cur_prop(size); + buffer.Read(cur_prop.data(), 0, size); + prop = cur_prop; + }; + + std::visit(overload{[&](u8& cur_prop) { read_pod(cur_prop); }, + [&](u16& cur_prop) { read_pod(cur_prop); }, + [&](u32& cur_prop) { read_pod(cur_prop); }, + [&](std::vector& cur_prop) { read_vector(cur_prop); }, + [&](std::vector& cur_prop) { read_vector(cur_prop); }}, + prop); + + return RESULT_SUCCESS; +} + +ResultCode OnlineService::ReceiveProperty(const u16 id, const u32 size, + Kernel::MappedBuffer& buffer) { + const auto property_id = static_cast(id); + if (!current_props.properties.contains(property_id)) { + LOG_ERROR(Service_BOSS, "Unknown property with id {:#06x}", property_id); + return ResultCode(1); + } + + auto write_pod = [&](T& cur_prop) { + static_assert(std::is_trivial::value, "Only trivial types are allowed for write_pod"); + if (size != sizeof(cur_prop)) { + LOG_ERROR(Service_BOSS, "Unexpected size of property {:#06x}, was expecting {}, got {}", + property_id, sizeof(cur_prop), size); + } + buffer.Write(&cur_prop, 0, size); + }; + + auto write_vector = [&](std::vector& cur_prop) { + if (size != cur_prop.size()) { + LOG_ERROR(Service_BOSS, "Unexpected size of property {:#06x}, was expecting {}, got {}", + property_id, cur_prop.size(), size); + } + buffer.Write(cur_prop.data(), 0, size); + }; + + auto& prop = current_props.properties[property_id]; + std::visit(overload{[&](u8& cur_prop) { write_pod(cur_prop); }, + [&](u16& cur_prop) { write_pod(cur_prop); }, + [&](u32& cur_prop) { write_pod(cur_prop); }, + [&](std::vector& cur_prop) { write_vector(cur_prop); }, + [&](std::vector& cur_prop) { write_vector(cur_prop); }}, + prop); + + return RESULT_SUCCESS; +} + +} // namespace Service::BOSS diff --git a/src/core/hle/service/boss/online_service.h b/src/core/hle/service/boss/online_service.h new file mode 100644 index 000000000..a84cd1830 --- /dev/null +++ b/src/core/hle/service/boss/online_service.h @@ -0,0 +1,159 @@ +// 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 { +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 size_t TASK_ID_SIZE = 8; +constexpr size_t URL_SIZE = 0x200; +constexpr size_t HEADERS_SIZE = 0x360; +constexpr size_t CERTIDLIST_SIZE = 3; +constexpr 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 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()}, + {PropertyID::TotalTasks, u16()}, + {PropertyID::TaskIdList, std::vector(TASKIDLIST_SIZE)}, + {static_cast(0x3B), u32()}, + {static_cast(0x3E), std::vector(0x200)}, + {static_cast(0x3F), u8()}, + }; +}; + +class OnlineService final { +public: + explicit OnlineService(Core::System& system_); + + ResultCode InitializeSession(u64 init_program_id); + void RegisterTask(const u32 size, Kernel::MappedBuffer& buffer); + ResultCode UnregisterTask(const u32 size, Kernel::MappedBuffer& buffer); + void GetTaskIdList(); + u16 GetNsDataIdList(const u32 filter, const u32 max_entries, Kernel::MappedBuffer& buffer); + ResultCode SendProperty(const u16 id, const u32 size, Kernel::MappedBuffer& buffer); + ResultCode ReceiveProperty(const u16 id, const u32 size, Kernel::MappedBuffer& buffer); + +private: + std::vector GetBossExtDataFiles(); + FileSys::Path GetBossDataDir(); + std::vector GetNsDataEntries(); + + BossTaskProperties current_props; + std::map task_id_list; + + Core::System& system; + u64 program_id; + u64 extdata_id; +}; + +} // namespace Service::BOSS diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index ba98ce045..6e963e0d7 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -365,6 +365,10 @@ void ArchiveManager::RegisterArchiveTypes() { std::make_unique(nand_directory); RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData); + auto bossextsavedata_factory = + std::make_unique(sdmc_directory, false, true); + RegisterArchiveType(std::move(bossextsavedata_factory), ArchiveIdCode::BossExtSaveData); + auto selfncch_factory = std::make_unique(); RegisterArchiveType(std::move(selfncch_factory), ArchiveIdCode::SelfNCCH); } diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index f4604803b..0f901a05b 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -40,6 +40,7 @@ enum class ArchiveIdCode : u32 { SystemSaveData = 0x00000008, SDMC = 0x00000009, SDMCWriteOnly = 0x0000000A, + BossExtSaveData = 0x12345678, NCCH = 0x2345678A, OtherSaveDataGeneral = 0x567890B2, OtherSaveDataPermitted = 0x567890B4,