mirror of
https://github.com/PabloMK7/citra.git
synced 2024-11-30 03:10:16 +00:00
service/cecd: Implement mboxlist handling
This commit is contained in:
parent
0e8398b51c
commit
7a8477d106
2 changed files with 123 additions and 31 deletions
|
@ -67,9 +67,17 @@ void Module::Interface::Open(Kernel::HLERequestContext& ctx) {
|
||||||
}
|
}
|
||||||
rb.Push<u32>(0); /// Zero entries
|
rb.Push<u32>(0); /// Zero entries
|
||||||
} else {
|
} else {
|
||||||
|
constexpr u32 max_entries = 32; /// reasonable value, just over max boxes 24
|
||||||
auto directory = dir_result.Unwrap();
|
auto directory = dir_result.Unwrap();
|
||||||
|
|
||||||
|
/// Actual reading into vector seems to be required for entry count
|
||||||
|
std::vector<FileSys::Entry> entries(max_entries);
|
||||||
|
const u32 entry_count = directory->backend->Read(max_entries, entries.data());
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_CECD, "Number of entries found in = {}", entry_count);
|
||||||
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.Push<u32>(directory->backend->Read(0, nullptr)); /// Entry count
|
rb.Push<u32>(entry_count); /// Entry count
|
||||||
directory->backend->Close();
|
directory->backend->Close();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -423,19 +431,49 @@ void Module::Interface::Delete(Kernel::HLERequestContext& ctx) { /// DeleteMessa
|
||||||
const u32 message_id_size = rp.Pop<u32>();
|
const u32 message_id_size = rp.Pop<u32>();
|
||||||
auto& message_id_buffer = rp.PopMappedBuffer();
|
auto& message_id_buffer = rp.PopMappedBuffer();
|
||||||
|
|
||||||
if (is_outbox) {
|
FileSys::Path path(cecd->GetCecDataPathTypeAsString(path_type, ncch_program_id).data());
|
||||||
|
FileSys::Mode mode;
|
||||||
} else { /// otherwise inbox
|
mode.write_flag.Assign(1);
|
||||||
}
|
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||||
rb.Push(RESULT_SUCCESS);
|
switch (path_type) {
|
||||||
|
case CecDataPathType::CEC_PATH_ROOT_DIR:
|
||||||
|
case CecDataPathType::CEC_PATH_MBOX_DIR:
|
||||||
|
case CecDataPathType::CEC_PATH_INBOX_DIR:
|
||||||
|
case CecDataPathType::CEC_PATH_OUTBOX_DIR:
|
||||||
|
rb.Push(Service::FS::DeleteDirectoryRecursivelyFromArchive(
|
||||||
|
cecd->cecd_system_save_data_archive, path));
|
||||||
|
break;
|
||||||
|
default: /// If not directory, then it is a file
|
||||||
|
if (message_id_size == 0) {
|
||||||
|
rb.Push(Service::FS::DeleteFileFromArchive(cecd->cecd_system_save_data_archive, path));
|
||||||
|
} else {
|
||||||
|
std::vector<u8> id_buffer(message_id_size);
|
||||||
|
message_id_buffer.Read(id_buffer.data(), 0, message_id_size);
|
||||||
|
|
||||||
|
FileSys::Path message_path;
|
||||||
|
if (is_outbox) {
|
||||||
|
message_path =
|
||||||
|
cecd->GetCecDataPathTypeAsString(CecDataPathType::CEC_PATH_OUTBOX_MSG,
|
||||||
|
ncch_program_id, id_buffer)
|
||||||
|
.data();
|
||||||
|
} else { /// otherwise inbox
|
||||||
|
message_path = cecd->GetCecDataPathTypeAsString(CecDataPathType::CEC_PATH_INBOX_MSG,
|
||||||
|
ncch_program_id, id_buffer)
|
||||||
|
.data();
|
||||||
|
}
|
||||||
|
rb.Push(Service::FS::DeleteFileFromArchive(cecd->cecd_system_save_data_archive,
|
||||||
|
message_path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rb.PushMappedBuffer(message_id_buffer);
|
rb.PushMappedBuffer(message_id_buffer);
|
||||||
|
|
||||||
LOG_DEBUG(Service_CECD,
|
LOG_DEBUG(Service_CECD,
|
||||||
"called, ncch_program_id={:#010x}, path_type={:#04x}, "
|
"called, ncch_program_id={:#010x}, path_type={:#04x}, path={}, "
|
||||||
"is_outbox={}, message_id_size={:#x}",
|
"is_outbox={}, message_id_size={:#x}",
|
||||||
ncch_program_id, static_cast<u32>(path_type), is_outbox, message_id_size);
|
ncch_program_id, static_cast<u32>(path_type), path.AsString(), is_outbox,
|
||||||
|
message_id_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::Cecd_0x000900C2(Kernel::HLERequestContext& ctx) { /// Update Index/List?
|
void Module::Interface::Cecd_0x000900C2(Kernel::HLERequestContext& ctx) { /// Update Index/List?
|
||||||
|
@ -798,25 +836,28 @@ std::string Module::GetCecCommandAsString(const CecCommand command) const {
|
||||||
|
|
||||||
void Module::CheckAndUpdateFile(const CecDataPathType path_type, const u32 ncch_program_id,
|
void Module::CheckAndUpdateFile(const CecDataPathType path_type, const u32 ncch_program_id,
|
||||||
std::vector<u8>& file_buffer) {
|
std::vector<u8>& file_buffer) {
|
||||||
|
constexpr u32 max_num_boxes = 24;
|
||||||
|
constexpr u32 name_size = 16; /// fixed size 16 characters long
|
||||||
|
constexpr u32 valid_name_size = 8; /// 8 characters are valid, the rest are null
|
||||||
const u32 file_size = file_buffer.size();
|
const u32 file_size = file_buffer.size();
|
||||||
|
|
||||||
switch (path_type) {
|
switch (path_type) {
|
||||||
case CecDataPathType::CEC_PATH_MBOX_LIST: {
|
case CecDataPathType::CEC_PATH_MBOX_LIST: {
|
||||||
CecMBoxListHeader mbox_list_header = {};
|
CecMBoxListHeader mbox_list_header = {};
|
||||||
CecMBoxListBoxes mbox_list_boxes = {};
|
|
||||||
std::memcpy(&mbox_list_header, file_buffer.data(), sizeof(CecMBoxListHeader));
|
std::memcpy(&mbox_list_header, file_buffer.data(), sizeof(CecMBoxListHeader));
|
||||||
std::memcpy(&mbox_list_boxes, &file_buffer[0xC], sizeof(CecMBoxListBoxes));
|
|
||||||
|
|
||||||
if (file_size != 0x18C) { /// 0x18C CecMBoxListHeader + CecMBoxListBoxes
|
if (file_size != sizeof(CecMBoxListHeader)) { /// 0x18C
|
||||||
LOG_DEBUG(Service_CECD, "CecMBoxListHeader size is incorrect: {}", file_size);
|
LOG_DEBUG(Service_CECD, "CecMBoxListHeader size is incorrect: {}", file_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mbox_list_header.magic != 0x6868) { /// 'hh'
|
if (mbox_list_header.magic != 0x6868) { /// 'hh'
|
||||||
if (mbox_list_header.magic == 0)
|
if (mbox_list_header.magic == 0 || mbox_list_header.magic == 0xFFFF) {
|
||||||
LOG_DEBUG(Service_CECD, "CecMBoxListHeader magic number is not set");
|
LOG_DEBUG(Service_CECD, "CecMBoxListHeader magic number is not set");
|
||||||
else
|
} else {
|
||||||
LOG_DEBUG(Service_CECD, "CecMBoxListHeader magic number is incorrect: {}",
|
LOG_DEBUG(Service_CECD, "CecMBoxListHeader magic number is incorrect: {}",
|
||||||
mbox_list_header.magic);
|
mbox_list_header.magic);
|
||||||
|
}
|
||||||
|
std::memset(&mbox_list_header, 0, sizeof(CecMBoxListHeader));
|
||||||
mbox_list_header.magic = 0x6868;
|
mbox_list_header.magic = 0x6868;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -832,10 +873,69 @@ void Module::CheckAndUpdateFile(const CecDataPathType path_type, const u32 ncch_
|
||||||
if (mbox_list_header.num_boxes > 24) {
|
if (mbox_list_header.num_boxes > 24) {
|
||||||
LOG_DEBUG(Service_CECD, "CecMBoxListHeader number of boxes is too large: {}",
|
LOG_DEBUG(Service_CECD, "CecMBoxListHeader number of boxes is too large: {}",
|
||||||
mbox_list_header.num_boxes);
|
mbox_list_header.num_boxes);
|
||||||
}
|
} else {
|
||||||
|
std::vector<u8> name_buffer(name_size);
|
||||||
|
std::memset(name_buffer.data(), 0, name_size);
|
||||||
|
|
||||||
|
if (ncch_program_id != 0) {
|
||||||
|
std::string name = Common::StringFromFormat("%08x", ncch_program_id);
|
||||||
|
std::memcpy(name_buffer.data(), name.data(), name.size());
|
||||||
|
|
||||||
|
bool already_activated = false;
|
||||||
|
for (auto i = 0; i < mbox_list_header.num_boxes; i++) {
|
||||||
|
LOG_DEBUG(Service_CECD, "{}", i);
|
||||||
|
/// Box names start at offset 0xC, are 16 char long, first 8 id, last 8 null
|
||||||
|
if (std::memcmp(name_buffer.data(), &mbox_list_header.box_names[i * name_size],
|
||||||
|
valid_name_size) == 0) {
|
||||||
|
LOG_DEBUG(Service_CECD, "Title already activated");
|
||||||
|
already_activated = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!already_activated) {
|
||||||
|
if (mbox_list_header.num_boxes < max_num_boxes) { /// max boxes
|
||||||
|
LOG_DEBUG(Service_CECD, "Adding title to mboxlist____: {}", name);
|
||||||
|
std::memcpy(
|
||||||
|
&mbox_list_header.box_names[mbox_list_header.num_boxes * name_size],
|
||||||
|
name_buffer.data(), name_size);
|
||||||
|
mbox_list_header.num_boxes++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { /// ncch_program_id == 0, remove/update activated boxes
|
||||||
|
/// We need to read the /CEC directory to find out which titles, if any,
|
||||||
|
/// are activated. The num_of_titles = (total_read_count) - 1, to adjust for
|
||||||
|
/// the MBoxList____ file that is present in the directory as well.
|
||||||
|
FileSys::Path root_path(
|
||||||
|
GetCecDataPathTypeAsString(CecDataPathType::CEC_PATH_ROOT_DIR, 0).data());
|
||||||
|
|
||||||
|
auto dir_result =
|
||||||
|
Service::FS::OpenDirectoryFromArchive(cecd_system_save_data_archive, root_path);
|
||||||
|
|
||||||
|
auto root_dir = dir_result.Unwrap();
|
||||||
|
std::vector<FileSys::Entry> entries(max_num_boxes + 1); // + 1 mboxlist
|
||||||
|
const u32 entry_count = root_dir->backend->Read(max_num_boxes + 1, entries.data());
|
||||||
|
root_dir->backend->Close();
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_CECD, "Number of entries found in /CEC = {}", entry_count);
|
||||||
|
|
||||||
|
std::string mbox_list_name("MBoxList____");
|
||||||
|
std::string file_name;
|
||||||
|
std::u16string u16_filename;
|
||||||
|
|
||||||
|
/// Loop through entries but don't add mboxlist____ to itself.
|
||||||
|
for (auto i = 0; i < entry_count; i++) {
|
||||||
|
u16_filename = std::u16string(entries[i].filename);
|
||||||
|
file_name = Common::UTF16ToUTF8(u16_filename);
|
||||||
|
|
||||||
|
if (mbox_list_name.compare(file_name) != 0) {
|
||||||
|
LOG_DEBUG(Service_CECD, "Adding title to mboxlist____: {}", file_name);
|
||||||
|
std::memcpy(&mbox_list_header.box_names[16 * mbox_list_header.num_boxes++],
|
||||||
|
file_name.data(), valid_name_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
std::memcpy(file_buffer.data(), &mbox_list_header, sizeof(CecMBoxListHeader));
|
std::memcpy(file_buffer.data(), &mbox_list_header, sizeof(CecMBoxListHeader));
|
||||||
std::memcpy(&file_buffer[0xC], &mbox_list_boxes, sizeof(CecMBoxListBoxes));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CecDataPathType::CEC_PATH_MBOX_INFO: {
|
case CecDataPathType::CEC_PATH_MBOX_INFO: {
|
||||||
|
|
|
@ -152,24 +152,17 @@ public:
|
||||||
static_assert(sizeof(CecMBoxInfoHeader) == 0x60,
|
static_assert(sizeof(CecMBoxInfoHeader) == 0x60,
|
||||||
"CecMBoxInfoHeader struct has incorrect size.");
|
"CecMBoxInfoHeader struct has incorrect size.");
|
||||||
|
|
||||||
struct CecMBoxListBoxes {
|
|
||||||
struct Box {
|
|
||||||
u32_le ncch_program_id;
|
|
||||||
u32_le padding;
|
|
||||||
u64_le padding2;
|
|
||||||
} box[24];
|
|
||||||
};
|
|
||||||
static_assert(sizeof(CecMBoxListBoxes) == 0x180, "CecMBoxListBoxes struct has incorrect size.");
|
|
||||||
|
|
||||||
struct CecMBoxListHeader {
|
struct CecMBoxListHeader {
|
||||||
u16_le magic; // 0x6868 'hh'
|
u16_le magic; // 0x6868 'hh'
|
||||||
u16_le padding;
|
u16_le padding;
|
||||||
u16_le version; /// 0x01 00, maybe activated flag?
|
u16_le version; /// 0x01 00, maybe activated flag?
|
||||||
u16_le padding2;
|
u16_le padding2;
|
||||||
u16_le num_boxes; /// 24 max?
|
u16_le num_boxes; /// 24 max
|
||||||
u16_le padding3;
|
u16_le padding3;
|
||||||
|
u8 box_names[16 * 24]; /// 16 char names, 24 boxes
|
||||||
};
|
};
|
||||||
static_assert(sizeof(CecMBoxListHeader) == 0xC, "CecMBoxListHeader struct has incorrect size.");
|
static_assert(sizeof(CecMBoxListHeader) == 0x18C,
|
||||||
|
"CecMBoxListHeader struct has incorrect size.");
|
||||||
|
|
||||||
struct CecMessageHeader {
|
struct CecMessageHeader {
|
||||||
u16_le magic; // ``
|
u16_le magic; // ``
|
||||||
|
@ -222,7 +215,7 @@ public:
|
||||||
BitField<1, 1, u32> read; /// 2
|
BitField<1, 1, u32> read; /// 2
|
||||||
BitField<2, 1, u32> write; /// 4
|
BitField<2, 1, u32> write; /// 4
|
||||||
BitField<3, 1, u32> create; /// 8
|
BitField<3, 1, u32> create; /// 8
|
||||||
BitField<4, 1, u32> check; /// 16
|
BitField<4, 1, u32> check; /// 16 maybe validate sig?
|
||||||
BitField<30, 1, u32> unk_flag;
|
BitField<30, 1, u32> unk_flag;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -579,13 +572,13 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::vector<u8> cecd_system_savedata_id = {0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x26, 0x00, 0x01, 0x00};
|
|
||||||
|
|
||||||
/// String used by cecd for base64 encoding found in the sysmodule disassembly
|
/// String used by cecd for base64 encoding found in the sysmodule disassembly
|
||||||
const std::string base64_dict =
|
const std::string base64_dict =
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
|
||||||
|
|
||||||
|
const std::vector<u8> cecd_system_savedata_id = {0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x26, 0x00, 0x01, 0x00};
|
||||||
|
|
||||||
/// Encoding function used for the message id
|
/// Encoding function used for the message id
|
||||||
std::string EncodeBase64(const std::vector<u8>& in, const std::string& dictionary) const;
|
std::string EncodeBase64(const std::vector<u8>& in, const std::string& dictionary) const;
|
||||||
|
|
||||||
|
@ -594,7 +587,6 @@ private:
|
||||||
|
|
||||||
std::string GetCecCommandAsString(const CecCommand command) const;
|
std::string GetCecCommandAsString(const CecCommand command) const;
|
||||||
|
|
||||||
// void CreateAndPopulateMBoxDirectory(const u32 ncch_program_id);
|
|
||||||
void CheckAndUpdateFile(const CecDataPathType path_type, const u32 ncch_program_id,
|
void CheckAndUpdateFile(const CecDataPathType path_type, const u32 ncch_program_id,
|
||||||
std::vector<u8>& file_buffer);
|
std::vector<u8>& file_buffer);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue