Merge PR 6883

This commit is contained in:
citrabot 2023-11-22 00:25:59 +00:00
parent 5733c8681e
commit a3dcb40e82
10 changed files with 682 additions and 95 deletions

View file

@ -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

View file

@ -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<u8> binary_data) : type(LowPathType::Binary), binary(std::move(binary_data)) {}
template <std::size_t size>
Path(const std::array<u8, size>& binary_data)

View file

@ -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<std::unique_ptr<ArchiveBackend>> 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.

View file

@ -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<ArchiveFactory>(*this);
ar& shared;
ar& mount_point;
ar& boss;
}
friend class boost::serialization::access;
};

View file

@ -14,13 +14,15 @@ namespace Service::BOSS {
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 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<u8>();
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<u8>();
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<u32>();
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<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 +224,18 @@ void Module::Interface::GetNsDataIdList1(Kernel::HLERequestContext& ctx) {
const u32 start_ns_data_id = rp.Pop<u32>();
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<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 +246,18 @@ void Module::Interface::GetNsDataIdList2(Kernel::HLERequestContext& ctx) {
const u32 start_ns_data_id = rp.Pop<u32>();
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<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 +268,18 @@ void Module::Interface::GetNsDataIdList3(Kernel::HLERequestContext& ctx) {
const u32 start_ns_data_id = rp.Pop<u32>();
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<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 +288,13 @@ void Module::Interface::SendProperty(Kernel::HLERequestContext& ctx) {
const u32 size = rp.Pop<u32>();
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<u32>();
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<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 +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<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}", size, unk_param2);
}
void Module::Interface::GetTaskInterval(Kernel::HLERequestContext& ctx) {
@ -340,7 +363,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 +371,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}", size);
}
void Module::Interface::GetTaskServiceStatus(Kernel::HLERequestContext& ctx) {
@ -361,12 +387,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 +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<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 +473,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 +488,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 +503,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 +518,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 +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<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 +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<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) {
@ -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<u8>(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<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 +643,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 +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<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 +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<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 +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<Module> 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 =

View file

@ -8,6 +8,7 @@
#include <boost/serialization/shared_ptr.hpp>
#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<Interface> {
@ -955,6 +956,7 @@ public:
void GetNsDataNewFlagPrivileged(Kernel::HLERequestContext& ctx);
protected:
OnlineService online_service;
std::shared_ptr<Module> boss;
private:
@ -974,6 +976,7 @@ public:
};
private:
Core::System& system;
std::shared_ptr<Kernel::Event> task_finish_event;
template <class Archive>

View file

@ -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<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>>(&current_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), 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<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), 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<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 u16 datatype_high =
static_cast<u16>(static_cast<u32>(current_entry.header.datatype) >> 16);
const u16 datatype_low =
static_cast<u16>((static_cast<u32>(current_entry.header.datatype) & 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

View file

@ -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 <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()},
};
};
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<FileSys::Entry> GetBossExtDataFiles();
FileSys::Path GetBossDataDir();
std::vector<NsDataEntry> GetNsDataEntries();
BossTaskProperties current_props;
std::map<std::string, BossTaskProperties> task_id_list;
Core::System& system;
u64 program_id;
u64 extdata_id;
};
} // namespace Service::BOSS

View file

@ -365,6 +365,10 @@ void ArchiveManager::RegisterArchiveTypes() {
std::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory);
RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData);
auto bossextsavedata_factory =
std::make_unique<FileSys::ArchiveFactory_ExtSaveData>(sdmc_directory, false, true);
RegisterArchiveType(std::move(bossextsavedata_factory), ArchiveIdCode::BossExtSaveData);
auto selfncch_factory = std::make_unique<FileSys::ArchiveFactory_SelfNCCH>();
RegisterArchiveType(std::move(selfncch_factory), ArchiveIdCode::SelfNCCH);
}

View file

@ -40,6 +40,7 @@ enum class ArchiveIdCode : u32 {
SystemSaveData = 0x00000008,
SDMC = 0x00000009,
SDMCWriteOnly = 0x0000000A,
BossExtSaveData = 0x12345678,
NCCH = 0x2345678A,
OtherSaveDataGeneral = 0x567890B2,
OtherSaveDataPermitted = 0x567890B4,