boss: Implement some NsData header and read commands. (#7283)

* boss: Implement some NsData header and read commands.

Co-authored-by: Rokkubro <lachlanb03@gmail.com>

* boss: Move opening ext data to common function and improve logging.

---------

Co-authored-by: Rokkubro <lachlanb03@gmail.com>
This commit is contained in:
Steveice10 2023-12-26 09:01:32 -08:00 committed by GitHub
parent 3113ae6616
commit 602f4f60d8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 239 additions and 49 deletions

View file

@ -631,34 +631,51 @@ void Module::Interface::DeleteNsData(Kernel::HLERequestContext& ctx) {
void Module::Interface::GetNsDataHeaderInfo(Kernel::HLERequestContext& ctx) { void Module::Interface::GetNsDataHeaderInfo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx); IPC::RequestParser rp(ctx);
const u32 ns_data_id = rp.Pop<u32>(); const auto ns_data_id = rp.Pop<u32>();
const u8 type = rp.Pop<u8>(); const auto type = rp.PopEnum<NsDataHeaderInfoType>();
const u32 size = rp.Pop<u32>(); const auto size = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer(); auto& buffer = rp.PopMappedBuffer();
const auto online_service = GetSessionService(ctx);
if (online_service == nullptr) {
return;
}
const auto result = online_service->GetNsDataHeaderInfo(ns_data_id, type, size, buffer);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(RESULT_SUCCESS); rb.Push(result);
rb.PushMappedBuffer(buffer); rb.PushMappedBuffer(buffer);
LOG_WARNING(Service_BOSS, "(STUBBED) ns_data_id={:#010x}, type={:#04x}, size={:#010x}", LOG_DEBUG(Service_BOSS, "called, ns_data_id={:#010x}, type={:#04x}, size={:#010x}", ns_data_id,
ns_data_id, type, size); type, size);
} }
void Module::Interface::ReadNsData(Kernel::HLERequestContext& ctx) { void Module::Interface::ReadNsData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx); IPC::RequestParser rp(ctx);
const u32 ns_data_id = rp.Pop<u32>(); const auto ns_data_id = rp.Pop<u32>();
const u64 offset = rp.Pop<u64>(); const auto offset = rp.Pop<u64>();
const u32 size = rp.Pop<u32>(); const auto size = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer(); auto& buffer = rp.PopMappedBuffer();
IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); const auto online_service = GetSessionService(ctx);
rb.Push(RESULT_SUCCESS); if (online_service == nullptr) {
rb.Push<u32>(size); /// Should be actual read size return;
rb.Push<u32>(0); /// unknown }
rb.PushMappedBuffer(buffer); const auto result = online_service->ReadNsData(ns_data_id, offset, size, buffer);
LOG_WARNING(Service_BOSS, "(STUBBED) ns_data_id={:#010x}, offset={:#018x}, size={:#010x}", if (result.Succeeded()) {
ns_data_id, offset, size); IPC::RequestBuilder rb = rp.MakeBuilder(3, 2);
rb.Push(result.Code());
rb.Push<u32>(static_cast<u32>(result.Unwrap()));
rb.Push<u32>(0); /// unknown
rb.PushMappedBuffer(buffer);
} else {
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(result.Code());
}
LOG_DEBUG(Service_BOSS, "called, ns_data_id={:#010x}, offset={:#018x}, size={:#010x}",
ns_data_id, offset, size);
} }
void Module::Interface::SetNsDataAdditionalInfo(Kernel::HLERequestContext& ctx) { void Module::Interface::SetNsDataAdditionalInfo(Kernel::HLERequestContext& ctx) {
@ -710,14 +727,27 @@ void Module::Interface::GetNsDataNewFlag(Kernel::HLERequestContext& ctx) {
void Module::Interface::GetNsDataLastUpdate(Kernel::HLERequestContext& ctx) { void Module::Interface::GetNsDataLastUpdate(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx); IPC::RequestParser rp(ctx);
const u32 unk_param1 = rp.Pop<u32>(); const u32 ns_data_id = rp.Pop<u32>();
const auto online_service = GetSessionService(ctx);
if (online_service == nullptr) {
return;
}
const auto entry = online_service->GetNsDataEntryFromId(ns_data_id);
if (!entry.has_value()) {
// TODO: Proper error code.
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_UNKNOWN);
return;
}
IPC::RequestBuilder rb = rp.MakeBuilder(3, 0); IPC::RequestBuilder rb = rp.MakeBuilder(3, 0);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // stub 0 (32bit value) rb.Push<u32>(0);
rb.Push<u32>(0); // stub 0 (32bit value) rb.Push<u32>(entry->header.download_date); // return the download date from the ns data
LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1={:#010x}", unk_param1); LOG_DEBUG(Service_BOSS, "called, ns_data_id={:#010X}", ns_data_id);
} }
void Module::Interface::GetErrorCode(Kernel::HLERequestContext& ctx) { void Module::Interface::GetErrorCode(Kernel::HLERequestContext& ctx) {

View file

@ -73,12 +73,14 @@ ResultCode OnlineService::InitializeSession(u64 init_program_id) {
auto create_archive_result = systemsavedata_factory.Open(archive_path, 0); auto create_archive_result = systemsavedata_factory.Open(archive_path, 0);
if (!create_archive_result.Succeeded()) { if (!create_archive_result.Succeeded()) {
LOG_ERROR(Service_BOSS, "Could not open BOSS savedata"); LOG_ERROR(Service_BOSS, "Could not open BOSS savedata");
return ResultCode(1); // TODO: Proper error code.
return RESULT_UNKNOWN;
} }
boss_system_save_data_archive = std::move(create_archive_result).Unwrap(); boss_system_save_data_archive = std::move(create_archive_result).Unwrap();
} else { } else {
LOG_ERROR(Service_BOSS, "Could not open BOSS savedata"); LOG_ERROR(Service_BOSS, "Could not open BOSS savedata");
return ResultCode(1); // TODO: Proper error code.
return RESULT_UNKNOWN;
} }
FileSys::Mode open_mode = {}; FileSys::Mode open_mode = {};
@ -151,14 +153,16 @@ void OnlineService::RegisterTask(const u32 size, Kernel::MappedBuffer& buffer) {
ResultCode OnlineService::UnregisterTask(const u32 size, Kernel::MappedBuffer& buffer) { ResultCode OnlineService::UnregisterTask(const u32 size, Kernel::MappedBuffer& buffer) {
if (size > TASK_ID_SIZE) { if (size > TASK_ID_SIZE) {
LOG_WARNING(Service_BOSS, "TaskId cannot be longer than 8"); LOG_WARNING(Service_BOSS, "TaskId cannot be longer than 8");
return ResultCode(1); // TODO: Proper error code.
return RESULT_UNKNOWN;
} }
std::string task_id(size, 0); std::string task_id(size, 0);
buffer.Read(task_id.data(), 0, size); buffer.Read(task_id.data(), 0, size);
if (task_id_list.erase(task_id) == 0) { if (task_id_list.erase(task_id) == 0) {
LOG_WARNING(Service_BOSS, "TaskId not in list"); LOG_WARNING(Service_BOSS, "TaskId not in list");
return ResultCode(1); // TODO: Proper error code.
return RESULT_UNKNOWN;
} }
return RESULT_SUCCESS; return RESULT_SUCCESS;
@ -187,20 +191,32 @@ void OnlineService::GetTaskIdList() {
} }
} }
std::vector<FileSys::Entry> OnlineService::GetBossExtDataFiles() { 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::unique_ptr<FileSys::ArchiveBackend> OnlineService::OpenBossExtData() {
FileSys::ArchiveFactory_ExtSaveData boss_extdata_archive_factory( FileSys::ArchiveFactory_ExtSaveData boss_extdata_archive_factory(
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::ExtSaveDataType::Boss); FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::ExtSaveDataType::Boss);
const FileSys::Path boss_path{GetBossDataDir()}; const FileSys::Path boss_path{GetBossDataDir()};
auto archive_result = boss_extdata_archive_factory.Open(boss_path, 0); auto archive_result = boss_extdata_archive_factory.Open(boss_path, 0);
if (!archive_result.Succeeded()) { if (!archive_result.Succeeded()) {
LOG_WARNING(Service_BOSS, "Extdata opening failed"); LOG_WARNING(Service_BOSS, "Failed to open SpotPass ext data archive with ID '{:#010x}'.",
return {}; extdata_id);
return nullptr;
} }
return std::move(archive_result).Unwrap();
}
auto boss_archive = std::move(archive_result).Unwrap(); std::vector<FileSys::Entry> OnlineService::GetBossExtDataFiles(
FileSys::ArchiveBackend* boss_archive) {
auto dir_result = boss_archive->OpenDirectory("/"); auto dir_result = boss_archive->OpenDirectory("/");
if (!dir_result.Succeeded()) { if (!dir_result.Succeeded()) {
LOG_WARNING(Service_BOSS, "Extdata directory opening failed"); LOG_WARNING(Service_BOSS,
"Failed to open root directory of SpotPass ext data with ID '{:#010x}'.",
extdata_id);
return {}; return {};
} }
auto dir = std::move(dir_result).Unwrap(); auto dir = std::move(dir_result).Unwrap();
@ -219,29 +235,20 @@ std::vector<FileSys::Entry> OnlineService::GetBossExtDataFiles() {
return boss_files; 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() { std::vector<NsDataEntry> OnlineService::GetNsDataEntries() {
FileSys::ArchiveFactory_ExtSaveData boss_extdata_archive_factory( auto boss_archive = OpenBossExtData();
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::ExtSaveDataType::Boss); if (!boss_archive) {
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 {}; return {};
} }
auto boss_archive = std::move(archive_result).Unwrap().get();
std::vector<NsDataEntry> ns_data; std::vector<NsDataEntry> ns_data;
std::vector<FileSys::Entry> boss_files = GetBossExtDataFiles(); const auto boss_files = GetBossExtDataFiles(boss_archive.get());
for (const auto& current_file : boss_files) { for (const auto& current_file : boss_files) {
constexpr u32 boss_header_length = 0x34; constexpr u32 boss_header_length = 0x34;
if (current_file.is_directory || current_file.file_size < boss_header_length) { 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"); LOG_WARNING(Service_BOSS,
"SpotPass extdata contains directory or file is too short: '{}'",
Common::UTF16ToUTF8(current_file.filename));
continue; continue;
} }
@ -315,6 +322,129 @@ u16 OnlineService::GetNsDataIdList(const u32 filter, const u32 max_entries,
return static_cast<u16>(output_entries.size()); return static_cast<u16>(output_entries.size());
} }
std::optional<NsDataEntry> OnlineService::GetNsDataEntryFromId(const u32 ns_data_id) {
std::vector<NsDataEntry> ns_data = GetNsDataEntries();
const auto entry_iter = std::find_if(ns_data.begin(), ns_data.end(), [ns_data_id](auto entry) {
return entry.header.ns_data_id == ns_data_id;
});
if (entry_iter == ns_data.end()) {
LOG_WARNING(Service_BOSS, "Could not find NsData with ID {:#010X}", ns_data_id);
return std::nullopt;
}
return *entry_iter;
}
ResultCode OnlineService::GetNsDataHeaderInfo(const u32 ns_data_id, const NsDataHeaderInfoType type,
const u32 size, Kernel::MappedBuffer& buffer) {
const auto entry = GetNsDataEntryFromId(ns_data_id);
if (!entry.has_value()) {
LOG_WARNING(Service_BOSS, "Failed to find NsData entry for ID {:#010X}", ns_data_id);
// TODO: Proper error code.
return RESULT_UNKNOWN;
}
static constexpr std::array EXPECTED_NS_DATA_HEADER_INFO_SIZES = {
sizeof(u64), // Program ID
sizeof(u32), // Unknown
sizeof(u32), // Data Type
sizeof(u32), // Payload Size
sizeof(u32), // NsData ID
sizeof(u32), // Version
sizeof(NsDataHeaderInfo), // Everything
};
if (size != EXPECTED_NS_DATA_HEADER_INFO_SIZES[static_cast<u8>(type)]) {
LOG_WARNING(Service_BOSS, "Invalid size {} for type {}", size, type);
// TODO: Proper error code.
return RESULT_UNKNOWN;
}
switch (type) {
case NsDataHeaderInfoType::ProgramId:
buffer.Write(&entry->header.program_id, 0, size);
return RESULT_SUCCESS;
case NsDataHeaderInfoType::Unknown: {
// TODO: Figure out what this is. Stubbed to zero for now.
const u32 zero = 0;
buffer.Write(&zero, 0, size);
return RESULT_SUCCESS;
}
case NsDataHeaderInfoType::Datatype:
buffer.Write(&entry->header.datatype, 0, size);
return RESULT_SUCCESS;
case NsDataHeaderInfoType::PayloadSize:
buffer.Write(&entry->header.payload_size, 0, size);
return RESULT_SUCCESS;
case NsDataHeaderInfoType::NsDataId:
buffer.Write(&entry->header.ns_data_id, 0, size);
return RESULT_SUCCESS;
case NsDataHeaderInfoType::Version:
buffer.Write(&entry->header.version, 0, size);
return RESULT_SUCCESS;
case NsDataHeaderInfoType::Everything: {
const NsDataHeaderInfo info = {
.program_id = entry->header.program_id,
.datatype = entry->header.datatype,
.payload_size = entry->header.payload_size,
.ns_data_id = entry->header.ns_data_id,
.version = entry->header.version,
};
buffer.Write(&info, 0, size);
return RESULT_SUCCESS;
}
default:
LOG_WARNING(Service_BOSS, "Unknown header info type {}", type);
// TODO: Proper error code.
return RESULT_UNKNOWN;
}
}
ResultVal<size_t> OnlineService::ReadNsData(const u32 ns_data_id, const u64 offset, const u32 size,
Kernel::MappedBuffer& buffer) {
std::optional<NsDataEntry> entry = GetNsDataEntryFromId(ns_data_id);
if (!entry.has_value()) {
LOG_WARNING(Service_BOSS, "Failed to find NsData entry for ID {:#010X}", ns_data_id);
// TODO: Proper error code.
return RESULT_UNKNOWN;
}
if (entry->header.payload_size < size + offset) {
LOG_WARNING(Service_BOSS,
"Invalid request to read {:#010X} bytes at offset {:#010X}, payload "
"length is {:#010X}",
size, offset, static_cast<u32>(entry->header.payload_size));
// TODO: Proper error code.
return RESULT_UNKNOWN;
}
auto boss_archive = OpenBossExtData();
if (!boss_archive) {
// TODO: Proper error code.
return RESULT_UNKNOWN;
}
FileSys::Path file_path = fmt::format("/{}", entry->filename);
FileSys::Mode mode{};
mode.read_flag.Assign(1);
auto file_result = boss_archive->OpenFile(file_path, mode);
if (!file_result.Succeeded()) {
LOG_WARNING(Service_BOSS, "Failed to open SpotPass extdata file '{}'.", entry->filename);
// TODO: Proper error code.
return RESULT_UNKNOWN;
}
auto file = std::move(file_result).Unwrap();
std::vector<u8> ns_data_array(size);
auto read_result = file->Read(sizeof(BossHeader) + offset, size, ns_data_array.data());
if (!read_result.Succeeded()) {
LOG_WARNING(Service_BOSS, "Failed to read SpotPass extdata file '{}'.", entry->filename);
// TODO: Proper error code.
return RESULT_UNKNOWN;
}
buffer.Write(ns_data_array.data(), 0, size);
return read_result;
}
template <class... Ts> template <class... Ts>
struct overload : Ts... { struct overload : Ts... {
using Ts::operator()...; using Ts::operator()...;
@ -325,8 +455,9 @@ overload(Ts...) -> overload<Ts...>;
ResultCode OnlineService::SendProperty(const u16 id, const u32 size, Kernel::MappedBuffer& buffer) { ResultCode OnlineService::SendProperty(const u16 id, const u32 size, Kernel::MappedBuffer& buffer) {
const auto property_id = static_cast<PropertyID>(id); const auto property_id = static_cast<PropertyID>(id);
if (!current_props.properties.contains(property_id)) { if (!current_props.properties.contains(property_id)) {
LOG_ERROR(Service_BOSS, "Unknown property with id {:#06x}", property_id); LOG_ERROR(Service_BOSS, "Unknown property with ID {:#06x} and size {}", property_id, size);
return ResultCode(1); // TODO: Proper error code.
return RESULT_UNKNOWN;
} }
auto& prop = current_props.properties[property_id]; auto& prop = current_props.properties[property_id];
@ -365,8 +496,9 @@ ResultCode OnlineService::ReceiveProperty(const u16 id, const u32 size,
Kernel::MappedBuffer& buffer) { Kernel::MappedBuffer& buffer) {
const auto property_id = static_cast<PropertyID>(id); const auto property_id = static_cast<PropertyID>(id);
if (!current_props.properties.contains(property_id)) { if (!current_props.properties.contains(property_id)) {
LOG_ERROR(Service_BOSS, "Unknown property with id {:#06x}", property_id); LOG_ERROR(Service_BOSS, "Unknown property with ID {:#06x} and size {}", property_id, size);
return ResultCode(1); // TODO: Proper error code.
return RESULT_UNKNOWN;
} }
auto write_pod = [&]<typename T>(T& cur_prop) { auto write_pod = [&]<typename T>(T& cur_prop) {

View file

@ -17,6 +17,7 @@ class MappedBuffer;
} }
namespace FileSys { namespace FileSys {
class ArchiveBackend;
struct Entry; struct Entry;
class Path; class Path;
} // namespace FileSys } // namespace FileSys
@ -67,6 +68,27 @@ struct NsDataEntry {
BossHeader header; BossHeader header;
}; };
enum class NsDataHeaderInfoType : u8 {
ProgramId = 0,
Unknown = 1,
Datatype = 2,
PayloadSize = 3,
NsDataId = 4,
Version = 5,
Everything = 6,
};
struct NsDataHeaderInfo {
u64 program_id;
INSERT_PADDING_BYTES(4);
u32 datatype;
u32 payload_size;
u32 ns_data_id;
u32 version;
INSERT_PADDING_BYTES(4);
};
static_assert(sizeof(NsDataHeaderInfo) == 0x20, "NsDataHeaderInfo has incorrect size");
enum class PropertyID : u16 { enum class PropertyID : u16 {
Interval = 0x03, Interval = 0x03,
Duration = 0x04, Duration = 0x04,
@ -144,11 +166,17 @@ public:
ResultCode UnregisterTask(const u32 size, Kernel::MappedBuffer& buffer); ResultCode UnregisterTask(const u32 size, Kernel::MappedBuffer& buffer);
void GetTaskIdList(); void GetTaskIdList();
u16 GetNsDataIdList(const u32 filter, const u32 max_entries, Kernel::MappedBuffer& buffer); u16 GetNsDataIdList(const u32 filter, const u32 max_entries, Kernel::MappedBuffer& buffer);
std::optional<NsDataEntry> GetNsDataEntryFromId(const u32 ns_data_id);
ResultCode GetNsDataHeaderInfo(const u32 ns_data_id, const NsDataHeaderInfoType type,
const u32 size, Kernel::MappedBuffer& buffer);
ResultVal<size_t> ReadNsData(const u32 ns_data_id, const u64 offset, const u32 size,
Kernel::MappedBuffer& buffer);
ResultCode SendProperty(const u16 id, const u32 size, 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); ResultCode ReceiveProperty(const u16 id, const u32 size, Kernel::MappedBuffer& buffer);
private: private:
std::vector<FileSys::Entry> GetBossExtDataFiles(); std::unique_ptr<FileSys::ArchiveBackend> OpenBossExtData();
std::vector<FileSys::Entry> GetBossExtDataFiles(FileSys::ArchiveBackend* boss_archive);
FileSys::Path GetBossDataDir(); FileSys::Path GetBossDataDir();
std::vector<NsDataEntry> GetNsDataEntries(); std::vector<NsDataEntry> GetNsDataEntries();