mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	boss: Implement Spotpass service (part 1) (#7232)
* boss: Implement Spotpass service (part 1) * boss: Fix save state (de)serialization. * boss: Fix casing of SpotPass in log messages. * boss: Minor logging improvements. * common: Add boost serialization support for std::variant. --------- Co-authored-by: Rokkubro <lachlanb03@gmail.com> Co-authored-by: FearlessTobi <thm.frey@gmail.com>
This commit is contained in:
		
							parent
							
								
									4d9eedd0d8
								
							
						
					
					
						commit
						24b5ffbfca
					
				
					 7 changed files with 1018 additions and 129 deletions
				
			
		|  | @ -234,6 +234,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 | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "common/archives.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/core.h" | ||||
| #include "core/hle/ipc_helpers.h" | ||||
|  | @ -10,17 +11,66 @@ | |||
| #include "core/hle/service/boss/boss_p.h" | ||||
| #include "core/hle/service/boss/boss_u.h" | ||||
| 
 | ||||
| SERVICE_CONSTRUCT_IMPL(Service::BOSS::Module) | ||||
| SERIALIZE_EXPORT_IMPL(Service::BOSS::Module) | ||||
| SERIALIZE_EXPORT_IMPL(Service::BOSS::Module::SessionData) | ||||
| 
 | ||||
| namespace Service::BOSS { | ||||
| 
 | ||||
| template <class Archive> | ||||
| void Module::serialize(Archive& ar, const unsigned int) { | ||||
|     ar& task_finish_event; | ||||
|     ar& new_arrival_flag; | ||||
|     ar& ns_data_new_flag; | ||||
|     ar& ns_data_new_flag_privileged; | ||||
|     ar& output_flag; | ||||
| } | ||||
| SERIALIZE_IMPL(Module) | ||||
| 
 | ||||
| template <class Archive> | ||||
| void Module::SessionData::serialize(Archive& ar, const unsigned int) { | ||||
|     ar& boost::serialization::base_object<Kernel::SessionRequestHandler::SessionDataBase>(*this); | ||||
|     ar& online_service; | ||||
| } | ||||
| SERIALIZE_IMPL(Module::SessionData) | ||||
| 
 | ||||
| std::shared_ptr<OnlineService> Module::Interface::GetSessionService( | ||||
|     Kernel::HLERequestContext& ctx) { | ||||
|     const auto session_data = GetSessionData(ctx.Session()); | ||||
|     if (session_data == nullptr || session_data->online_service == nullptr) { | ||||
|         LOG_WARNING(Service_BOSS, "Client attempted to use uninitialized BOSS session."); | ||||
| 
 | ||||
|         // TODO: Error code for uninitialized session.
 | ||||
|         IPC::RequestParser rp(ctx); | ||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|         rb.Push(RESULT_UNKNOWN); | ||||
|         return nullptr; | ||||
|     } | ||||
|     return session_data->online_service; | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::InitializeSession(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx); | ||||
|     const u64 programID = rp.Pop<u64>(); | ||||
|     const u64 program_id = rp.Pop<u64>(); | ||||
|     rp.PopPID(); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     const auto session_data = GetSessionData(ctx.Session()); | ||||
|     if (session_data->online_service == nullptr) { | ||||
|         u64 curr_program_id; | ||||
|         u64 curr_extdata_id; | ||||
|         boss->system.GetAppLoader().ReadProgramId(curr_program_id); | ||||
|         boss->system.GetAppLoader().ReadExtdataId(curr_extdata_id); | ||||
| 
 | ||||
|     LOG_WARNING(Service_BOSS, "(STUBBED) programID={:#018X}", programID); | ||||
|         session_data->online_service = | ||||
|             std::make_shared<OnlineService>(curr_program_id, curr_extdata_id); | ||||
|     } | ||||
| 
 | ||||
|     const auto result = session_data->online_service->InitializeSession(program_id); | ||||
| 
 | ||||
|     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 +83,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 +130,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); | ||||
| } | ||||
| 
 | ||||
|  | @ -89,9 +139,9 @@ void Module::Interface::GetNewArrivalFlag(Kernel::HLERequestContext& ctx) { | |||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push<u8>(new_arrival_flag); | ||||
|     rb.Push<u8>(boss->new_arrival_flag); | ||||
| 
 | ||||
|     LOG_WARNING(Service_BOSS, "(STUBBED) new_arrival_flag={}", new_arrival_flag); | ||||
|     LOG_WARNING(Service_BOSS, "(STUBBED) new_arrival_flag={}", boss->new_arrival_flag); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::RegisterNewArrivalEvent(Kernel::HLERequestContext& ctx) { | ||||
|  | @ -106,12 +156,12 @@ void Module::Interface::RegisterNewArrivalEvent(Kernel::HLERequestContext& ctx) | |||
| 
 | ||||
| void Module::Interface::SetOptoutFlag(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx); | ||||
|     output_flag = rp.Pop<u8>(); | ||||
|     boss->output_flag = rp.Pop<u8>(); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_WARNING(Service_BOSS, "output_flag={}", output_flag); | ||||
|     LOG_WARNING(Service_BOSS, "output_flag={}", boss->output_flag); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::GetOptoutFlag(Kernel::HLERequestContext& ctx) { | ||||
|  | @ -119,9 +169,9 @@ void Module::Interface::GetOptoutFlag(Kernel::HLERequestContext& ctx) { | |||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push<u8>(output_flag); | ||||
|     rb.Push<u8>(boss->output_flag); | ||||
| 
 | ||||
|     LOG_WARNING(Service_BOSS, "output_flag={}", output_flag); | ||||
|     LOG_WARNING(Service_BOSS, "output_flag={}", boss->output_flag); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::RegisterTask(Kernel::HLERequestContext& ctx) { | ||||
|  | @ -131,12 +181,18 @@ void Module::Interface::RegisterTask(Kernel::HLERequestContext& ctx) { | |||
|     const u8 unk_param3 = rp.Pop<u8>(); | ||||
|     auto& buffer = rp.PopMappedBuffer(); | ||||
| 
 | ||||
|     const auto online_service = GetSessionService(ctx); | ||||
|     if (online_service == nullptr) { | ||||
|         return; | ||||
|     } | ||||
|     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 +201,17 @@ void Module::Interface::UnregisterTask(Kernel::HLERequestContext& ctx) { | |||
|     const u8 unk_param2 = rp.Pop<u8>(); | ||||
|     auto& buffer = rp.PopMappedBuffer(); | ||||
| 
 | ||||
|     const auto online_service = GetSessionService(ctx); | ||||
|     if (online_service == nullptr) { | ||||
|         return; | ||||
|     } | ||||
|     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 +224,22 @@ 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); | ||||
| 
 | ||||
|     const auto online_service = GetSessionService(ctx); | ||||
|     if (online_service == nullptr) { | ||||
|         return; | ||||
|     } | ||||
|     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 +251,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 +262,22 @@ void Module::Interface::GetNsDataIdList(Kernel::HLERequestContext& ctx) { | |||
|     const u32 start_ns_data_id = rp.Pop<u32>(); | ||||
|     auto& buffer = rp.PopMappedBuffer(); | ||||
| 
 | ||||
|     const auto online_service = GetSessionService(ctx); | ||||
|     if (online_service == nullptr) { | ||||
|         return; | ||||
|     } | ||||
|     const u16 entries_count = online_service->GetNsDataIdList(filter, max_entries, buffer); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push<u16>(0); /// Actual number of output entries
 | ||||
|     rb.Push<u16>(0); /// Last word-index copied to output in the internal NsDataId list.
 | ||||
|     rb.Push<u16>(entries_count); /// Actual number of output entries
 | ||||
|     rb.Push<u16>(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 +288,22 @@ void Module::Interface::GetNsDataIdList1(Kernel::HLERequestContext& ctx) { | |||
|     const u32 start_ns_data_id = rp.Pop<u32>(); | ||||
|     auto& buffer = rp.PopMappedBuffer(); | ||||
| 
 | ||||
|     const auto online_service = GetSessionService(ctx); | ||||
|     if (online_service == nullptr) { | ||||
|         return; | ||||
|     } | ||||
|     const u16 entries_count = online_service->GetNsDataIdList(filter, max_entries, buffer); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push<u16>(0); /// Actual number of output entries
 | ||||
|     rb.Push<u16>(0); /// Last word-index copied to output in the internal NsDataId list.
 | ||||
|     rb.Push<u16>(entries_count); /// Actual number of output entries
 | ||||
|     rb.Push<u16>(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 +314,22 @@ void Module::Interface::GetNsDataIdList2(Kernel::HLERequestContext& ctx) { | |||
|     const u32 start_ns_data_id = rp.Pop<u32>(); | ||||
|     auto& buffer = rp.PopMappedBuffer(); | ||||
| 
 | ||||
|     const auto online_service = GetSessionService(ctx); | ||||
|     if (online_service == nullptr) { | ||||
|         return; | ||||
|     } | ||||
|     const u16 entries_count = online_service->GetNsDataIdList(filter, max_entries, buffer); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push<u16>(0); /// Actual number of output entries
 | ||||
|     rb.Push<u16>(0); /// Last word-index copied to output in the internal NsDataId list.
 | ||||
|     rb.Push<u16>(entries_count); /// Actual number of output entries
 | ||||
|     rb.Push<u16>(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 +340,22 @@ void Module::Interface::GetNsDataIdList3(Kernel::HLERequestContext& ctx) { | |||
|     const u32 start_ns_data_id = rp.Pop<u32>(); | ||||
|     auto& buffer = rp.PopMappedBuffer(); | ||||
| 
 | ||||
|     const auto online_service = GetSessionService(ctx); | ||||
|     if (online_service == nullptr) { | ||||
|         return; | ||||
|     } | ||||
|     const u16 entries_count = online_service->GetNsDataIdList(filter, max_entries, buffer); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push<u16>(0); /// Actual number of output entries
 | ||||
|     rb.Push<u16>(0); /// Last word-index copied to output in the internal NsDataId list.
 | ||||
|     rb.Push<u16>(entries_count); /// Actual number of output entries
 | ||||
|     rb.Push<u16>(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 +364,17 @@ void Module::Interface::SendProperty(Kernel::HLERequestContext& ctx) { | |||
|     const u32 size = rp.Pop<u32>(); | ||||
|     auto& buffer = rp.PopMappedBuffer(); | ||||
| 
 | ||||
|     const auto online_service = GetSessionService(ctx); | ||||
|     if (online_service == nullptr) { | ||||
|         return; | ||||
|     } | ||||
|     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 +385,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 +394,18 @@ void Module::Interface::ReceiveProperty(Kernel::HLERequestContext& ctx) { | |||
|     const u32 size = rp.Pop<u32>(); | ||||
|     auto& buffer = rp.PopMappedBuffer(); | ||||
| 
 | ||||
|     const auto online_service = GetSessionService(ctx); | ||||
|     if (online_service == nullptr) { | ||||
|         return; | ||||
|     } | ||||
|     const auto result = online_service->ReceiveProperty(property_id, size, buffer); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push<u32>(size); /// Should be actual read size
 | ||||
|     rb.Push(result); | ||||
|     rb.Push<u32>(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 +418,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 +427,15 @@ void Module::Interface::UpdateTaskCount(Kernel::HLERequestContext& ctx) { | |||
|     const u32 unk_param2 = rp.Pop<u32>(); | ||||
|     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}, task_id={}", size, | ||||
|                 unk_param2, task_id); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::GetTaskInterval(Kernel::HLERequestContext& ctx) { | ||||
|  | @ -340,7 +448,7 @@ void Module::Interface::GetTaskInterval(Kernel::HLERequestContext& ctx) { | |||
|     rb.Push<u32>(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 +456,15 @@ void Module::Interface::GetTaskCount(Kernel::HLERequestContext& ctx) { | |||
|     const u32 size = rp.Pop<u32>(); | ||||
|     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<u32>(0); // stub 0 ( 32bit value)
 | ||||
|     rb.PushMappedBuffer(buffer); | ||||
| 
 | ||||
|     LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}", size); | ||||
|     LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010x}, task_id={}", size, task_id); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::GetTaskServiceStatus(Kernel::HLERequestContext& ctx) { | ||||
|  | @ -361,12 +472,16 @@ void Module::Interface::GetTaskServiceStatus(Kernel::HLERequestContext& ctx) { | |||
|     const u32 size = rp.Pop<u32>(); | ||||
|     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<u8>(0); // stub 0 ( 8bit value)
 | ||||
|     rb.Push<u8>(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 +493,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 +505,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 +517,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 +543,7 @@ void Module::Interface::GetTaskState(Kernel::HLERequestContext& ctx) { | |||
|     rb.Push<u8>(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 +558,7 @@ void Module::Interface::GetTaskResult(Kernel::HLERequestContext& ctx) { | |||
|     rb.Push<u8>(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 +573,7 @@ void Module::Interface::GetTaskCommErrorCode(Kernel::HLERequestContext& ctx) { | |||
|     rb.Push<u8>(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 +588,7 @@ void Module::Interface::GetTaskStatus(Kernel::HLERequestContext& ctx) { | |||
|     rb.Push<u8>(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 +603,7 @@ void Module::Interface::GetTaskError(Kernel::HLERequestContext& ctx) { | |||
|     rb.Push<u8>(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 +616,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 +626,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 +640,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 +657,7 @@ void Module::Interface::ReadNsData(Kernel::HLERequestContext& ctx) { | |||
|     rb.Push<u32>(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 +669,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,19 +681,19 @@ void Module::Interface::GetNsDataAdditionalInfo(Kernel::HLERequestContext& ctx) | |||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push<u32>(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) { | ||||
|     IPC::RequestParser rp(ctx); | ||||
|     const u32 unk_param1 = rp.Pop<u32>(); | ||||
|     ns_data_new_flag = rp.Pop<u8>(); | ||||
|     boss->ns_data_new_flag = rp.Pop<u8>(); | ||||
| 
 | ||||
|     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, | ||||
|                 ns_data_new_flag); | ||||
|     LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1={:#010x}, ns_data_new_flag={:#04x}", unk_param1, | ||||
|                 boss->ns_data_new_flag); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::GetNsDataNewFlag(Kernel::HLERequestContext& ctx) { | ||||
|  | @ -587,10 +702,10 @@ void Module::Interface::GetNsDataNewFlag(Kernel::HLERequestContext& ctx) { | |||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push<u8>(ns_data_new_flag); | ||||
|     rb.Push<u8>(boss->ns_data_new_flag); | ||||
| 
 | ||||
|     LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1={:#010X}, ns_data_new_flag={:#04X}", unk_param1, | ||||
|                 ns_data_new_flag); | ||||
|     LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1={:#010x}, ns_data_new_flag={:#04x}", unk_param1, | ||||
|                 boss->ns_data_new_flag); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::GetNsDataLastUpdate(Kernel::HLERequestContext& ctx) { | ||||
|  | @ -602,7 +717,7 @@ void Module::Interface::GetNsDataLastUpdate(Kernel::HLERequestContext& ctx) { | |||
|     rb.Push<u32>(0); // stub 0 (32bit value)
 | ||||
|     rb.Push<u32>(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 +728,7 @@ void Module::Interface::GetErrorCode(Kernel::HLERequestContext& ctx) { | |||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push<u32>(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 +743,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 +770,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 +797,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 +810,7 @@ void Module::Interface::GetTaskProperty0(Kernel::HLERequestContext& ctx) { | |||
|     rb.Push<u8>(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 +824,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 +840,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 +856,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 +868,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 +879,7 @@ void Module::Interface::GetAppNewFlag(Kernel::HLERequestContext& ctx) { | |||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push<u8>(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 +898,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 +919,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 +934,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 +945,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 +962,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 +981,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); | ||||
| } | ||||
| 
 | ||||
|  | @ -874,15 +989,15 @@ void Module::Interface::SetNsDataNewFlagPrivileged(Kernel::HLERequestContext& ct | |||
|     IPC::RequestParser rp(ctx); | ||||
|     const u64 programID = rp.Pop<u64>(); | ||||
|     const u32 unk_param1 = rp.Pop<u32>(); | ||||
|     ns_data_new_flag_privileged = rp.Pop<u8>(); | ||||
|     boss->ns_data_new_flag_privileged = rp.Pop<u8>(); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|     LOG_WARNING( | ||||
|         Service_BOSS, | ||||
|         "(STUBBED) programID={:#018X}, unk_param1={:#010X}, ns_data_new_flag_privileged={:#04X}", | ||||
|         programID, unk_param1, ns_data_new_flag_privileged); | ||||
|         "(STUBBED) programID={:#018x}, unk_param1={:#010x}, ns_data_new_flag_privileged={:#04x}", | ||||
|         programID, unk_param1, boss->ns_data_new_flag_privileged); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::GetNsDataNewFlagPrivileged(Kernel::HLERequestContext& ctx) { | ||||
|  | @ -892,18 +1007,18 @@ void Module::Interface::GetNsDataNewFlagPrivileged(Kernel::HLERequestContext& ct | |||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push<u8>(ns_data_new_flag_privileged); | ||||
|     rb.Push<u8>(boss->ns_data_new_flag_privileged); | ||||
| 
 | ||||
|     LOG_WARNING( | ||||
|         Service_BOSS, | ||||
|         "(STUBBED) programID={:#018X}, unk_param1={:#010X}, ns_data_new_flag_privileged={:#04X}", | ||||
|         programID, unk_param1, ns_data_new_flag_privileged); | ||||
|         "(STUBBED) programID={:#018x}, unk_param1={:#010x}, ns_data_new_flag_privileged={:#04x}", | ||||
|         programID, unk_param1, boss->ns_data_new_flag_privileged); | ||||
| } | ||||
| 
 | ||||
| Module::Interface::Interface(std::shared_ptr<Module> boss, const char* name, u32 max_session) | ||||
|     : ServiceFramework(name, max_session), 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 = | ||||
|  |  | |||
|  | @ -5,10 +5,11 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <boost/serialization/shared_ptr.hpp> | ||||
| #include <boost/serialization/export.hpp> | ||||
| #include "core/global.h" | ||||
| #include "core/hle/kernel/event.h" | ||||
| #include "core/hle/kernel/resource_limit.h" | ||||
| #include "core/hle/service/boss/online_service.h" | ||||
| #include "core/hle/service/service.h" | ||||
| 
 | ||||
| namespace Core { | ||||
|  | @ -19,10 +20,19 @@ namespace Service::BOSS { | |||
| 
 | ||||
| class Module final { | ||||
| public: | ||||
|     explicit Module(Core::System& system); | ||||
|     explicit Module(Core::System& system_); | ||||
|     ~Module() = default; | ||||
| 
 | ||||
|     class Interface : public ServiceFramework<Interface> { | ||||
|     struct SessionData : public Kernel::SessionRequestHandler::SessionDataBase { | ||||
|         std::shared_ptr<OnlineService> online_service{}; | ||||
| 
 | ||||
|     private: | ||||
|         template <class Archive> | ||||
|         void serialize(Archive& ar, const unsigned int); | ||||
|         friend class boost::serialization::access; | ||||
|     }; | ||||
| 
 | ||||
|     class Interface : public ServiceFramework<Interface, SessionData> { | ||||
|     public: | ||||
|         Interface(std::shared_ptr<Module> boss, const char* name, u32 max_session); | ||||
|         ~Interface() = default; | ||||
|  | @ -958,29 +968,20 @@ public: | |||
|     protected: | ||||
|         std::shared_ptr<Module> boss; | ||||
| 
 | ||||
|     private: | ||||
|         u8 new_arrival_flag; | ||||
|         u8 ns_data_new_flag; | ||||
|         u8 ns_data_new_flag_privileged; | ||||
|         u8 output_flag; | ||||
| 
 | ||||
|         template <class Archive> | ||||
|         void serialize(Archive& ar, const unsigned int) { | ||||
|             ar& new_arrival_flag; | ||||
|             ar& ns_data_new_flag; | ||||
|             ar& ns_data_new_flag_privileged; | ||||
|             ar& output_flag; | ||||
|         } | ||||
|         friend class boost::serialization::access; | ||||
|         std::shared_ptr<OnlineService> GetSessionService(Kernel::HLERequestContext& ctx); | ||||
|     }; | ||||
| 
 | ||||
| private: | ||||
|     Core::System& system; | ||||
|     std::shared_ptr<Kernel::Event> task_finish_event; | ||||
| 
 | ||||
|     u8 new_arrival_flag; | ||||
|     u8 ns_data_new_flag; | ||||
|     u8 ns_data_new_flag_privileged; | ||||
|     u8 output_flag; | ||||
| 
 | ||||
|     template <class Archive> | ||||
|     void serialize(Archive& ar, const unsigned int) { | ||||
|         ar& task_finish_event; | ||||
|     } | ||||
|     void serialize(Archive& ar, const unsigned int); | ||||
|     friend class boost::serialization::access; | ||||
| }; | ||||
| 
 | ||||
|  | @ -988,9 +989,6 @@ void InstallInterfaces(Core::System& system); | |||
| 
 | ||||
| } // namespace Service::BOSS
 | ||||
| 
 | ||||
| namespace boost::serialization { | ||||
| template <class Archive> | ||||
| void load_construct_data(Archive& ar, Service::BOSS::Module* t, const unsigned int) { | ||||
|     ::new (t) Service::BOSS::Module(Core::Global<Core::System>()); | ||||
| } | ||||
| } // namespace boost::serialization
 | ||||
| SERVICE_CONSTRUCT(Service::BOSS::Module) | ||||
| BOOST_CLASS_EXPORT_KEY(Service::BOSS::Module) | ||||
| BOOST_CLASS_EXPORT_KEY(Service::BOSS::Module::SessionData) | ||||
|  |  | |||
							
								
								
									
										400
									
								
								src/core/hle/service/boss/online_service.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										400
									
								
								src/core/hle/service/boss/online_service.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,400 @@ | |||
| // Copyright 2023 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <boost/serialization/map.hpp> | ||||
| #include "common/archives.h" | ||||
| #include "common/file_util.h" | ||||
| #include "common/serialization/boost_std_variant.hpp" | ||||
| #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(u64 program_id_, u64 extdata_id_) | ||||
|     : program_id(program_id_), extdata_id(extdata_id_) {} | ||||
| 
 | ||||
| template <class Archive> | ||||
| void OnlineService::serialize(Archive& ar, const unsigned int) { | ||||
|     ar& current_props; | ||||
|     ar& task_id_list; | ||||
|     ar& program_id; | ||||
|     ar& extdata_id; | ||||
| } | ||||
| SERIALIZE_IMPL(OnlineService) | ||||
| 
 | ||||
| template <class Archive> | ||||
| void BossTaskProperties::serialize(Archive& ar, const unsigned int) { | ||||
|     ar& task_result; | ||||
|     ar& properties; | ||||
| } | ||||
| SERIALIZE_IMPL(BossTaskProperties) | ||||
| 
 | ||||
| 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<FileSys::ArchiveBackend> 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<u8*>(&sv_data)); | ||||
| 
 | ||||
|         BossSSData ss_data; | ||||
|         boss_ss->Read(entry_offset, sizeof(BossSSData), reinterpret_cast<u8*>(&ss_data)); | ||||
| 
 | ||||
|         if (sv_data.program_id != init_program_id) { | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         std::vector<u8> 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<u16>(task_id_list.size()); | ||||
| 
 | ||||
|     const auto num_task_ids = TASKIDLIST_SIZE / TASK_ID_SIZE; | ||||
|     std::vector<std::array<u8, TASK_ID_SIZE>> 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<std::vector<u8>>(¤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<FileSys::Entry> OnlineService::GetBossExtDataFiles() { | ||||
|     FileSys::ArchiveFactory_ExtSaveData boss_extdata_archive_factory( | ||||
|         FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::ExtSaveDataType::Boss); | ||||
|     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<FileSys::Entry> 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<u32>(extdata_id >> 32); | ||||
|     const u32 low = static_cast<u32>(extdata_id & 0xFFFFFFFF); | ||||
|     return FileSys::ConstructExtDataBinaryPath(1, high, low); | ||||
| } | ||||
| 
 | ||||
| std::vector<NsDataEntry> OnlineService::GetNsDataEntries() { | ||||
|     FileSys::ArchiveFactory_ExtSaveData boss_extdata_archive_factory( | ||||
|         FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::ExtSaveDataType::Boss); | ||||
|     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<NsDataEntry> ns_data; | ||||
|     std::vector<FileSys::Entry> 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<u8*>(&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<u64>(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<u32>(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<NsDataEntry> ns_data = GetNsDataEntries(); | ||||
|     std::vector<u32> output_entries; | ||||
|     for (const auto& current_entry : ns_data) { | ||||
|         const u32 datatype_raw = static_cast<u32>(current_entry.header.datatype); | ||||
|         const u16 datatype_high = static_cast<u16>(datatype_raw >> 16); | ||||
|         const u16 datatype_low = static_cast<u16>(datatype_raw & 0xFFFF); | ||||
|         const u16 filter_high = static_cast<u16>(filter >> 16); | ||||
|         const u16 filter_low = static_cast<u16>(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<u16>(output_entries.size()); | ||||
| } | ||||
| 
 | ||||
| template <class... Ts> | ||||
| struct overload : Ts... { | ||||
|     using Ts::operator()...; | ||||
| }; | ||||
| template <class... Ts> | ||||
| overload(Ts...) -> overload<Ts...>; | ||||
| 
 | ||||
| ResultCode OnlineService::SendProperty(const u16 id, const u32 size, Kernel::MappedBuffer& buffer) { | ||||
|     const auto property_id = static_cast<PropertyID>(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 = [&]<typename T>(T& old_prop) { | ||||
|         static_assert(std::is_trivial<T>::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 = [&]<typename T>(std::vector<T>& 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<T> 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<u8>& cur_prop) { read_vector(cur_prop); }, | ||||
|                         [&](std::vector<u32>& 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<PropertyID>(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 = [&]<typename T>(T& cur_prop) { | ||||
|         static_assert(std::is_trivial<T>::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 = [&]<typename T>(std::vector<T>& 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<u8>& cur_prop) { write_vector(cur_prop); }, | ||||
|                         [&](std::vector<u32>& cur_prop) { write_vector(cur_prop); }}, | ||||
|                prop); | ||||
| 
 | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| } // namespace Service::BOSS
 | ||||
							
								
								
									
										169
									
								
								src/core/hle/service/boss/online_service.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								src/core/hle/service/boss/online_service.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,169 @@ | |||
| // Copyright 2023 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <string> | ||||
| #include <variant> | ||||
| #include <vector> | ||||
| 
 | ||||
| #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<u8, 8> boss_system_savedata_id{ | ||||
|     0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x01, 0x00, | ||||
| }; | ||||
| 
 | ||||
| #pragma pack(push, 4) | ||||
| struct BossHeader { | ||||
|     u8 header_length; | ||||
|     std::array<u8, 11> zero1; | ||||
|     u32_be unknown; | ||||
|     u32_be download_date; | ||||
|     std::array<u8, 4> zero2; | ||||
|     u64_be program_id; | ||||
|     std::array<u8, 4> 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<char, TASK_ID_SIZE> task_id; | ||||
| }; | ||||
| 
 | ||||
| struct BossSSData { | ||||
|     INSERT_PADDING_BYTES(0x21C); | ||||
|     std::array<u8, URL_SIZE> url; | ||||
| }; | ||||
| 
 | ||||
| using BossTaskProperty = std::variant<u8, u16, u32, std::vector<u8>, std::vector<u32>>; | ||||
| struct BossTaskProperties { | ||||
|     bool task_result; | ||||
|     std::map<PropertyID, BossTaskProperty> properties{ | ||||
|         {static_cast<PropertyID>(0x00), u8()}, | ||||
|         {static_cast<PropertyID>(0x01), u8()}, | ||||
|         {static_cast<PropertyID>(0x02), u32()}, | ||||
|         {PropertyID::Interval, u32()}, | ||||
|         {PropertyID::Duration, u32()}, | ||||
|         {static_cast<PropertyID>(0x05), u8()}, | ||||
|         {static_cast<PropertyID>(0x06), u8()}, | ||||
|         {PropertyID::Url, std::vector<u8>(URL_SIZE)}, | ||||
|         {static_cast<PropertyID>(0x08), u32()}, | ||||
|         {static_cast<PropertyID>(0x09), u8()}, | ||||
|         {static_cast<PropertyID>(0x0A), std::vector<u8>(0x100)}, | ||||
|         {static_cast<PropertyID>(0x0B), std::vector<u8>(0x200)}, | ||||
|         {static_cast<PropertyID>(0x0C), u32()}, | ||||
|         {PropertyID::Headers, std::vector<u8>(HEADERS_SIZE)}, | ||||
|         {PropertyID::CertId, u32()}, | ||||
|         {PropertyID::CertIdList, std::vector<u32>(CERTIDLIST_SIZE)}, | ||||
|         {PropertyID::LoadCert, u8()}, | ||||
|         {PropertyID::LoadRootCert, u8()}, | ||||
|         {static_cast<PropertyID>(0x12), u8()}, | ||||
|         {static_cast<PropertyID>(0x13), u32()}, | ||||
|         {static_cast<PropertyID>(0x14), u32()}, | ||||
|         {static_cast<PropertyID>(0x15), std::vector<u8>(0x40)}, | ||||
|         {static_cast<PropertyID>(0x16), u32()}, | ||||
|         {static_cast<PropertyID>(0x18), u8()}, | ||||
|         {static_cast<PropertyID>(0x19), u8()}, | ||||
|         {static_cast<PropertyID>(0x1A), u8()}, | ||||
|         {static_cast<PropertyID>(0x1B), u32()}, | ||||
|         {static_cast<PropertyID>(0x1C), u32()}, | ||||
|         {PropertyID::TotalTasks, u16()}, | ||||
|         {PropertyID::TaskIdList, std::vector<u8>(TASKIDLIST_SIZE)}, | ||||
|         {static_cast<PropertyID>(0x3B), u32()}, | ||||
|         {static_cast<PropertyID>(0x3E), std::vector<u8>(0x200)}, | ||||
|         {static_cast<PropertyID>(0x3F), u8()}, | ||||
|     }; | ||||
| 
 | ||||
|     template <class Archive> | ||||
|     void serialize(Archive& ar, const unsigned int); | ||||
|     friend class boost::serialization::access; | ||||
| }; | ||||
| 
 | ||||
| class OnlineService final { | ||||
| public: | ||||
|     explicit OnlineService(u64 program_id_, u64 extdata_id_); | ||||
| 
 | ||||
|     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<FileSys::Entry> GetBossExtDataFiles(); | ||||
|     FileSys::Path GetBossDataDir(); | ||||
|     std::vector<NsDataEntry> GetNsDataEntries(); | ||||
| 
 | ||||
|     BossTaskProperties current_props; | ||||
|     std::map<std::string, BossTaskProperties> task_id_list; | ||||
| 
 | ||||
|     u64 program_id; | ||||
|     u64 extdata_id; | ||||
| 
 | ||||
|     // For serialization
 | ||||
|     explicit OnlineService() = default; | ||||
| 
 | ||||
|     template <class Archive> | ||||
|     void serialize(Archive& ar, const unsigned int); | ||||
|     friend class boost::serialization::access; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Service::BOSS
 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue