diff --git a/src/core/hle/service/cecd/cecd.cpp b/src/core/hle/service/cecd/cecd.cpp index dcf9a8832..0dc801a39 100644 --- a/src/core/hle/service/cecd/cecd.cpp +++ b/src/core/hle/service/cecd/cecd.cpp @@ -2,7 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/file_util.h" #include "common/logging/log.h" +#include "common/string_util.h" +#include "core/file_sys/archive_systemsavedata.h" +#include "core/file_sys/errors.h" +#include "core/file_sys/file_backend.h" #include "core/hle/ipc_helpers.h" #include "core/hle/result.h" #include "core/hle/service/cecd/cecd.h" @@ -16,7 +21,7 @@ namespace CECD { void Module::Interface::OpenRawFile(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x01, 3, 2); const u32 ncch_program_id = rp.Pop(); - const u32 path_type = rp.Pop(); + const auto path_type = static_cast(rp.Pop()); const u32 file_open_flag = rp.Pop(); rp.PopPID(); @@ -27,18 +32,18 @@ void Module::Interface::OpenRawFile(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_CECD, "(STUBBED) called, ncch_program_id={:#010x}, path_type={:#010x}, " "file_open_flag={:#010x}", - ncch_program_id, path_type, file_open_flag); + ncch_program_id, static_cast(path_type), file_open_flag); } void Module::Interface::ReadRawFile(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x02, 1, 2); const u32 buffer_size = rp.Pop(); - auto buffer = rp.PopStaticBuffer(); + auto& buffer = rp.PopMappedBuffer(); IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); rb.Push(RESULT_SUCCESS); rb.Push(0); /// Read size - rb.PushStaticBuffer(buffer, 0); + rb.PushMappedBuffer(buffer); LOG_WARNING(Service_CECD, "(STUBBED) called, buffer_size={:#010x}", buffer_size); } @@ -49,14 +54,14 @@ void Module::Interface::ReadMessage(Kernel::HLERequestContext& ctx) { const bool is_out_box = rp.Pop(); const u32 message_id_size = rp.Pop(); const u32 buffer_size = rp.Pop(); - const auto message_id_buffer = rp.PopStaticBuffer(); - auto write_buffer = rp.PopStaticBuffer(); + const auto& message_id_buffer = rp.PopMappedBuffer(); + auto& write_buffer = rp.PopMappedBuffer(); IPC::RequestBuilder rb = rp.MakeBuilder(2, 4); rb.Push(RESULT_SUCCESS); rb.Push(0); /// Read size - rb.PushStaticBuffer(message_id_buffer, 0); - rb.PushStaticBuffer(write_buffer, 1); + rb.PushMappedBuffer(message_id_buffer); + rb.PushMappedBuffer(write_buffer); LOG_WARNING(Service_CECD, "(STUBBED) called, ncch_program_id={:#010x}, is_out_box={}", ncch_program_id, is_out_box); @@ -68,16 +73,16 @@ void Module::Interface::ReadMessageWithHMAC(Kernel::HLERequestContext& ctx) { const bool is_out_box = rp.Pop(); const u32 message_id_size = rp.Pop(); const u32 buffer_size = rp.Pop(); - const auto message_id_buffer = rp.PopStaticBuffer(); - const auto hmac_key_buffer = rp.PopStaticBuffer(); - auto write_buffer = rp.PopStaticBuffer(); + const auto& message_id_buffer = rp.PopMappedBuffer(); + const auto& hmac_key_buffer = rp.PopMappedBuffer(); + auto& write_buffer = rp.PopMappedBuffer(); IPC::RequestBuilder rb = rp.MakeBuilder(2, 6); rb.Push(RESULT_SUCCESS); rb.Push(0); /// Read size - rb.PushStaticBuffer(message_id_buffer, 0); - rb.PushStaticBuffer(hmac_key_buffer, 1); - rb.PushStaticBuffer(write_buffer, 2); + rb.PushMappedBuffer(message_id_buffer); + rb.PushMappedBuffer(hmac_key_buffer); + rb.PushMappedBuffer(write_buffer); LOG_WARNING(Service_CECD, "(STUBBED) called, ncch_program_id={:#010x}, is_out_box={}", ncch_program_id, is_out_box); @@ -86,11 +91,11 @@ void Module::Interface::ReadMessageWithHMAC(Kernel::HLERequestContext& ctx) { void Module::Interface::WriteRawFile(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x05, 1, 2); const u32 buffer_size = rp.Pop(); - const auto buffer = rp.PopStaticBuffer(); + const auto& buffer = rp.PopMappedBuffer(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(RESULT_SUCCESS); - rb.PushStaticBuffer(buffer, 0); + rb.PushMappedBuffer(buffer); LOG_WARNING(Service_CECD, "(STUBBED) called, buffer_size={:#010x}", buffer_size); } @@ -101,13 +106,13 @@ void Module::Interface::WriteMessage(Kernel::HLERequestContext& ctx) { const bool is_out_box = rp.Pop(); const u32 message_id_size = rp.Pop(); const u32 buffer_size = rp.Pop(); - const auto read_buffer = rp.PopStaticBuffer(); - auto message_id_buffer = rp.PopStaticBuffer(); + const auto& read_buffer = rp.PopMappedBuffer(); + auto& message_id_buffer = rp.PopMappedBuffer(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 4); rb.Push(RESULT_SUCCESS); - rb.PushStaticBuffer(read_buffer, 0); - rb.PushStaticBuffer(message_id_buffer, 1); + rb.PushMappedBuffer(read_buffer); + rb.PushMappedBuffer(message_id_buffer); LOG_WARNING(Service_CECD, "(STUBBED) called, ncch_program_id={:#010x}, is_out_box={}", ncch_program_id, is_out_box); @@ -119,15 +124,15 @@ void Module::Interface::WriteMessageWithHMAC(Kernel::HLERequestContext& ctx) { const bool is_out_box = rp.Pop(); const u32 message_id_size = rp.Pop(); const u32 buffer_size = rp.Pop(); - const auto read_buffer = rp.PopStaticBuffer(); - const auto hmac_key_buffer = rp.PopStaticBuffer(); - auto message_id_buffer = rp.PopStaticBuffer(); + const auto& read_buffer = rp.PopMappedBuffer(); + const auto& hmac_key_buffer = rp.PopMappedBuffer(); + auto& message_id_buffer = rp.PopMappedBuffer(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 6); rb.Push(RESULT_SUCCESS); - rb.PushStaticBuffer(read_buffer, 0); - rb.PushStaticBuffer(hmac_key_buffer, 1); - rb.PushStaticBuffer(message_id_buffer, 2); + rb.PushMappedBuffer(read_buffer); + rb.PushMappedBuffer(hmac_key_buffer); + rb.PushMappedBuffer(message_id_buffer); LOG_WARNING(Service_CECD, "(STUBBED) called, ncch_program_id={:#010x}, is_out_box={}", ncch_program_id, is_out_box); @@ -136,18 +141,18 @@ void Module::Interface::WriteMessageWithHMAC(Kernel::HLERequestContext& ctx) { void Module::Interface::Delete(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x10, 4, 2); const u32 ncch_program_id = rp.Pop(); - const u32 path_type = rp.Pop(); + const auto path_type = static_cast(rp.Pop()); const bool is_out_box = rp.Pop(); const u32 message_id_size = rp.Pop(); - const auto message_id_buffer = rp.PopStaticBuffer(); + const auto& message_id_buffer = rp.PopMappedBuffer(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(RESULT_SUCCESS); - rb.PushStaticBuffer(message_id_buffer, 0); + rb.PushMappedBuffer(message_id_buffer); LOG_WARNING(Service_CECD, "(STUBBED) called, ncch_program_id={:#010x}, path_type={:#010x}, is_out_box={}", - ncch_program_id, path_type, is_out_box); + ncch_program_id, static_cast(path_type), is_out_box); } void Module::Interface::GetSystemInfo(Kernel::HLERequestContext& ctx) { @@ -155,13 +160,13 @@ void Module::Interface::GetSystemInfo(Kernel::HLERequestContext& ctx) { const u32 dest_buffer_size = rp.Pop(); const u32 info_type = rp.Pop(); const u32 param_buffer_size = rp.Pop(); - const auto param_buffer = rp.PopStaticBuffer(); - auto dest_buffer = rp.PopStaticBuffer(); + const auto& param_buffer = rp.PopMappedBuffer(); + auto& dest_buffer = rp.PopMappedBuffer(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 4); rb.Push(RESULT_SUCCESS); - rb.PushStaticBuffer(param_buffer, 0); - rb.PushStaticBuffer(dest_buffer, 1); + rb.PushMappedBuffer(param_buffer); + rb.PushMappedBuffer(dest_buffer); LOG_WARNING(Service_CECD, "(STUBBED) called, dest_buffer_size={:#010x}, info_type={:#010x}, " @@ -203,38 +208,109 @@ void Module::Interface::OpenAndWrite(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x11, 4, 4); const u32 buffer_size = rp.Pop(); const u32 ncch_program_id = rp.Pop(); - const u32 path_type = rp.Pop(); + const auto path_type = static_cast(rp.Pop()); const u32 file_open_flag = rp.Pop(); rp.PopPID(); - const auto read_buffer = rp.PopStaticBuffer(); + auto& read_buffer = rp.PopMappedBuffer(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); - rb.Push(RESULT_SUCCESS); - rb.PushStaticBuffer(read_buffer, 0); + + FileSys::Path path(cecd->GetCecDataPathTypeAsString(path_type, ncch_program_id).data()); + FileSys::Mode write_mode = {}; + write_mode.create_flag.Assign(1); + write_mode.write_flag.Assign(1); + + auto file_result = Service::FS::OpenFileFromArchive(cecd->cecd_system_save_data_archive, path, + write_mode); + if (file_result.Succeeded()) { + std::vector buffer(buffer_size); + read_buffer.Read(buffer.data(), 0, buffer_size); + const u32 bytes_written = + file_result.Unwrap()->backend->Write(0, buffer_size, true, buffer.data()).Unwrap(); + + rb.Push(RESULT_SUCCESS); + } else { + rb.Push(ResultCode(ErrorDescription::NoData, ErrorModule::CEC, ErrorSummary::NotFound, + ErrorLevel::Status)); + } + rb.PushMappedBuffer(read_buffer); LOG_WARNING(Service_CECD, "(STUBBED) called, ncch_program_id={:#010x}, path_type={:#010x}, " "file_open_flag={:#010x}", - ncch_program_id, path_type, file_open_flag); + ncch_program_id, static_cast(path_type), file_open_flag); } void Module::Interface::OpenAndRead(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x12, 4, 4); const u32 buffer_size = rp.Pop(); const u32 ncch_program_id = rp.Pop(); - const u32 path_type = rp.Pop(); + const auto path_type = static_cast(rp.Pop()); const u32 file_open_flag = rp.Pop(); rp.PopPID(); - auto write_buffer = rp.PopStaticBuffer(); + auto& write_buffer = rp.PopMappedBuffer(); IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); - rb.Push(RESULT_SUCCESS); - rb.PushStaticBuffer(write_buffer, 0); + + FileSys::Path path(cecd->GetCecDataPathTypeAsString(path_type, ncch_program_id).data()); + FileSys::Mode read_mode = {}; + read_mode.read_flag.Assign(1); + + auto file_result = Service::FS::OpenFileFromArchive(cecd->cecd_system_save_data_archive, path, + read_mode); + if (file_result.Succeeded()) { + std::vector buffer(buffer_size); + const u32 bytes_read = + file_result.Unwrap()->backend->Read(0, buffer_size, buffer.data()).Unwrap(); + write_buffer.Write(buffer.data(), 0, buffer_size); + + rb.Push(RESULT_SUCCESS); + rb.Push(bytes_read); + } else { + rb.Push(ResultCode(ErrorDescription::NoData, ErrorModule::CEC, ErrorSummary::NotFound, + ErrorLevel::Status)); + rb.Push(0); /// No bytes read + } + rb.PushMappedBuffer(write_buffer); LOG_WARNING(Service_CECD, "(STUBBED) called, ncch_program_id={:#010x}, path_type={:#010x}, " "file_open_flag={:#010x}", - ncch_program_id, path_type, file_open_flag); + ncch_program_id, static_cast(path_type), file_open_flag); +} + +std::string Module::GetCecDataPathTypeAsString(const CecDataPathType type, const u32 program_id, + const std::vector& msg_id) { + switch(type) { + case CecDataPathType::CEC_PATH_MBOX_LIST: + return "/CEC/MBoxList____"; + case CecDataPathType::CEC_PATH_MBOX_INFO: + return Common::StringFromFormat("/CEC/%08x/MBoxInfo____", program_id); + case CecDataPathType::CEC_PATH_INBOX_INFO: + return Common::StringFromFormat("/CEC/%08x/InBox___/BoxInfo_____", program_id); + case CecDataPathType::CEC_PATH_OUTBOX_INFO: + return Common::StringFromFormat("/CEC/%08x/OutBox__/BoxInfo_____", program_id); + case CecDataPathType::CEC_PATH_OUTBOX_INDEX: + return Common::StringFromFormat("/CEC/%08x/OutBox__/OBIndex_____", program_id); + case CecDataPathType::CEC_PATH_INBOX_MSG: + return Common::StringFromFormat("/CEC/%08x/InBox___/_%08x", program_id, msg_id.data()); + case CecDataPathType::CEC_PATH_OUTBOX_MSG: + return Common::StringFromFormat("/CEC/%08x/OutBox__/_%08x", program_id, msg_id.data()); + case CecDataPathType::CEC_PATH_ROOT_DIR: + return "/CEC"; + case CecDataPathType::CEC_PATH_MBOX_DIR: + return Common::StringFromFormat("/CEC/%08x", program_id); + case CecDataPathType::CEC_PATH_INBOX_DIR: + return Common::StringFromFormat("/CEC/%08x/InBox___", program_id); + case CecDataPathType::CEC_PATH_OUTBOX_DIR: + return Common::StringFromFormat("/CEC/%08x/OutBox__", program_id); + case CecDataPathType::CEC_MBOX_DATA: + case CecDataPathType::CEC_MBOX_ICON: + case CecDataPathType::CEC_MBOX_TITLE: + default: + return Common::StringFromFormat("/CEC/%08x/MBoxData.%03d", program_id, + static_cast(type) - 100); + } } Module::Interface::Interface(std::shared_ptr cecd, const char* name, u32 max_session) @@ -244,6 +320,32 @@ Module::Module() { using namespace Kernel; cecinfo_event = Event::Create(Kernel::ResetType::OneShot, "CECD::cecinfo_event"); change_state_event = Event::Create(Kernel::ResetType::OneShot, "CECD::change_state_event"); + + // Open the SystemSaveData archive 0x00010026 + FileSys::Path archive_path(cecd_system_savedata_id); + auto archive_result = + Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path); + + // If the archive didn't exist, create the files inside + if (archive_result.Code() == FileSys::ERR_NOT_FORMATTED) { + // Format the archive to create the directories + Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SystemSaveData, + FileSys::ArchiveFormatInfo(), archive_path); + + // Open it again to get a valid archive now that the folder exists + archive_result = + Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path); + } + + ASSERT_MSG(archive_result.Succeeded(), "Could not open the CECD SystemSaveData archive!"); + + cecd_system_save_data_archive = *archive_result; +} + +Module::~Module() { + if (cecd_system_save_data_archive) { + Service::FS::CloseArchive(cecd_system_save_data_archive); + } } void InstallInterfaces(SM::ServiceManager& service_manager) { diff --git a/src/core/hle/service/cecd/cecd.h b/src/core/hle/service/cecd/cecd.h index 707cc9ca3..ec7fb0a7e 100644 --- a/src/core/hle/service/cecd/cecd.h +++ b/src/core/hle/service/cecd/cecd.h @@ -6,6 +6,7 @@ #include "core/hle/kernel/event.h" #include "core/hle/service/service.h" +#include "core/hle/service/fs/archive.h" namespace Service { namespace CECD { @@ -13,7 +14,7 @@ namespace CECD { class Module final { public: Module(); - ~Module() = default; + ~Module(); enum class CecStateAbbreviated : u32 { CEC_STATE_ABBREV_IDLE = 1, /// Relates to CEC_STATE_IDLE @@ -48,6 +49,23 @@ public: CEC_COMMAND_END = 0x15, }; + enum class CecDataPathType : u32 { + CEC_PATH_MBOX_LIST = 1, + CEC_PATH_MBOX_INFO = 2, + CEC_PATH_INBOX_INFO = 3, + CEC_PATH_OUTBOX_INFO = 4, + CEC_PATH_OUTBOX_INDEX = 5, + CEC_PATH_INBOX_MSG = 6, + CEC_PATH_OUTBOX_MSG = 7, + CEC_PATH_ROOT_DIR = 10, + CEC_PATH_MBOX_DIR = 11, + CEC_PATH_INBOX_DIR = 12, + CEC_PATH_OUTBOX_DIR = 13, + CEC_MBOX_DATA = 100, + CEC_MBOX_ICON = 101, + CEC_MBOX_TITLE = 110, + }; + class Interface : public ServiceFramework { public: Interface(std::shared_ptr cecd, const char* name, u32 max_session); @@ -361,6 +379,15 @@ public: }; private: + std::string GetCecDataPathTypeAsString(const CecDataPathType type, const u32 program_id, + const std::vector& message_id = std::vector()); + + const std::vector cecd_system_savedata_id = { + 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x01, 0x00 + }; + + Service::FS::ArchiveHandle cecd_system_save_data_archive; + Kernel::SharedPtr cecinfo_event; Kernel::SharedPtr change_state_event; };