Rokkubro b5ff2440f6 Squash commits during rebase again:
Final things from review comments

Clang format

Shorten property code (similar to #6883) and move to utils

Move all additional helper functions into a 'utils' file. Simplify some things as requested in review

Minor code changes from review before rebase

fix misusing std span

Fix leftovers from rebase, and null-terminator problem with download string-view

Change downloadbossdatafromurl to take in string_views, make getting the list of files more dynamic

Fix error in linux builds and cleanup

Squash commits during rebase:

Some changes as per review and cleanup

More changes as per review

Changes as per review

Futures cannot be copied, remove stubbed warning on implemented calls, remove unneeded task_status simulation, simplify getting task_status and duration

Implement downloading tasks in background

Very final changes from review; and use common for converting strings

FInal changes for review

Attempt to fix codecvt error

Use references when any_casting

Update boost submodule to use master again, refactor how properties work, other minor changes per review

Fix operator overload error on linux/mingw

Make some changes as requested by review; change boost submodule url temporarily to use boost.url

Fix for android build

Fixes android builds when web services are enabled, like in #6555

Avoid crashes when urls are invalid

clang-format

Return error status properly on task fail

Fix implementation of gettaskstate, gettaskstatus and gettaskservicestatus

Fix mingw build error

Add support for reading tasks from boss save data databases.

clang-format

Implement storing task properties

Fix missing includes and add references in loops

Change task_id_list to map, initial implementation of task properties, minor refactor

Remove the dependency on the newer behavior of std erase to fix android building

Fix compilation on android and other platforms when web services are not enabled

Fix clang-format errors

Add support for downloading and decrypting spotpass data directly from nintendo servers

Fix windows implicit conversion error again

Fix comment

Fix filter in NsDataIdList; Finish GetNsDataHeaderInfo; Implement basic support for registering tasks and checking if they exist TODO actually read and write from boss savedata dbs

Add boss extdata to archive.h so the lle boss module can function properly

Implement ReadNsData and partially implement GetNsDataHeaderInfo and GetNsDataLastUpdate; MK7 now reads spotpass data and successfully boots!

Made requested changes; added filtering; removed readnsdata implementation

Add partial implementations of GetNsDataIdList(1/2/3) and ReadNsData

Add zeroed array of nsdataid entries, run clang-format

Check the spotpass extdata directory to determine number of ns output entries

Check for PLvPWAA

Only set the number of output entries in GetNsDataIdList1 to 1 if PLvPWAA is detected.

Fix plvpwaa dlc error

Return 1 for the number of output entries in the GetNsDataIdList1 stub. This fixes the extra content for Professor Layton vs Phoenix Wright Ace Attorney as the game expects the boss extdata to not be empty. Might break other games if they attempt to do anything with the ns data. (although the readnsdata and deletensdata methods are both still stubbed)
2023-11-02 21:09:42 +11:00

1377 lines
51 KiB
C++

// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <common/common_paths.h>
#include <core/file_sys/archive_systemsavedata.h>
#include <cryptopp/aes.h>
#include <cryptopp/modes.h>
#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/archive_extsavedata.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/file_backend.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/boss/boss.h"
#include "core/hle/service/boss/boss_p.h"
#include "core/hle/service/boss/boss_u.h"
#include "core/hw/aes/key.h"
namespace Service::BOSS {
void Module::Interface::InitializeSession(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u64 provided_program_id = rp.Pop<u64>();
rp.PopPID();
util.cur_props = BossTaskProperties();
// I'm putting this here for now because I don't know where else to put it;
// the BOSS service saves data in its 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) and BOSS_SV
// (Saved Values? Includes task id and most properties, keyed on task_id) 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.
if (provided_program_id != 0) {
util.program_id = provided_program_id;
}
LOG_DEBUG(Service_BOSS, "called, program_id={:#018X}", util.program_id);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
// We can always return success from this as the boss dbs are not properly used atm
rb.Push(RESULT_SUCCESS);
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
FileSys::ArchiveFactory_SystemSaveData systemsavedata_factory(nand_directory);
// Open the SystemSaveData archive 0x00010034
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 the archive didn't exist, create the files inside
if (archive_result.Code() == FileSys::ERROR_NOT_FOUND) {
// Format the archive to create the directories
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()) {
boss_system_save_data_archive = std::move(create_archive_result).Unwrap();
} else {
LOG_ERROR(Service_BOSS, "Could not open boss savedata");
return;
}
} else if (archive_result.Succeeded()) {
boss_system_save_data_archive = std::move(archive_result).Unwrap();
} else {
LOG_ERROR(Service_BOSS, "Could not open boss savedata");
return;
}
FileSys::Path boss_a_path("/BOSS_A.db");
FileSys::Mode open_mode = {};
open_mode.read_flag.Assign(1);
auto boss_a_result = boss_system_save_data_archive->OpenFile(boss_a_path, open_mode);
// Read the file if it already exists
if (boss_a_result.Succeeded()) {
auto boss_a = std::move(boss_a_result).Unwrap();
const u64 boss_a_size = boss_a->GetSize();
// Check the file has a valid size (multiple of the number of entries, plus header)
if (!(boss_a_size > BOSS_SAVE_HEADER_SIZE &&
((boss_a_size - BOSS_SAVE_HEADER_SIZE) % BOSS_A_ENTRY_SIZE) == 0)) {
} else {
u64 num_entries = (boss_a_size - BOSS_SAVE_HEADER_SIZE) / BOSS_A_ENTRY_SIZE;
// Print the program ids of the entries
for (u64 i = 0; i < num_entries; i++) {
u64 entry_offset = i * BOSS_A_ENTRY_SIZE + BOSS_SAVE_HEADER_SIZE;
u64 prog_id;
boss_a->Read(entry_offset, sizeof(prog_id), reinterpret_cast<u8*>(&prog_id));
LOG_DEBUG(Service_BOSS, "Id in entry {} is {:#018X}", i, prog_id);
}
}
}
FileSys::Path boss_sv_path("/BOSS_SV.db");
auto boss_sv_result = boss_system_save_data_archive->OpenFile(boss_sv_path, open_mode);
FileSys::Path boss_ss_path("/BOSS_SS.db");
auto boss_ss_result = boss_system_save_data_archive->OpenFile(boss_ss_path, open_mode);
// Read the files if they already exist
if (boss_sv_result.Succeeded() && boss_ss_result.Succeeded()) {
auto boss_sv = std::move(boss_sv_result).Unwrap();
auto boss_ss = std::move(boss_ss_result).Unwrap();
// Check the file length is valid. Both files should have the same number of entries
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_WARNING(Service_BOSS, "Boss dbs have incorrect size");
return;
} else {
u64 num_entries = (boss_sv->GetSize() - BOSS_SAVE_HEADER_SIZE) / BOSS_S_ENTRY_SIZE;
for (u64 i = 0; i < num_entries; i++) {
u64 entry_offset = i * BOSS_S_ENTRY_SIZE + BOSS_SAVE_HEADER_SIZE;
// Print the program id and task id to debug
u64 prog_id;
boss_sv->Read(entry_offset + BOSS_S_PROG_ID_OFFSET, sizeof(prog_id),
reinterpret_cast<u8*>(&prog_id));
LOG_DEBUG(Service_BOSS, "Id sv in entry {} is {:#018X}", i, prog_id);
std::string task_id(TASK_ID_SIZE, 0);
boss_sv->Read(entry_offset + BOSS_S_TASK_ID_OFFSET, TASK_ID_SIZE,
reinterpret_cast<u8*>(task_id.data()));
LOG_DEBUG(Service_BOSS, "Task id in entry {} is {}", i, task_id);
std::vector<u8> url(URL_SIZE);
boss_ss->Read(entry_offset + BOSS_S_URL_OFFSET, URL_SIZE, url.data());
LOG_DEBUG(Service_BOSS, "Url for task {} is {}", task_id,
std::string_view(reinterpret_cast<char*>(url.data()), url.size()));
// If the task is for the current program, store the download url for the session.
// In the future we could store more properties, including from the sv db.
if (prog_id == util.program_id) {
LOG_DEBUG(Service_BOSS, "Storing download url");
util.cur_props.props[PropertyID::Url] = url;
if (util.task_id_list.contains(task_id)) {
LOG_WARNING(Service_BOSS, "Task id already in list, will be replaced");
util.task_id_list.erase(task_id);
}
util.task_id_list.emplace(task_id, std::move(util.cur_props));
util.cur_props = BossTaskProperties();
}
}
}
}
}
void Module::Interface::SetStorageInfo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u64 extdata_id = rp.Pop<u64>();
const u32 boss_size = rp.Pop<u32>();
const u8 extdata_type = rp.Pop<u8>(); /// 0 = NAND, 1 = SD
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_BOSS,
"(STUBBED) extdata_id={:#018X}, boss_size={:#010X}, extdata_type={:#04X}",
extdata_id, boss_size, extdata_type);
}
void Module::Interface::UnregisterStorage(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_BOSS, "(STUBBED) called");
}
void Module::Interface::GetStorageInfo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
LOG_WARNING(Service_BOSS, "(STUBBED) called");
}
void Module::Interface::RegisterPrivateRootCa(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
[[maybe_unused]] const u32 size = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(RESULT_SUCCESS);
rb.PushMappedBuffer(buffer);
LOG_WARNING(Service_BOSS, "(STUBBED)");
}
void Module::Interface::RegisterPrivateClientCert(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 buffer1_size = rp.Pop<u32>();
const u32 buffer2_size = rp.Pop<u32>();
auto& buffer1 = rp.PopMappedBuffer();
auto& buffer2 = rp.PopMappedBuffer();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 4);
rb.Push(RESULT_SUCCESS);
rb.PushMappedBuffer(buffer1);
rb.PushMappedBuffer(buffer2);
LOG_WARNING(Service_BOSS, "(STUBBED) buffer1_size={:#010X}, buffer2_size={:#010X}, ",
buffer1_size, buffer2_size);
}
void Module::Interface::GetNewArrivalFlag(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(new_arrival_flag);
LOG_WARNING(Service_BOSS, "(STUBBED) new_arrival_flag={}", new_arrival_flag);
}
void Module::Interface::RegisterNewArrivalEvent(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
[[maybe_unused]] const auto event = rp.PopObject<Kernel::Event>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_BOSS, "(STUBBED)");
}
void Module::Interface::SetOptoutFlag(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
output_flag = rp.Pop<u8>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_BOSS, "output_flag={}", output_flag);
}
void Module::Interface::GetOptoutFlag(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(output_flag);
LOG_WARNING(Service_BOSS, "output_flag={}", output_flag);
}
void Module::Interface::RegisterTask(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 size = rp.Pop<u32>();
const u8 unk_param2 = rp.Pop<u8>();
const u8 unk_param3 = rp.Pop<u8>();
auto& buffer = rp.PopMappedBuffer();
std::string task_id(size, 0);
buffer.Read(task_id.data(), 0, size);
if (util.task_id_list.contains(task_id)) {
LOG_WARNING(Service_BOSS, "Task id already in list, will be replaced");
util.task_id_list.erase(task_id);
}
util.task_id_list.emplace(task_id, std::move(util.cur_props));
util.cur_props = BossTaskProperties();
LOG_DEBUG(Service_BOSS, "Read task id {}", task_id);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(RESULT_SUCCESS);
rb.PushMappedBuffer(buffer);
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) {
IPC::RequestParser rp(ctx);
const u32 size = rp.Pop<u32>();
const u8 unk_param2 = rp.Pop<u8>();
auto& buffer = rp.PopMappedBuffer();
ResultCode result = RESULT_FAILED;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
if (size > TASK_ID_SIZE) {
LOG_WARNING(Service_BOSS, "Task Id cannot be longer than 8");
rb.Push(result);
rb.PushMappedBuffer(buffer);
return;
} else {
std::string task_id(size, 0);
buffer.Read(task_id.data(), 0, size);
LOG_DEBUG(Service_BOSS, "Read task id {}", task_id);
if (util.task_id_list.erase(task_id) == 0) {
LOG_WARNING(Service_BOSS, "Task Id not in list");
} else {
LOG_DEBUG(Service_BOSS, "Task Id erased");
result = RESULT_SUCCESS;
}
}
rb.Push(result);
rb.PushMappedBuffer(buffer);
LOG_DEBUG(Service_BOSS, "called, size={:#010X}, unk_param2={:#04X}", size, unk_param2);
}
void Module::Interface::ReconfigureTask(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 size = rp.Pop<u32>();
const u8 unk_param2 = rp.Pop<u8>();
auto& buffer = rp.PopMappedBuffer();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(RESULT_SUCCESS);
rb.PushMappedBuffer(buffer);
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 u16 num_task_ids = static_cast<u16>(util.task_id_list.size());
util.cur_props.props[PropertyID::TotalTasks] = num_task_ids;
LOG_DEBUG(Service_BOSS, "Prepared total_tasks = {}", num_task_ids);
u16 num_returned_task_ids = 0;
std::vector<std::array<u8, TASK_ID_SIZE>> task_ids(TASKIDLIST_SIZE / TASK_ID_SIZE);
for (const auto& iter : util.task_id_list) {
const std::string_view cur_task_id = iter.first;
if (cur_task_id.size() > TASK_ID_SIZE ||
num_returned_task_ids >= TASKIDLIST_SIZE / TASK_ID_SIZE) {
LOG_WARNING(Service_BOSS, "task id {} too long or too many task ids", cur_task_id);
} else {
std::memcpy(task_ids[num_returned_task_ids].data(), cur_task_id.data(), TASK_ID_SIZE);
num_returned_task_ids++;
LOG_TRACE(Service_BOSS, "wrote task id {}", cur_task_id);
}
}
const auto task_list_prop =
std::get_if<std::vector<u8>>(&util.cur_props.props[PropertyID::TaskIdList]);
if (task_list_prop && task_list_prop->size() == TASKIDLIST_SIZE) {
std::memcpy(task_list_prop->data(), task_ids.data(), TASKIDLIST_SIZE);
LOG_DEBUG(Service_BOSS, "wrote out {} task ids", num_returned_task_ids);
}
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_BOSS, "called");
}
void Module::Interface::GetStepIdList(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 size = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(RESULT_SUCCESS);
rb.PushMappedBuffer(buffer);
LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}", size);
}
void Module::Interface::GetNsDataIdList(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 filter = rp.Pop<u32>();
const u32 max_entries = rp.Pop<u32>(); /// buffer size in words
const u16 word_index_start = rp.Pop<u16>();
const u32 start_ns_data_id = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer();
const u16 entries_count = util.GetOutputEntries(filter, max_entries, buffer);
IPC::RequestBuilder rb = rp.MakeBuilder(3, 2);
rb.Push(RESULT_SUCCESS);
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_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) {
IPC::RequestParser rp(ctx);
const u32 filter = rp.Pop<u32>();
const u32 max_entries = rp.Pop<u32>(); /// buffer size in words
const u16 word_index_start = rp.Pop<u16>();
const u32 start_ns_data_id = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer();
const u16 entries_count = util.GetOutputEntries(filter, max_entries, buffer);
IPC::RequestBuilder rb = rp.MakeBuilder(3, 2);
rb.Push(RESULT_SUCCESS);
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_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) {
IPC::RequestParser rp(ctx);
const u32 filter = rp.Pop<u32>();
const u32 max_entries = rp.Pop<u32>(); /// buffer size in words
const u16 word_index_start = rp.Pop<u16>();
const u32 start_ns_data_id = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer();
const u16 entries_count = util.GetOutputEntries(filter, max_entries, buffer);
IPC::RequestBuilder rb = rp.MakeBuilder(3, 2);
rb.Push(RESULT_SUCCESS);
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_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) {
IPC::RequestParser rp(ctx);
const u32 filter = rp.Pop<u32>();
const u32 max_entries = rp.Pop<u32>(); /// buffer size in words
const u16 word_index_start = rp.Pop<u16>();
const u32 start_ns_data_id = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer();
const u16 entries_count = util.GetOutputEntries(filter, max_entries, buffer);
IPC::RequestBuilder rb = rp.MakeBuilder(3, 2);
rb.Push(RESULT_SUCCESS);
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_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) {
IPC::RequestParser rp(ctx);
const PropertyID property_id = static_cast<PropertyID>(rp.Pop<u16>());
const u32 size = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer();
LOG_DEBUG(Service_BOSS, "called, property_id={:#06X}, size={:#010X}", property_id, size);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
ResultCode result = RESULT_FAILED;
if (util.ReadWriteProperties(false, property_id, size, buffer)) {
result = RESULT_SUCCESS;
}
rb.Push(result);
rb.PushMappedBuffer(buffer);
}
void Module::Interface::SendPropertyHandle(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u16 property_id = rp.Pop<u16>();
[[maybe_unused]] const std::shared_ptr<Kernel::Object> object = rp.PopGenericObject();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_BOSS, "(STUBBED) property_id={:#06X}", property_id);
}
void Module::Interface::ReceiveProperty(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const PropertyID property_id = static_cast<PropertyID>(rp.Pop<u16>());
const u32 size = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer();
LOG_DEBUG(Service_BOSS, "called, property_id={:#06X}, size={:#010X}", property_id, size);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
ResultCode result = RESULT_FAILED;
if (util.ReadWriteProperties(true, property_id, size, buffer)) {
result = RESULT_SUCCESS;
}
rb.Push(result);
rb.Push<u32>(size); // The size of the property per id, not how much data
rb.PushMappedBuffer(buffer);
}
void Module::Interface::UpdateTaskInterval(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 size = rp.Pop<u32>();
const u16 unk_param2 = rp.Pop<u16>();
auto& buffer = rp.PopMappedBuffer();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(RESULT_SUCCESS);
rb.PushMappedBuffer(buffer);
LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}, unk_param2={:#06X}", size, unk_param2);
}
void Module::Interface::UpdateTaskCount(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 size = rp.Pop<u32>();
const u32 unk_param2 = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer();
if (size > TASK_ID_SIZE) {
LOG_WARNING(Service_BOSS, "Task Id cannot be longer than 8");
} else {
std::string task_id(size, 0);
buffer.Read(task_id.data(), 0, size);
LOG_DEBUG(Service_BOSS, "Read task id {}", task_id);
}
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);
}
void Module::Interface::GetTaskInterval(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 size = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer();
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);
}
void Module::Interface::GetTaskCount(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 size = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer();
if (size > TASK_ID_SIZE) {
LOG_WARNING(Service_BOSS, "Task Id cannot be longer than 8");
} else {
std::string task_id(size, 0);
buffer.Read(task_id.data(), 0, size);
LOG_DEBUG(Service_BOSS, "Read task id {}", task_id);
}
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);
}
void Module::Interface::GetTaskServiceStatus(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(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?
u8 task_service_status = 1;
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(task_service_status); // stub 1 ( 8bit value) this is not taskstatus
rb.PushMappedBuffer(buffer);
LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}", size);
}
void Module::Interface::StartTask(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 size = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer();
if (size > TASK_ID_SIZE) {
LOG_WARNING(Service_BOSS, "Task Id cannot be longer than 8");
} else {
std::string task_id(size, 0);
buffer.Read(task_id.data(), 0, size);
if (!util.task_id_list.contains(task_id)) {
LOG_WARNING(Service_BOSS, "Task Id {} not found", task_id);
} else {
util.task_id_list[task_id].times_checked = 0;
if (const auto* url_prop = util.task_id_list[task_id].props.contains(PropertyID::Url)
? std::get_if<std::vector<u8>>(
&util.task_id_list[task_id].props[PropertyID::Url])
: nullptr;
url_prop && url_prop->size() == URL_SIZE) {
const char* url_pointer = reinterpret_cast<const char*>(url_prop->data());
std::string_view url(url_pointer, strnlen(url_pointer, URL_SIZE));
std::string_view file_name(util.task_id_list.find(task_id)->first.c_str(),
strnlen(task_id.c_str(), TASK_ID_SIZE));
util.task_id_list[task_id].download_task =
std::async(std::launch::async, DownloadBossDataFromURL, url, file_name,
util.program_id, util.extdata_id);
} else {
LOG_ERROR(Service_BOSS, "URL property is invalid");
}
}
LOG_DEBUG(Service_BOSS, "Read task id {}", task_id);
}
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(RESULT_SUCCESS);
rb.PushMappedBuffer(buffer);
LOG_DEBUG(Service_BOSS, "size={:#010X}", size);
}
void Module::Interface::StartTaskImmediate(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_BOSS, "StartTaskImmediate called");
// StartTask and StartTaskImmediate do much the same thing
StartTask(ctx);
LOG_DEBUG(Service_BOSS, "called");
}
void Module::Interface::CancelTask(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 size = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer();
if (size > TASK_ID_SIZE) {
LOG_WARNING(Service_BOSS, "Task Id cannot be longer than 8");
} else {
std::string task_id(size, 0);
buffer.Read(task_id.data(), 0, size);
LOG_DEBUG(Service_BOSS, "Read task id {}", task_id);
}
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(RESULT_SUCCESS);
rb.PushMappedBuffer(buffer);
LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}", size);
}
void Module::Interface::GetTaskFinishHandle(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects<Kernel::Event>(boss->task_finish_event);
LOG_WARNING(Service_BOSS, "(STUBBED) called");
}
void Module::Interface::GetTaskState(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 size = rp.Pop<u32>();
const s8 state = rp.Pop<u8>();
auto& buffer = rp.PopMappedBuffer();
TaskStatus task_status = TaskStatus::Failed;
u32 duration;
if (size > TASK_ID_SIZE) {
LOG_WARNING(Service_BOSS, "Task Id cannot be longer than 8");
duration = 0;
} else {
std::string task_id(size, 0);
buffer.Read(task_id.data(), 0, size);
LOG_DEBUG(Service_BOSS, "Read task id {}", task_id);
std::tie(task_status, duration) = util.GetTaskStatusAndDuration(task_id, false);
}
IPC::RequestBuilder rb = rp.MakeBuilder(4, 2);
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(static_cast<u8>(task_status)); /// TaskStatus
rb.Push<u32>(duration); /// Current state value for task PropertyID 0x4
rb.Push<u8>(0); /// unknown, usually 0
rb.PushMappedBuffer(buffer);
LOG_DEBUG(Service_BOSS, "size={:#010X}, state={:#06X}", size, state);
}
void Module::Interface::GetTaskResult(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 size = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer();
TaskStatus task_status = TaskStatus::Failed;
u32 duration;
if (size > TASK_ID_SIZE) {
LOG_WARNING(Service_BOSS, "Task Id cannot be longer than 8");
duration = 0;
} else {
std::string task_id(size, 0);
buffer.Read(task_id.data(), 0, size);
LOG_DEBUG(Service_BOSS, "Read task id {}", task_id);
std::tie(task_status, duration) = util.GetTaskStatusAndDuration(task_id, true);
}
IPC::RequestBuilder rb = rp.MakeBuilder(4, 2);
rb.Push(RESULT_SUCCESS);
// This might be task_status; however it is considered a failure if
// anything other than 0 is returned, apps won't call this method
// unless they have previously determined the task has ended
rb.Push<u8>(static_cast<u8>(task_status));
rb.Push<u32>(duration); // return duration (number of times to check)
rb.Push<u8>(0); // stub 0 (8 bit value)
rb.PushMappedBuffer(buffer);
LOG_DEBUG(Service_BOSS, "size={:#010X}", size);
}
void Module::Interface::GetTaskCommErrorCode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 size = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer();
if (size > TASK_ID_SIZE) {
LOG_WARNING(Service_BOSS, "Task Id cannot be longer than 8");
} else {
std::string task_id(size, 0);
buffer.Read(task_id.data(), 0, size);
LOG_DEBUG(Service_BOSS, "Read task id {}", task_id);
if (!util.task_id_list.contains(task_id)) {
LOG_WARNING(Service_BOSS, "Could not find task_id in list");
}
}
IPC::RequestBuilder rb = rp.MakeBuilder(4, 2);
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // stub 0 (32 bit value)
rb.Push<u32>(0); // stub 0 (32 bit value)
rb.Push<u8>(0); // stub 0 (8 bit value)
rb.PushMappedBuffer(buffer);
LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}", size);
}
void Module::Interface::GetTaskStatus(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 size = rp.Pop<u32>();
const u8 unk_param2 = rp.Pop<u8>();
const u8 unk_param3 = rp.Pop<u8>();
auto& buffer = rp.PopMappedBuffer();
TaskStatus task_status;
if (size > TASK_ID_SIZE) {
LOG_WARNING(Service_BOSS, "Task Id cannot be longer than 8");
task_status = TaskStatus::Failed;
} else {
std::string task_id(size, 0);
buffer.Read(task_id.data(), 0, size);
LOG_DEBUG(Service_BOSS, "Read task id {}", task_id);
task_status = util.GetTaskStatusAndDuration(task_id, false).first;
}
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(static_cast<u8>(task_status)); // return current task status
rb.PushMappedBuffer(buffer);
LOG_DEBUG(Service_BOSS, "size={:#010X}, unk_param2={:#04X}, unk_param3={:#04X}", size,
unk_param2, unk_param3);
}
void Module::Interface::GetTaskError(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 size = rp.Pop<u32>();
const u8 unk_param2 = rp.Pop<u8>();
auto& buffer = rp.PopMappedBuffer();
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
rb.Push(RESULT_SUCCESS);
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);
}
void Module::Interface::GetTaskInfo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 size = rp.Pop<u32>();
const u8 unk_param2 = rp.Pop<u8>();
auto& buffer = rp.PopMappedBuffer();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(RESULT_SUCCESS);
rb.PushMappedBuffer(buffer);
LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}, unk_param2={:#04X}", size, unk_param2);
}
void Module::Interface::DeleteNsData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 ns_data_id = rp.Pop<u32>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_BOSS, "(STUBBED) ns_data_id={:#010X}", ns_data_id);
}
void Module::Interface::GetNsDataHeaderInfo(Kernel::HLERequestContext& ctx) {
NsDataHeaderInfo info{};
IPC::RequestParser rp(ctx);
info.ns_data_id = rp.Pop<u32>();
const NsDataHeaderInfoType type = static_cast<NsDataHeaderInfoType>(rp.Pop<u8>());
const u32 size = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer();
ResultCode result = RESULT_FAILED;
u32 zero = 0;
std::optional<NsDataEntry> entry = util.GetNsDataEntryFromID(info.ns_data_id);
if (entry.has_value()) {
info.program_id = entry->header.program_id;
info.datatype = entry->header.datatype;
info.payload_size = entry->header.payload_size;
info.version = entry->header.version;
switch (type) {
case NsDataHeaderInfoType::ProgramId:
if (size != sizeof(info.program_id)) {
LOG_WARNING(Service_BOSS, "Invalid size {} for type {}", size, type);
break;
}
buffer.Write(&info.program_id, 0, size);
result = RESULT_SUCCESS;
LOG_DEBUG(Service_BOSS, "Wrote out program id {}", info.program_id);
break;
case NsDataHeaderInfoType::Unknown:
if (size != sizeof(u32)) {
LOG_WARNING(Service_BOSS, "Invalid size {} for type {}", size, type);
break;
}
buffer.Write(&zero, 0, size);
result = RESULT_SUCCESS;
LOG_DEBUG(Service_BOSS, "Wrote out unknown as zero");
break;
case NsDataHeaderInfoType::Datatype:
if (size != sizeof(info.datatype)) {
LOG_WARNING(Service_BOSS, "Invalid size {} for type {}", size, type);
break;
}
buffer.Write(&info.datatype, 0, size);
result = RESULT_SUCCESS;
LOG_DEBUG(Service_BOSS, "Wrote out content datatype {}", info.datatype);
break;
case NsDataHeaderInfoType::PayloadSize:
if (size != sizeof(info.payload_size)) {
LOG_WARNING(Service_BOSS, "Invalid size {} for type {}", size, type);
break;
}
buffer.Write(&info.payload_size, 0, size);
result = RESULT_SUCCESS;
LOG_DEBUG(Service_BOSS, "Wrote out payload size {}", info.payload_size);
break;
case NsDataHeaderInfoType::NsDataId:
if (size != sizeof(info.ns_data_id)) {
LOG_WARNING(Service_BOSS, "Invalid size {} for type {}", size, type);
break;
}
buffer.Write(&info.ns_data_id, 0, size);
result = RESULT_SUCCESS;
LOG_DEBUG(Service_BOSS, "Wrote out NsDataID {}", info.ns_data_id);
break;
case NsDataHeaderInfoType::Version:
if (size != sizeof(info.version)) {
LOG_WARNING(Service_BOSS, "Invalid size {} for type {}", size, type);
break;
}
buffer.Write(&info.version, 0, size);
result = RESULT_SUCCESS;
LOG_DEBUG(Service_BOSS, "Wrote out version {}", info.version);
break;
case NsDataHeaderInfoType::Everything:
if (size != sizeof(info)) {
LOG_WARNING(Service_BOSS, "Invalid size {} for type {}", size, type);
break;
}
buffer.Write(&info, 0, size);
result = RESULT_SUCCESS;
LOG_DEBUG(Service_BOSS,
"Wrote out unknown with program id {:#018X}, unknown zero, "
"datatype {:#010X}, "
"payload size {:#010X}, NsDataID {:#010X}, version "
"{:#010X} and unknown zero",
info.program_id, info.datatype, info.payload_size, info.ns_data_id,
info.version);
break;
default:
LOG_WARNING(Service_BOSS, "Unknown header info type {}", type);
result = RESULT_FAILED;
}
}
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(result);
rb.PushMappedBuffer(buffer);
LOG_DEBUG(Service_BOSS, "ns_data_id={:#010X}, type={:#04X}, size={:#010X}", info.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>();
auto& buffer = rp.PopMappedBuffer();
// This is the error code for NsDataID not found
ResultCode result = RESULT_FAILED;
u32 read_size = 0;
FileSys::ArchiveFactory_ExtSaveData boss_extdata_archive_factory(
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), false, true);
const FileSys::Path boss_path{GetBossDataDir(util.extdata_id)};
auto archive_result = boss_extdata_archive_factory.Open(boss_path, 0);
IPC::RequestBuilder rb = rp.MakeBuilder(3, 2);
std::optional<NsDataEntry> entry = util.GetNsDataEntryFromID(ns_data_id);
if (!archive_result.Succeeded() || !entry.has_value()) {
LOG_WARNING(Service_BOSS, "Opening Spotpass Extdata failed.");
rb.Push(result);
rb.Push<u32>(read_size);
rb.Push<u32>(0);
rb.PushMappedBuffer(buffer);
LOG_DEBUG(Service_BOSS, "ns_data_id={:#010X}, offset={:#018X}, size={:#010X}", ns_data_id,
offset, size);
return;
}
LOG_DEBUG(Service_BOSS, "Spotpass Extdata opened successfully!");
auto boss_archive = std::move(archive_result).Unwrap().get();
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, "Opening Spotpass file failed.");
rb.Push(result);
rb.Push<u32>(read_size);
rb.Push<u32>(0);
rb.PushMappedBuffer(buffer);
LOG_DEBUG(Service_BOSS, "ns_data_id={:#010X}, offset={:#018X}, size={:#010X}", ns_data_id,
offset, size);
return;
}
auto file = std::move(file_result).Unwrap();
LOG_DEBUG(Service_BOSS, "Opening Spotpass file succeeded!");
if (entry->header.payload_size < size + offset) {
LOG_WARNING(Service_BOSS,
"Request to read {:#010X} bytes at offset {:#010X}, payload "
"length is {:#010X}",
size, offset, u32(entry->header.payload_size));
rb.Push(result);
rb.Push<u32>(read_size);
rb.Push<u32>(0);
rb.PushMappedBuffer(buffer);
LOG_DEBUG(Service_BOSS, "ns_data_id={:#010X}, offset={:#018X}, size={:#010X}", ns_data_id,
offset, size);
return;
}
std::vector<u8> ns_data_array(size);
file->Read(BOSS_HEADER_LENGTH + offset, size, ns_data_array.data());
buffer.Write(ns_data_array.data(), 0, size);
result = RESULT_SUCCESS;
read_size = size;
LOG_DEBUG(Service_BOSS, "Read {:#010X} bytes from file {}", read_size, entry->filename);
rb.Push(result);
rb.Push<u32>(read_size); /// Should be actual read size
rb.Push<u32>(0); /// unknown
rb.PushMappedBuffer(buffer);
LOG_DEBUG(Service_BOSS, "ns_data_id={:#010X}, offset={:#018X}, size={:#010X}", ns_data_id,
offset, size);
}
void Module::Interface::SetNsDataAdditionalInfo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 unk_param1 = rp.Pop<u32>();
const u32 unk_param2 = rp.Pop<u32>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1={:#010X}, unk_param2={:#010X}", unk_param1,
unk_param2);
}
void Module::Interface::GetNsDataAdditionalInfo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 unk_param1 = rp.Pop<u32>();
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // stub 0 (32bit value)
LOG_WARNING(Service_BOSS, "(STUBBED) unk_param1={:#010X}", unk_param1);
}
void Module::Interface::SetNsDataNewFlag(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 ns_data_id = rp.Pop<u32>();
ns_data_new_flag = rp.Pop<u8>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_BOSS, "(STUBBED) ns_data_id={:#010X}, ns_data_new_flag={:#04X}", ns_data_id,
ns_data_new_flag);
}
void Module::Interface::GetNsDataNewFlag(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 ns_data_id = rp.Pop<u32>();
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(ns_data_new_flag);
LOG_WARNING(Service_BOSS, "(STUBBED) ns_data_id={:#010X}, ns_data_new_flag={:#04X}", ns_data_id,
ns_data_new_flag);
}
void Module::Interface::GetNsDataLastUpdate(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 ns_data_id = rp.Pop<u32>();
u32 last_update = 0;
std::optional<NsDataEntry> entry = util.GetNsDataEntryFromID(ns_data_id);
if (entry.has_value()) {
last_update = entry->header.download_date;
LOG_DEBUG(Service_BOSS, "Last update: {}", last_update);
}
IPC::RequestBuilder rb = rp.MakeBuilder(3, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
rb.Push<u32>(last_update); // return the download date from the ns data
LOG_DEBUG(Service_BOSS, "ns_data_id={:#010X}", ns_data_id);
}
void Module::Interface::GetErrorCode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u8 input = rp.Pop<u8>();
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); /// output value
LOG_WARNING(Service_BOSS, "(STUBBED) input={:#010X}", input);
}
void Module::Interface::RegisterStorageEntry(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 unk_param1 = rp.Pop<u32>();
const u32 unk_param2 = rp.Pop<u32>();
const u32 unk_param3 = rp.Pop<u32>();
const u32 unk_param4 = rp.Pop<u32>();
const u8 unk_param5 = rp.Pop<u8>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_BOSS,
"(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);
}
void Module::Interface::GetStorageEntryInfo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(3, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // stub 0 (32bit value)
rb.Push<u16>(0); // stub 0 (16bit value)
LOG_WARNING(Service_BOSS, "(STUBBED) called");
}
void Module::Interface::SetStorageOption(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u8 unk_param1 = rp.Pop<u8>();
const u32 unk_param2 = rp.Pop<u32>();
const u16 unk_param3 = rp.Pop<u16>();
const u16 unk_param4 = rp.Pop<u16>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_BOSS,
"(STUBBED) unk_param1={:#04X}, unk_param2={:#010X}, "
"unk_param3={:#08X}, unk_param4={:#08X}",
unk_param1, unk_param2, unk_param3, unk_param4);
}
void Module::Interface::GetStorageOption(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
IPC::RequestBuilder rb = rp.MakeBuilder(5, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // stub 0 (32bit value)
rb.Push<u8>(0); // stub 0 (8bit value)
rb.Push<u16>(0); // stub 0 (16bit value)
rb.Push<u16>(0); // stub 0 (16bit value)
LOG_WARNING(Service_BOSS, "(STUBBED) called");
}
void Module::Interface::StartBgImmediate(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 size = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(RESULT_SUCCESS);
rb.PushMappedBuffer(buffer);
LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}", size);
}
void Module::Interface::GetTaskProperty0(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 size = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer();
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(0); /// current state of PropertyID 0x0 stub 0 (8bit value)
rb.PushMappedBuffer(buffer);
LOG_WARNING(Service_BOSS, "(STUBBED) size={:#010X}", size);
}
void Module::Interface::RegisterImmediateTask(Kernel::HLERequestContext& ctx) {
RegisterTask(ctx);
LOG_DEBUG(Service_BOSS, "called");
}
void Module::Interface::SetTaskQuery(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 buffer1_size = rp.Pop<u32>();
const u32 buffer2_size = rp.Pop<u32>();
auto& buffer1 = rp.PopMappedBuffer();
auto& buffer2 = rp.PopMappedBuffer();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 4);
rb.Push(RESULT_SUCCESS);
rb.PushMappedBuffer(buffer1);
rb.PushMappedBuffer(buffer2);
LOG_WARNING(Service_BOSS, "(STUBBED) buffer1_size={:#010X}, buffer2_size={:#010X}",
buffer1_size, buffer2_size);
}
void Module::Interface::GetTaskQuery(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u32 buffer1_size = rp.Pop<u32>();
const u32 buffer2_size = rp.Pop<u32>();
auto& buffer1 = rp.PopMappedBuffer();
auto& buffer2 = rp.PopMappedBuffer();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 4);
rb.Push(RESULT_SUCCESS);
rb.PushMappedBuffer(buffer1);
rb.PushMappedBuffer(buffer2);
LOG_WARNING(Service_BOSS, "(STUBBED) buffer1_size={:#010X}, buffer2_size={:#010X}",
buffer1_size, buffer2_size);
}
void Module::Interface::InitializeSessionPrivileged(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u64 programID = rp.Pop<u64>();
rp.PopPID();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_BOSS, "(STUBBED) programID={:#018X}", programID);
}
void Module::Interface::GetAppNewFlag(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u64 programID = rp.Pop<u64>();
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(0); // 0 = nothing new, 1 = new content
LOG_WARNING(Service_BOSS, "(STUBBED) programID={:#018X}", programID);
}
void Module::Interface::GetNsDataIdListPrivileged(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u64 programID = rp.Pop<u64>();
const u32 filter = rp.Pop<u32>();
const u32 max_entries = rp.Pop<u32>(); /// buffer size in words
const u16 word_index_start = rp.Pop<u16>();
const u32 start_ns_data_id = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer();
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.PushMappedBuffer(buffer);
LOG_WARNING(Service_BOSS,
"(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);
}
void Module::Interface::GetNsDataIdListPrivileged1(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u64 programID = rp.Pop<u64>();
const u32 filter = rp.Pop<u32>();
const u32 max_entries = rp.Pop<u32>(); /// buffer size in words
const u16 word_index_start = rp.Pop<u16>();
const u32 start_ns_data_id = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer();
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.PushMappedBuffer(buffer);
LOG_WARNING(Service_BOSS,
"(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);
}
void Module::Interface::SendPropertyPrivileged(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u16 property_id = rp.Pop<u16>();
const u32 size = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(RESULT_SUCCESS);
rb.PushMappedBuffer(buffer);
LOG_WARNING(Service_BOSS, "(STUBBED) property_id={:#06X}, size={:#010X}", property_id, size);
}
void Module::Interface::DeleteNsDataPrivileged(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u64 programID = rp.Pop<u64>();
const u32 ns_data_id = rp.Pop<u32>();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_BOSS, "(STUBBED) programID={:#018X}, ns_data_id={:#010X}", programID,
ns_data_id);
}
void Module::Interface::GetNsDataHeaderInfoPrivileged(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u64 programID = rp.Pop<u64>();
const u32 ns_data_id = rp.Pop<u32>();
const u8 type = rp.Pop<u8>();
const u32 size = rp.Pop<u32>();
auto& buffer = rp.PopMappedBuffer();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(RESULT_SUCCESS);
rb.PushMappedBuffer(buffer);
LOG_WARNING(Service_BOSS,
"(STUBBED) programID={:#018X} ns_data_id={:#010X}, type={:#04X}, size={:#010X}",
programID, ns_data_id, type, size);
}
void Module::Interface::ReadNsDataPrivileged(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u64 programID = rp.Pop<u64>();
const u32 ns_data_id = rp.Pop<u32>();
const u64 offset = rp.Pop<u64>();
const u32 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);
LOG_WARNING(Service_BOSS,
"(STUBBED) programID={:#018X}, ns_data_id={:#010X}, offset={:#018X}, size={:#010X}",
programID, ns_data_id, offset, size);
}
void Module::Interface::SetNsDataNewFlagPrivileged(Kernel::HLERequestContext& ctx) {
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>();
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);
}
void Module::Interface::GetNsDataNewFlagPrivileged(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx);
const u64 programID = rp.Pop<u64>();
const u32 unk_param1 = rp.Pop<u32>();
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(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);
}
Module::Interface::Interface(std::shared_ptr<Module> boss, const char* name, u32 max_session)
: ServiceFramework(name, max_session), boss(std::move(boss)) {
util = Util();
this->boss->loader.ReadProgramId(util.program_id);
this->boss->loader.ReadExtdataId(util.extdata_id);
}
Module::Module(Core::System& system) : loader(system.GetAppLoader()) {
using namespace Kernel;
// TODO: verify ResetType
task_finish_event =
system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "BOSS::task_finish_event");
}
void InstallInterfaces(Core::System& system) {
auto& service_manager = system.ServiceManager();
auto boss = std::make_shared<Module>(system);
std::make_shared<BOSS_P>(boss)->InstallAsService(service_manager);
std::make_shared<BOSS_U>(boss)->InstallAsService(service_manager);
}
} // namespace Service::BOSS