diff --git a/src/core/hle/service/cecd/cecd.cpp b/src/core/hle/service/cecd/cecd.cpp index 0dc801a39f..c003e547a3 100644 --- a/src/core/hle/service/cecd/cecd.cpp +++ b/src/core/hle/service/cecd/cecd.cpp @@ -6,9 +6,11 @@ #include "common/logging/log.h" #include "common/string_util.h" #include "core/file_sys/archive_systemsavedata.h" +#include "core/file_sys/directory_backend.h" #include "core/file_sys/errors.h" #include "core/file_sys/file_backend.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/process.h" #include "core/hle/result.h" #include "core/hle/service/cecd/cecd.h" #include "core/hle/service/cecd/cecd_ndm.h" @@ -18,43 +20,124 @@ namespace Service { namespace CECD { +using CecDataPathType = Module::CecDataPathType; +using CecOpenMode = Module::CecOpenMode; +using CecSystemInfoType = Module::CecSystemInfoType; + void Module::Interface::OpenRawFile(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x01, 3, 2); const u32 ncch_program_id = rp.Pop(); const auto path_type = static_cast(rp.Pop()); - const u32 file_open_flag = rp.Pop(); + CecOpenMode open_mode; + open_mode.raw = rp.Pop(); rp.PopPID(); - IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); - rb.Push(RESULT_SUCCESS); - rb.Push(0); /// File size? + FileSys::Path path(cecd->GetCecDataPathTypeAsString(path_type, ncch_program_id).data()); + FileSys::Mode mode; + mode.read_flag.Assign(1); + mode.write_flag.Assign(1); + mode.create_flag.Assign(1); - LOG_WARNING(Service_CECD, - "(STUBBED) called, ncch_program_id={:#010x}, path_type={:#010x}, " - "file_open_flag={:#010x}", - ncch_program_id, static_cast(path_type), file_open_flag); + SessionData* session_data = GetSessionData(ctx.Session()); + session_data->ncch_program_id = ncch_program_id; + session_data->open_mode.raw = open_mode.raw; + session_data->data_path_type = path_type; + session_data->path = path; + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + 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: { + auto dir_result = + Service::FS::OpenDirectoryFromArchive(cecd->cecd_system_save_data_archive, path); + if (dir_result.Failed()) { + if (open_mode.make_dir) { + Service::FS::CreateDirectoryFromArchive(cecd->cecd_system_save_data_archive, path); + rb.Push(RESULT_SUCCESS); + } else { + LOG_ERROR(Service_CECD, "Failed to open directory: {}", path.AsString()); + rb.Push(ResultCode(ErrorDescription::NoData, ErrorModule::CEC, + ErrorSummary::NotFound, ErrorLevel::Status)); + } + rb.Push(0); /// Zero entries + } else { + auto directory = dir_result.Unwrap(); + rb.Push(RESULT_SUCCESS); + rb.Push(directory->backend->Read(0, nullptr)); /// Entry count + } + break; + } + default: { /// If not directory, then it is a file + auto file_result = + Service::FS::OpenFileFromArchive(cecd->cecd_system_save_data_archive, path, mode); + if (file_result.Failed()) { + LOG_ERROR(Service_CECD, "Failed to open file: {}", path.AsString()); + rb.Push(ResultCode(ErrorDescription::NoData, ErrorModule::CEC, ErrorSummary::NotFound, + ErrorLevel::Status)); + rb.Push(0); /// No file size + } else { + session_data->file = std::move(file_result.Unwrap()); + rb.Push(RESULT_SUCCESS); + rb.Push(session_data->file->backend->GetSize()); /// Return file size + } + + if (path_type == CecDataPathType::CEC_MBOX_PROGRAM_ID) { + std::vector program_id(8); + u64_le le_program_id = Kernel::g_current_process->codeset->program_id; + std::memcpy(program_id.data(), &le_program_id, sizeof(u64)); + session_data->file->backend->Write(0, sizeof(u64), true, program_id.data()); + } + } + } + + LOG_DEBUG(Service_CECD, + "called, ncch_program_id={:#010x}, path_type={:#04x}, open_mode={:#010x}, path={}", + ncch_program_id, static_cast(path_type), open_mode.raw, path.AsString()); } void Module::Interface::ReadRawFile(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x02, 1, 2); - const u32 buffer_size = rp.Pop(); - auto& buffer = rp.PopMappedBuffer(); + const u32 write_buffer_size = rp.Pop(); + auto& write_buffer = rp.PopMappedBuffer(); + + SessionData* session_data = GetSessionData(ctx.Session()); IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); - rb.Push(RESULT_SUCCESS); - rb.Push(0); /// Read size - rb.PushMappedBuffer(buffer); + switch (session_data->data_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(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::CEC, + ErrorSummary::NotFound, ErrorLevel::Status)); + rb.Push(0); /// No bytes read + break; + default: /// If not directory, then it is a file + std::vector buffer(write_buffer_size); + const u32 bytes_read = + session_data->file->backend->Read(0, write_buffer_size, buffer.data()).Unwrap(); - LOG_WARNING(Service_CECD, "(STUBBED) called, buffer_size={:#010x}", buffer_size); + write_buffer.Write(buffer.data(), 0, write_buffer_size); + session_data->file->backend->Close(); + + rb.Push(RESULT_SUCCESS); + rb.Push(bytes_read); + } + rb.PushMappedBuffer(write_buffer); + + LOG_DEBUG(Service_CECD, "called, write_buffer_size={:#010x}, path={}", write_buffer_size, + session_data->path.AsString()); } void Module::Interface::ReadMessage(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x03, 4, 4); const u32 ncch_program_id = rp.Pop(); - const bool is_out_box = rp.Pop(); + const bool is_outbox = rp.Pop(); const u32 message_id_size = rp.Pop(); const u32 buffer_size = rp.Pop(); - const auto& message_id_buffer = rp.PopMappedBuffer(); + auto& message_id_buffer = rp.PopMappedBuffer(); auto& write_buffer = rp.PopMappedBuffer(); IPC::RequestBuilder rb = rp.MakeBuilder(2, 4); @@ -63,18 +146,18 @@ void Module::Interface::ReadMessage(Kernel::HLERequestContext& ctx) { 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); + LOG_WARNING(Service_CECD, "(STUBBED) called, ncch_program_id={:#010x}, is_outbox={}", + ncch_program_id, is_outbox); } void Module::Interface::ReadMessageWithHMAC(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x04, 4, 6); const u32 ncch_program_id = rp.Pop(); - const bool is_out_box = rp.Pop(); + const bool is_outbox = rp.Pop(); const u32 message_id_size = rp.Pop(); const u32 buffer_size = rp.Pop(); - const auto& message_id_buffer = rp.PopMappedBuffer(); - const auto& hmac_key_buffer = rp.PopMappedBuffer(); + auto& message_id_buffer = rp.PopMappedBuffer(); + auto& hmac_key_buffer = rp.PopMappedBuffer(); auto& write_buffer = rp.PopMappedBuffer(); IPC::RequestBuilder rb = rp.MakeBuilder(2, 6); @@ -84,29 +167,48 @@ void Module::Interface::ReadMessageWithHMAC(Kernel::HLERequestContext& ctx) { 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); + LOG_WARNING(Service_CECD, "(STUBBED) called, ncch_program_id={:#010x}, is_outbox={}", + ncch_program_id, is_outbox); } void Module::Interface::WriteRawFile(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x05, 1, 2); - const u32 buffer_size = rp.Pop(); - const auto& buffer = rp.PopMappedBuffer(); + const u32 read_buffer_size = rp.Pop(); + auto& read_buffer = rp.PopMappedBuffer(); + + SessionData* session_data = GetSessionData(ctx.Session()); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); - rb.Push(RESULT_SUCCESS); - rb.PushMappedBuffer(buffer); + switch (session_data->data_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(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::CEC, + ErrorSummary::NotFound, ErrorLevel::Status)); + break; + default: /// If not directory, then it is a file + std::vector buffer(read_buffer_size); + read_buffer.Read(buffer.data(), 0, read_buffer_size); - LOG_WARNING(Service_CECD, "(STUBBED) called, buffer_size={:#010x}", buffer_size); + const u32 bytes_written = + session_data->file->backend->Write(0, read_buffer_size, true, buffer.data()).Unwrap(); + session_data->file->backend->Close(); + + rb.Push(RESULT_SUCCESS); + } + rb.PushMappedBuffer(read_buffer); + + LOG_DEBUG(Service_CECD, "called, read_buffer_size={:#010x}", read_buffer_size); } void Module::Interface::WriteMessage(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x06, 4, 4); const u32 ncch_program_id = rp.Pop(); - const bool is_out_box = rp.Pop(); + const bool is_outbox = rp.Pop(); const u32 message_id_size = rp.Pop(); const u32 buffer_size = rp.Pop(); - const auto& read_buffer = rp.PopMappedBuffer(); + auto& read_buffer = rp.PopMappedBuffer(); auto& message_id_buffer = rp.PopMappedBuffer(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 4); @@ -114,18 +216,18 @@ void Module::Interface::WriteMessage(Kernel::HLERequestContext& ctx) { 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); + LOG_WARNING(Service_CECD, "(STUBBED) called, ncch_program_id={:#010x}, is_outbox={}", + ncch_program_id, is_outbox); } void Module::Interface::WriteMessageWithHMAC(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x07, 4, 6); const u32 ncch_program_id = rp.Pop(); - const bool is_out_box = rp.Pop(); + const bool is_outbox = rp.Pop(); const u32 message_id_size = rp.Pop(); const u32 buffer_size = rp.Pop(); - const auto& read_buffer = rp.PopMappedBuffer(); - const auto& hmac_key_buffer = rp.PopMappedBuffer(); + auto& read_buffer = rp.PopMappedBuffer(); + auto& hmac_key_buffer = rp.PopMappedBuffer(); auto& message_id_buffer = rp.PopMappedBuffer(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 6); @@ -134,44 +236,99 @@ void Module::Interface::WriteMessageWithHMAC(Kernel::HLERequestContext& ctx) { 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); + LOG_WARNING(Service_CECD, "(STUBBED) called, ncch_program_id={:#010x}, is_outbox={}", + ncch_program_id, is_outbox); } void Module::Interface::Delete(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx, 0x10, 4, 2); + IPC::RequestParser rp(ctx, 0x08, 4, 2); const u32 ncch_program_id = rp.Pop(); const auto path_type = static_cast(rp.Pop()); - const bool is_out_box = rp.Pop(); + const bool is_outbox = rp.Pop(); const u32 message_id_size = rp.Pop(); - const auto& message_id_buffer = rp.PopMappedBuffer(); + auto& message_id_buffer = rp.PopMappedBuffer(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(RESULT_SUCCESS); rb.PushMappedBuffer(message_id_buffer); LOG_WARNING(Service_CECD, - "(STUBBED) called, ncch_program_id={:#010x}, path_type={:#010x}, is_out_box={}", - ncch_program_id, static_cast(path_type), is_out_box); + "(STUBBED) called, ncch_program_id={:#010x}, path_type={:#04x}, is_outbox={}", + ncch_program_id, static_cast(path_type), is_outbox); +} + +void Module::Interface::Cecd_0x000900C2(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x09, 3, 2); + const u32 ncch_program_id = rp.Pop(); + const u32 size = rp.Pop(); + const u32 option = rp.Pop(); + auto& message_id_buffer = rp.PopMappedBuffer(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushMappedBuffer(message_id_buffer); + + LOG_WARNING(Service_CECD, + "(STUBBED) called, ncch_program_id={:#010x}, size={:#010x}, option={:#010x}", + ncch_program_id, size, option); } void Module::Interface::GetSystemInfo(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx, 0x10, 3, 4); + IPC::RequestParser rp(ctx, 0x0A, 3, 4); const u32 dest_buffer_size = rp.Pop(); - const u32 info_type = rp.Pop(); + const CecSystemInfoType info_type = static_cast(rp.Pop()); const u32 param_buffer_size = rp.Pop(); - const auto& param_buffer = rp.PopMappedBuffer(); + auto& param_buffer = rp.PopMappedBuffer(); auto& dest_buffer = rp.PopMappedBuffer(); + /// TODO: Other CecSystemInfoTypes IPC::RequestBuilder rb = rp.MakeBuilder(1, 4); + std::vector buffer; + switch (info_type) { + case CecSystemInfoType::EulaVersion: /// TODO: Read config Eula version + buffer = {0xFF, 0xFF}; + dest_buffer.Write(buffer.data(), 0, buffer.size()); + break; + case CecSystemInfoType::Eula: + buffer = {0x01}; /// Eula agreed + dest_buffer.Write(buffer.data(), 0, buffer.size()); + break; + case CecSystemInfoType::ParentControl: + buffer = {0x00}; /// No parent control + dest_buffer.Write(buffer.data(), 0, buffer.size()); + break; + default: + LOG_ERROR(Service_CECD, "Unknown system info type {}", static_cast(info_type)); + } + rb.Push(RESULT_SUCCESS); rb.PushMappedBuffer(param_buffer); rb.PushMappedBuffer(dest_buffer); - LOG_WARNING(Service_CECD, - "(STUBBED) called, dest_buffer_size={:#010x}, info_type={:#010x}, " - "param_buffer_size={:#010x}", - dest_buffer_size, info_type, param_buffer_size); + LOG_DEBUG(Service_CECD, + "(STUBBED) called, dest_buffer_size={:#010x}, info_type={:#010x}, " + "param_buffer_size={:#010x}", + dest_buffer_size, static_cast(info_type), param_buffer_size); +} + +void Module::Interface::RunCommand(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x0B, 1, 0); + const auto command = static_cast(rp.Pop()); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_CECD, "(STUBBED) called, command={}", static_cast(command)); +} + +void Module::Interface::RunCommandAlt(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x0C, 1, 0); + const auto command = static_cast(rp.Pop()); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_CECD, "(STUBBED) called, command={}", static_cast(command)); } void Module::Interface::GetCecStateAbbreviated(Kernel::HLERequestContext& ctx) { @@ -209,36 +366,49 @@ void Module::Interface::OpenAndWrite(Kernel::HLERequestContext& ctx) { const u32 buffer_size = rp.Pop(); const u32 ncch_program_id = rp.Pop(); const auto path_type = static_cast(rp.Pop()); - const u32 file_open_flag = rp.Pop(); + CecOpenMode open_mode; + open_mode.raw = rp.Pop(); rp.PopPID(); auto& read_buffer = rp.PopMappedBuffer(); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); - 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); + FileSys::Mode mode; + mode.write_flag.Assign(1); + mode.create_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(); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + 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(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::CEC, + ErrorSummary::NotFound, ErrorLevel::Status)); + break; + default: /// If not directory, then it is a file + auto file_result = + Service::FS::OpenFileFromArchive(cecd->cecd_system_save_data_archive, path, mode); + if (file_result.Succeeded()) { + auto file = file_result.Unwrap(); + std::vector buffer(buffer_size); - rb.Push(RESULT_SUCCESS); - } else { - rb.Push(ResultCode(ErrorDescription::NoData, ErrorModule::CEC, ErrorSummary::NotFound, - ErrorLevel::Status)); + read_buffer.Read(buffer.data(), 0, buffer_size); + const u32 bytes_written = + file->backend->Write(0, buffer_size, true, buffer.data()).Unwrap(); + file->backend->Close(); + + rb.Push(RESULT_SUCCESS); + } else { + LOG_ERROR(Service_CECD, "Failed to open file: {}", path.AsString()); + 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, static_cast(path_type), file_open_flag); + LOG_DEBUG(Service_CECD, + "called, ncch_program_id={:#010x}, path_type={:#04x}, open_mode={:#010x}, path={}", + ncch_program_id, static_cast(path_type), open_mode.raw, path.AsString()); } void Module::Interface::OpenAndRead(Kernel::HLERequestContext& ctx) { @@ -246,42 +416,93 @@ void Module::Interface::OpenAndRead(Kernel::HLERequestContext& ctx) { const u32 buffer_size = rp.Pop(); const u32 ncch_program_id = rp.Pop(); const auto path_type = static_cast(rp.Pop()); - const u32 file_open_flag = rp.Pop(); + CecOpenMode open_mode; + open_mode.raw = rp.Pop(); rp.PopPID(); auto& write_buffer = rp.PopMappedBuffer(); - IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); - FileSys::Path path(cecd->GetCecDataPathTypeAsString(path_type, ncch_program_id).data()); - FileSys::Mode read_mode = {}; - read_mode.read_flag.Assign(1); + FileSys::Mode mode; + mode.read_flag.Assign(1); + mode.create_flag.Assign(1); + mode.write_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)); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); + 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(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::CEC, + ErrorSummary::NotFound, ErrorLevel::Status)); rb.Push(0); /// No bytes read + break; + default: /// If not directory, then it is a file + auto file_result = + Service::FS::OpenFileFromArchive(cecd->cecd_system_save_data_archive, path, mode); + if (file_result.Succeeded()) { + auto file = file_result.Unwrap(); + std::vector buffer(buffer_size); + + const u32 bytes_read = file->backend->Read(0, buffer_size, buffer.data()).Unwrap(); + write_buffer.Write(buffer.data(), 0, buffer_size); + file->backend->Close(); + + rb.Push(RESULT_SUCCESS); + rb.Push(bytes_read); + } else { + LOG_ERROR(Service_CECD, "Failed to open file: {}", path.AsString()); + + if (path_type == CecDataPathType::CEC_PATH_MBOX_INFO) { + const FileSys::Path mbox_path( + cecd->GetCecDataPathTypeAsString(CecDataPathType::CEC_PATH_MBOX_DIR, + ncch_program_id) + .data()); + Service::FS::CreateDirectoryFromArchive(cecd->cecd_system_save_data_archive, + mbox_path); + } + 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, static_cast(path_type), file_open_flag); + LOG_DEBUG(Service_CECD, + "called, ncch_program_id={:#010x}, path_type={:#04x}, open_mode={:#010x}, path={}", + ncch_program_id, static_cast(path_type), open_mode.raw, path.AsString()); +} + +std::string Module::EncodeBase64(const std::vector& in, const std::string& dictionary) const { + std::string out; + out.reserve((in.size() * 4) / 3); + int b; + for (int i = 0; i < in.size(); i += 3) { + b = (in[i] & 0xFC) >> 2; + out += dictionary[b]; + b = (in[i] & 0x03) << 4; + if (i + 1 < in.size()) { + b |= (in[i + 1] & 0xF0) >> 4; + out += dictionary[b]; + b = (in[i + 1] & 0x0F) << 2; + if (i + 2 < in.size()) { + b |= (in[i + 2] & 0xC0) >> 6; + out += dictionary[b]; + b = in[i + 2] & 0x3F; + out += dictionary[b]; + } else { + out += dictionary[b]; + } + } else { + out += dictionary[b]; + } + } + return out; } std::string Module::GetCecDataPathTypeAsString(const CecDataPathType type, const u32 program_id, - const std::vector& msg_id) { - switch(type) { + const std::vector& msg_id) const { + switch (type) { case CecDataPathType::CEC_PATH_MBOX_LIST: return "/CEC/MBoxList____"; case CecDataPathType::CEC_PATH_MBOX_INFO: @@ -293,9 +514,11 @@ std::string Module::GetCecDataPathTypeAsString(const CecDataPathType type, const 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()); + return Common::StringFromFormat("/CEC/%08x/InBox___/_%08x", program_id, + EncodeBase64(msg_id, base64_dict).data()); case CecDataPathType::CEC_PATH_OUTBOX_MSG: - return Common::StringFromFormat("/CEC/%08x/OutBox__/_%08x", program_id, msg_id.data()); + return Common::StringFromFormat("/CEC/%08x/OutBox__/_%08x", program_id, + EncodeBase64(msg_id, base64_dict).data()); case CecDataPathType::CEC_PATH_ROOT_DIR: return "/CEC"; case CecDataPathType::CEC_PATH_MBOX_DIR: @@ -313,6 +536,13 @@ std::string Module::GetCecDataPathTypeAsString(const CecDataPathType type, const } } +Module::SessionData::SessionData() {} + +Module::SessionData::~SessionData() { + if (file) + file->backend->Close(); +} + Module::Interface::Interface(std::shared_ptr cecd, const char* name, u32 max_session) : ServiceFramework(name, max_session), cecd(std::move(cecd)) {} @@ -336,16 +566,14 @@ Module::Module() { 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) { + 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 ec7fb0a7e0..95c977da6f 100644 --- a/src/core/hle/service/cecd/cecd.h +++ b/src/core/hle/service/cecd/cecd.h @@ -4,9 +4,10 @@ #pragma once +#include "common/bit_field.h" #include "core/hle/kernel/event.h" -#include "core/hle/service/service.h" #include "core/hle/service/fs/archive.h" +#include "core/hle/service/service.h" namespace Service { namespace CECD { @@ -16,14 +17,6 @@ public: Module(); ~Module(); - enum class CecStateAbbreviated : u32 { - CEC_STATE_ABBREV_IDLE = 1, /// Relates to CEC_STATE_IDLE - CEC_STATE_ABBREV_NOT_LOCAL = 2, /// Relates to CEC_STATEs *FINISH*, *POST, and OVER_BOSS - CEC_STATE_ABBREV_SCANNING = 3, /// Relates to CEC_STATE_SCANNING - CEC_STATE_ABBREV_WLREADY = 4, /// Relates to CEC_STATE_WIRELESS_READY when a bool is true - CEC_STATE_ABBREV_OTHER = 5, /// Relates to CEC_STATEs besides *FINISH*, *POST, and - }; /// OVER_BOSS and those listed here - enum class CecCommand : u32 { CEC_COMMAND_NONE = 0, CEC_COMMAND_START = 1, @@ -49,24 +42,158 @@ public: CEC_COMMAND_END = 0x15, }; + /** + * CecDataPathType possible missing values; need to figure out placement + * + * data:/CEC/TMP + * utBox__ + * data:/CEC/test + */ 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, + CEC_PATH_INVALID = 0, + CEC_PATH_MBOX_LIST = 1, /// data:/CEC/MBoxList____ + CEC_PATH_MBOX_INFO = 2, /// data:/CEC//MBoxInfo____ + CEC_PATH_INBOX_INFO = 3, /// data:/CEC//InBox___/BoxInfo_____ + CEC_PATH_OUTBOX_INFO = 4, /// data:/CEC//OutBox__/BoxInfo_____ + CEC_PATH_OUTBOX_INDEX = 5, /// data:/CEC//OutBox__/OBIndex_____ + CEC_PATH_INBOX_MSG = 6, /// data:/CEC//InBox___/_ + CEC_PATH_OUTBOX_MSG = 7, /// data:/CEC//OutBox__/_ + CEC_PATH_ROOT_DIR = 10, /// data:/CEC + CEC_PATH_MBOX_DIR = 11, /// data:/CEC/ + CEC_PATH_INBOX_DIR = 12, /// data:/CEC//InBox___ + CEC_PATH_OUTBOX_DIR = 13, /// data:/CEC//OutBox__ + CEC_MBOX_DATA = 100, /// data:/CEC//MBoxData.0 + CEC_MBOX_ICON = 101, /// data:/CEC//MBoxData.001 + CEC_MBOX_TITLE = 110, /// data:/CEC//MBoxData.010 + CEC_MBOX_PROGRAM_ID = 150, /// data:/CEC//MBoxData.050 }; - class Interface : public ServiceFramework { + enum class CecState : u32 { + CEC_STATE_NONE = 0, + CEC_STATE_INIT = 1, + CEC_STATE_WIRELESS_PARAM_SETUP = 2, + CEC_STATE_WIRELESS_READY = 3, + CEC_STATE_WIRELESS_START_CONFIG = 4, + CEC_STATE_SCAN = 5, + CEC_STATE_SCANNING = 6, + CEC_STATE_CONNECT = 7, + CEC_STATE_CONNECTING = 8, + CEC_STATE_CONNECTED = 9, + CEC_STATE_CONNECT_TCP = 10, + CEC_STATE_CONNECTING_TCP = 11, + CEC_STATE_CONNECTED_TCP = 12, + CEC_STATE_NEGOTIATION = 13, + CEC_STATE_SEND_RECV_START = 14, + CEC_STATE_SEND_RECV_INIT = 15, + CEC_STATE_SEND_READY = 16, + CEC_STATE_RECEIVE_READY = 17, + CEC_STATE_RECEIVE = 18, + CEC_STATE_CONNECTION_FINISH_TCP = 19, + CEC_STATE_CONNECTION_FINISH = 20, + CEC_STATE_SEND_POST = 21, + CEC_STATE_RECEIVE_POST = 22, + CEC_STATE_FINISHING = 23, + CEC_STATE_FINISH = 24, + CEC_STATE_OVER_BOSS = 25, + CEC_STATE_IDLE = 26 + }; + + /// Need to confirm if CecStateAbbreviated is up-to-date and valid + enum class CecStateAbbreviated : u32 { + CEC_STATE_ABBREV_IDLE = 1, /// Relates to CEC_STATE_IDLE + CEC_STATE_ABBREV_NOT_LOCAL = 2, /// Relates to CEC_STATEs *FINISH*, *POST, and OVER_BOSS + CEC_STATE_ABBREV_SCANNING = 3, /// Relates to CEC_STATE_SCANNING + CEC_STATE_ABBREV_WLREADY = 4, /// Relates to CEC_STATE_WIRELESS_READY when a bool is true + CEC_STATE_ABBREV_OTHER = 5, /// Relates to CEC_STATEs besides *FINISH*, *POST, and + }; /// OVER_BOSS and those listed here + + enum class CecSystemInfoType : u32 { EulaVersion = 1, Eula = 2, ParentControl = 3 }; + + struct CecInOutBoxInfoHeader { + u16_le magic; // bb + u16_le padding; + u32_le box_info_size; + u32_le max_box_size; + u32_le box_size; + u32_le max_message_num; + u32_le message_num; + u32_le max_batch_size; + u32_le max_message_size; + }; + static_assert(sizeof(CecInOutBoxInfoHeader) == 0x20, + "CecInOutBoxInfoHeader struct has incorrect size."); + + struct CecMessageHeader { + u16_le magic; // `` + u16_le padding; + u32_le message_size; + u32_le header_size; + u32_le body_size; + + u32_le title_id; + u32_le title_id_2; + u32_le batch_id; + u32_le unknown_id; + + u8 message_id[8]; + u32_le version; + u8 message_id_2[8]; + u8 flag; + u8 send_method; + u8 is_unopen; + u8 is_new; + u64_le sender_id; + u64_le sender_id2; + struct Time { + u32_le a, b, c; + } send_time, recv_time, create_time; + u8 send_count; + u8 foward_count; + u16_le user_data; + }; + static_assert(sizeof(CecMessageHeader) == 0x70, "CecMessageHeader struct has incorrect size."); + + enum class CecNdmStatus : u32 { + NDM_STATUS_WORKING = 0, + NDM_STATUS_IDLE = 1, + NDM_STATUS_SUSPENDING = 2, + NDM_STATUS_SUSPENDED = 3, + }; + + union CecOpenMode { + u32 raw; + BitField<1, 1, u32> read; + BitField<2, 1, u32> write; + BitField<3, 1, u32> make_dir; + BitField<4, 1, u32> skip_check; /// or is it check/test? + BitField<30, 1, u32> unk_flag; + }; + + enum class CecTest : u32 { + CEC_TEST_000 = 0, + CEC_TEST_001 = 1, + CEC_TEST_002 = 2, + CEC_TEST_003 = 3, + CEC_TEST_004 = 4, + CEC_TEST_005 = 5, + CEC_TEST_006 = 6, + }; + + /// Opening a file and reading/writing can be handled by two different functions + /// So, we need to pass that file data around + struct SessionData : public Kernel::SessionRequestHandler::SessionDataBase { + SessionData(); + ~SessionData(); + + u32 ncch_program_id; + CecDataPathType data_path_type; + CecOpenMode open_mode; + FileSys::Path path; + + std::shared_ptr file; + }; + + class Interface : public ServiceFramework { public: Interface(std::shared_ptr cecd, const char* name, u32 max_session); ~Interface() = default; @@ -107,7 +234,7 @@ public: * Inputs: * 0 : Header Code[0x00030104] * 1 : NCCH Program ID - * 2 : bool is_out_box? + * 2 : bool is_outbox * 3 : Message ID size (unused, always 8) * 4 : Buffer size (unused) * 5 : Descriptor for mapping a read-only buffer in the target process @@ -129,7 +256,7 @@ public: * Inputs: * 0 : Header Code[0x00040106] * 1 : NCCH Program ID - * 2 : bool is_out_box? + * 2 : bool is_outbox * 3 : Message ID size(unused, always 8) * 4 : Buffer size(unused) * 5 : Descriptor for mapping a read-only buffer in the target process @@ -169,7 +296,7 @@ public: * Inputs: * 0 : Header Code[0x00060104] * 1 : NCCH Program ID - * 2 : bool is_out_box? + * 2 : bool is_outbox * 3 : Message ID size(unused, always 8) * 4 : Buffer size(unused) * 5 : Descriptor for mapping a read-only buffer in the target process @@ -190,7 +317,7 @@ public: * Inputs: * 0 : Header Code[0x00070106] * 1 : NCCH Program ID - * 2 : bool is_out_box? + * 2 : bool is_outbox * 3 : Message ID size(unused, always 8) * 4 : Buffer size(unused) * 5 : Descriptor for mapping a read-only buffer in the target process @@ -216,7 +343,7 @@ public: * 0 : Header Code[0x00080102] * 1 : NCCH Program ID * 2 : Path type - * 3 : bool is_out_box? + * 3 : bool is_outbox * 4 : Message ID size (unused) * 5 : Descriptor for mapping a read-only buffer in the target process * 6 : Message ID address @@ -227,6 +354,22 @@ public: */ void Delete(Kernel::HLERequestContext& ctx); + /** + * CECD::Cecd_0x000900C2 service function + * Inputs: + * 0 : Header Code[0x000900C2] + * 1 : NCCH Program ID + * 2 : Path type + * 3 : bool is_outbox + * 4 : Descriptor for mapping a read-only buffer in the target process + * 5 : Message ID address + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Descriptor for mapping a read-only buffer in the target process + * 3 : Message ID address + */ + void Cecd_0x000900C2(Kernel::HLERequestContext& ctx); + /** * CECD::GetSystemInfo service function * Inputs: @@ -318,7 +461,7 @@ public: * 1 : Buffer size (unused) * 2 : NCCH Program ID * 3 : Path type - * 4 : File open flag? + * 4 : File open flag * 5 : Descriptor for process ID * 6 : Placeholder for process ID * 7 : Descriptor for mapping a read-only buffer in the target process @@ -337,7 +480,7 @@ public: * 1 : Buffer size (unused) * 2 : NCCH Program ID * 3 : Path type - * 4 : File open flag? + * 4 : File open flag * 5 : Descriptor for process ID * 6 : Placeholder for process ID * 7 : Descriptor for mapping a write-only buffer in the target process @@ -379,12 +522,18 @@ 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}; - const std::vector cecd_system_savedata_id = { - 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x01, 0x00 - }; + /// String used by cecd for base64 encoding found in the sysmodule disassembly + const std::string base64_dict = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-"; + + /// Encoding function used for the message id + std::string EncodeBase64(const std::vector& in, const std::string& dictionary) const; + + std::string GetCecDataPathTypeAsString(const CecDataPathType type, const u32 program_id, + const std::vector& msg_id = std::vector()) const; Service::FS::ArchiveHandle cecd_system_save_data_archive; diff --git a/src/core/hle/service/cecd/cecd_s.cpp b/src/core/hle/service/cecd/cecd_s.cpp index e8d6546c6e..b736c689b1 100644 --- a/src/core/hle/service/cecd/cecd_s.cpp +++ b/src/core/hle/service/cecd/cecd_s.cpp @@ -20,9 +20,11 @@ CECD_S::CECD_S(std::shared_ptr cecd) {0x00060104, &CECD_S::WriteMessage, "WriteMessage"}, {0x00070106, &CECD_S::WriteMessageWithHMAC, "WriteMessageWithHMAC"}, {0x00080102, &CECD_S::Delete, "Delete"}, + {0x000900C2, &CECD_S::Cecd_0x000900C2, "Cecd_0x000900C2"}, {0x000A00C4, &CECD_S::GetSystemInfo, "GetSystemInfo"}, - {0x000B0040, nullptr, "RunCommand"}, - {0x000C0040, nullptr, "RunCommandAlt"}, + {0x000B0040, &CECD_S::RunCommand, "RunCommand"}, + {0x000C0040, &CECD_S::RunCommandAlt, "RunCommandAlt"}, + {0x000D0082, nullptr, "GetCecInfoBuffer"}, {0x000E0000, &CECD_S::GetCecStateAbbreviated, "GetCecStateAbbreviated"}, {0x000F0000, &CECD_S::GetCecInfoEventHandle, "GetCecInfoEventHandle"}, {0x00100000, &CECD_S::GetChangeStateEventHandle, "GetChangeStateEventHandle"}, diff --git a/src/core/hle/service/cecd/cecd_u.cpp b/src/core/hle/service/cecd/cecd_u.cpp index f89563116a..e12b3dde04 100644 --- a/src/core/hle/service/cecd/cecd_u.cpp +++ b/src/core/hle/service/cecd/cecd_u.cpp @@ -20,9 +20,11 @@ CECD_U::CECD_U(std::shared_ptr cecd) {0x00060104, &CECD_U::WriteMessage, "WriteMessage"}, {0x00070106, &CECD_U::WriteMessageWithHMAC, "WriteMessageWithHMAC"}, {0x00080102, &CECD_U::Delete, "Delete"}, + {0x000900C2, &CECD_U::Cecd_0x000900C2, "Cecd_0x000900C2"}, {0x000A00C4, &CECD_U::GetSystemInfo, "GetSystemInfo"}, - {0x000B0040, nullptr, "RunCommand"}, - {0x000C0040, nullptr, "RunCommandAlt"}, + {0x000B0040, &CECD_U::RunCommand, "RunCommand"}, + {0x000C0040, &CECD_U::RunCommandAlt, "RunCommandAlt"}, + {0x000D0082, nullptr, "GetCecInfoBuffer"}, {0x000E0000, &CECD_U::GetCecStateAbbreviated, "GetCecStateAbbreviated"}, {0x000F0000, &CECD_U::GetCecInfoEventHandle, "GetCecInfoEventHandle"}, {0x00100000, &CECD_U::GetChangeStateEventHandle, "GetChangeStateEventHandle"},