mirror of
https://github.com/citra-emu/citra-canary.git
synced 2024-12-22 18:45:26 +00:00
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:
parent
3113ae6616
commit
602f4f60d8
|
@ -631,34 +631,51 @@ void Module::Interface::DeleteNsData(Kernel::HLERequestContext& ctx) {
|
|||
|
||||
void Module::Interface::GetNsDataHeaderInfo(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
const u32 ns_data_id = rp.Pop<u32>();
|
||||
const u8 type = rp.Pop<u8>();
|
||||
const u32 size = rp.Pop<u32>();
|
||||
const auto ns_data_id = rp.Pop<u32>();
|
||||
const auto type = rp.PopEnum<NsDataHeaderInfoType>();
|
||||
const auto size = rp.Pop<u32>();
|
||||
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);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(result);
|
||||
rb.PushMappedBuffer(buffer);
|
||||
|
||||
LOG_WARNING(Service_BOSS, "(STUBBED) ns_data_id={:#010x}, type={:#04x}, size={:#010x}",
|
||||
ns_data_id, type, size);
|
||||
LOG_DEBUG(Service_BOSS, "called, ns_data_id={:#010x}, type={:#04x}, size={:#010x}", ns_data_id,
|
||||
type, size);
|
||||
}
|
||||
|
||||
void Module::Interface::ReadNsData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
const u32 ns_data_id = rp.Pop<u32>();
|
||||
const u64 offset = rp.Pop<u64>();
|
||||
const u32 size = rp.Pop<u32>();
|
||||
const auto ns_data_id = rp.Pop<u32>();
|
||||
const auto offset = rp.Pop<u64>();
|
||||
const auto size = rp.Pop<u32>();
|
||||
auto& buffer = rp.PopMappedBuffer();
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(3, 2);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(size); /// Should be actual read size
|
||||
rb.Push<u32>(0); /// unknown
|
||||
rb.PushMappedBuffer(buffer);
|
||||
const auto online_service = GetSessionService(ctx);
|
||||
if (online_service == nullptr) {
|
||||
return;
|
||||
}
|
||||
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}",
|
||||
ns_data_id, offset, size);
|
||||
if (result.Succeeded()) {
|
||||
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) {
|
||||
|
@ -710,14 +727,27 @@ void Module::Interface::GetNsDataNewFlag(Kernel::HLERequestContext& ctx) {
|
|||
|
||||
void Module::Interface::GetNsDataLastUpdate(Kernel::HLERequestContext& 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);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(0); // stub 0 (32bit value)
|
||||
rb.Push<u32>(0); // stub 0 (32bit value)
|
||||
rb.Push<u32>(0);
|
||||
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) {
|
||||
|
|
|
@ -73,12 +73,14 @@ ResultCode OnlineService::InitializeSession(u64 init_program_id) {
|
|||
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);
|
||||
// TODO: Proper error code.
|
||||
return RESULT_UNKNOWN;
|
||||
}
|
||||
boss_system_save_data_archive = std::move(create_archive_result).Unwrap();
|
||||
} else {
|
||||
LOG_ERROR(Service_BOSS, "Could not open BOSS savedata");
|
||||
return ResultCode(1);
|
||||
// TODO: Proper error code.
|
||||
return RESULT_UNKNOWN;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (size > TASK_ID_SIZE) {
|
||||
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);
|
||||
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);
|
||||
// TODO: Proper error code.
|
||||
return RESULT_UNKNOWN;
|
||||
}
|
||||
|
||||
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(
|
||||
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 {};
|
||||
LOG_WARNING(Service_BOSS, "Failed to open SpotPass ext data archive with ID '{:#010x}'.",
|
||||
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("/");
|
||||
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 {};
|
||||
}
|
||||
auto dir = std::move(dir_result).Unwrap();
|
||||
|
@ -219,29 +235,20 @@ std::vector<FileSys::Entry> OnlineService::GetBossExtDataFiles() {
|
|||
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");
|
||||
auto boss_archive = OpenBossExtData();
|
||||
if (!boss_archive) {
|
||||
return {};
|
||||
}
|
||||
auto boss_archive = std::move(archive_result).Unwrap().get();
|
||||
|
||||
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) {
|
||||
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");
|
||||
LOG_WARNING(Service_BOSS,
|
||||
"SpotPass extdata contains directory or file is too short: '{}'",
|
||||
Common::UTF16ToUTF8(current_file.filename));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -315,6 +322,129 @@ u16 OnlineService::GetNsDataIdList(const u32 filter, const u32 max_entries,
|
|||
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>
|
||||
struct overload : Ts... {
|
||||
using Ts::operator()...;
|
||||
|
@ -325,8 +455,9 @@ 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);
|
||||
LOG_ERROR(Service_BOSS, "Unknown property with ID {:#06x} and size {}", property_id, size);
|
||||
// TODO: Proper error code.
|
||||
return RESULT_UNKNOWN;
|
||||
}
|
||||
|
||||
auto& prop = current_props.properties[property_id];
|
||||
|
@ -365,8 +496,9 @@ 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);
|
||||
LOG_ERROR(Service_BOSS, "Unknown property with ID {:#06x} and size {}", property_id, size);
|
||||
// TODO: Proper error code.
|
||||
return RESULT_UNKNOWN;
|
||||
}
|
||||
|
||||
auto write_pod = [&]<typename T>(T& cur_prop) {
|
||||
|
|
|
@ -17,6 +17,7 @@ class MappedBuffer;
|
|||
}
|
||||
|
||||
namespace FileSys {
|
||||
class ArchiveBackend;
|
||||
struct Entry;
|
||||
class Path;
|
||||
} // namespace FileSys
|
||||
|
@ -67,6 +68,27 @@ struct NsDataEntry {
|
|||
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 {
|
||||
Interval = 0x03,
|
||||
Duration = 0x04,
|
||||
|
@ -144,11 +166,17 @@ public:
|
|||
ResultCode UnregisterTask(const u32 size, Kernel::MappedBuffer& buffer);
|
||||
void GetTaskIdList();
|
||||
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 ReceiveProperty(const u16 id, const u32 size, Kernel::MappedBuffer& buffer);
|
||||
|
||||
private:
|
||||
std::vector<FileSys::Entry> GetBossExtDataFiles();
|
||||
std::unique_ptr<FileSys::ArchiveBackend> OpenBossExtData();
|
||||
std::vector<FileSys::Entry> GetBossExtDataFiles(FileSys::ArchiveBackend* boss_archive);
|
||||
FileSys::Path GetBossDataDir();
|
||||
std::vector<NsDataEntry> GetNsDataEntries();
|
||||
|
||||
|
|
Loading…
Reference in a new issue