From 413f1651b74f5c1137b98f25cb7581317716f236 Mon Sep 17 00:00:00 2001
From: NarcolepticK <NarcolepticKrias@gmail.com>
Date: Mon, 6 Aug 2018 02:39:47 -0400
Subject: [PATCH] service/cecd: Implement basic file handling functions.

---
 src/core/hle/service/cecd/cecd.cpp   | 426 ++++++++++++++++++++-------
 src/core/hle/service/cecd/cecd.h     | 221 +++++++++++---
 src/core/hle/service/cecd/cecd_s.cpp |   6 +-
 src/core/hle/service/cecd/cecd_u.cpp |   6 +-
 4 files changed, 520 insertions(+), 139 deletions(-)

diff --git a/src/core/hle/service/cecd/cecd.cpp b/src/core/hle/service/cecd/cecd.cpp
index 0dc801a39..c003e547a 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<u32>();
     const auto path_type = static_cast<CecDataPathType>(rp.Pop<u32>());
-    const u32 file_open_flag = rp.Pop<u32>();
+    CecOpenMode open_mode;
+    open_mode.raw = rp.Pop<u32>();
     rp.PopPID();
 
-    IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
-    rb.Push(RESULT_SUCCESS);
-    rb.Push<u32>(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<u32>(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<u32>(0); /// Zero entries
+        } else {
+            auto directory = dir_result.Unwrap();
+            rb.Push(RESULT_SUCCESS);
+            rb.Push<u32>(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<u32>(0); /// No file size
+        } else {
+            session_data->file = std::move(file_result.Unwrap());
+            rb.Push(RESULT_SUCCESS);
+            rb.Push<u32>(session_data->file->backend->GetSize()); /// Return file size
+        }
+
+        if (path_type == CecDataPathType::CEC_MBOX_PROGRAM_ID) {
+            std::vector<u8> 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<u32>(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<u32>();
-    auto& buffer = rp.PopMappedBuffer();
+    const u32 write_buffer_size = rp.Pop<u32>();
+    auto& write_buffer = rp.PopMappedBuffer();
+
+    SessionData* session_data = GetSessionData(ctx.Session());
 
     IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
-    rb.Push(RESULT_SUCCESS);
-    rb.Push<u32>(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<u32>(0); /// No bytes read
+        break;
+    default: /// If not directory, then it is a file
+        std::vector<u8> 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<u32>(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<u32>();
-    const bool is_out_box = rp.Pop<bool>();
+    const bool is_outbox = rp.Pop<bool>();
     const u32 message_id_size = rp.Pop<u32>();
     const u32 buffer_size = rp.Pop<u32>();
-    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<u32>();
-    const bool is_out_box = rp.Pop<bool>();
+    const bool is_outbox = rp.Pop<bool>();
     const u32 message_id_size = rp.Pop<u32>();
     const u32 buffer_size = rp.Pop<u32>();
-    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<u32>();
-    const auto& buffer = rp.PopMappedBuffer();
+    const u32 read_buffer_size = rp.Pop<u32>();
+    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<u8> 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<u32>();
-    const bool is_out_box = rp.Pop<bool>();
+    const bool is_outbox = rp.Pop<bool>();
     const u32 message_id_size = rp.Pop<u32>();
     const u32 buffer_size = rp.Pop<u32>();
-    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<u32>();
-    const bool is_out_box = rp.Pop<bool>();
+    const bool is_outbox = rp.Pop<bool>();
     const u32 message_id_size = rp.Pop<u32>();
     const u32 buffer_size = rp.Pop<u32>();
-    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<u32>();
     const auto path_type = static_cast<CecDataPathType>(rp.Pop<u32>());
-    const bool is_out_box = rp.Pop<bool>();
+    const bool is_outbox = rp.Pop<bool>();
     const u32 message_id_size = rp.Pop<u32>();
-    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<u32>(path_type), is_out_box);
+                "(STUBBED) called, ncch_program_id={:#010x}, path_type={:#04x}, is_outbox={}",
+                ncch_program_id, static_cast<u32>(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<u32>();
+    const u32 size = rp.Pop<u32>();
+    const u32 option = rp.Pop<u32>();
+    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<u32>();
-    const u32 info_type = rp.Pop<u32>();
+    const CecSystemInfoType info_type = static_cast<CecSystemInfoType>(rp.Pop<u32>());
     const u32 param_buffer_size = rp.Pop<u32>();
-    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<u8> 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<u32>(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<u32>(info_type), param_buffer_size);
+}
+
+void Module::Interface::RunCommand(Kernel::HLERequestContext& ctx) {
+    IPC::RequestParser rp(ctx, 0x0B, 1, 0);
+    const auto command = static_cast<CecCommand>(rp.Pop<u32>());
+
+    IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+    rb.Push(RESULT_SUCCESS);
+
+    LOG_WARNING(Service_CECD, "(STUBBED) called, command={}", static_cast<u32>(command));
+}
+
+void Module::Interface::RunCommandAlt(Kernel::HLERequestContext& ctx) {
+    IPC::RequestParser rp(ctx, 0x0C, 1, 0);
+    const auto command = static_cast<CecCommand>(rp.Pop<u32>());
+
+    IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+    rb.Push(RESULT_SUCCESS);
+
+    LOG_WARNING(Service_CECD, "(STUBBED) called, command={}", static_cast<u32>(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<u32>();
     const u32 ncch_program_id = rp.Pop<u32>();
     const auto path_type = static_cast<CecDataPathType>(rp.Pop<u32>());
-    const u32 file_open_flag = rp.Pop<u32>();
+    CecOpenMode open_mode;
+    open_mode.raw = rp.Pop<u32>();
     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<u8> 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<u8> 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<u32>(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<u32>(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<u32>();
     const u32 ncch_program_id = rp.Pop<u32>();
     const auto path_type = static_cast<CecDataPathType>(rp.Pop<u32>());
-    const u32 file_open_flag = rp.Pop<u32>();
+    CecOpenMode open_mode;
+    open_mode.raw = rp.Pop<u32>();
     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<u8> 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<u32>(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<u32>(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<u8> 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<u32>(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<u32>(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<u32>(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<u32>(path_type), open_mode.raw, path.AsString());
+}
+
+std::string Module::EncodeBase64(const std::vector<u8>& 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<u8>& msg_id) {
-    switch(type) {
+                                               const std::vector<u8>& 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<Module> 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 ec7fb0a7e..95c977da6 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/<id>/MBoxInfo____
+        CEC_PATH_INBOX_INFO = 3,   /// data:/CEC/<id>/InBox___/BoxInfo_____
+        CEC_PATH_OUTBOX_INFO = 4,  /// data:/CEC/<id>/OutBox__/BoxInfo_____
+        CEC_PATH_OUTBOX_INDEX = 5, /// data:/CEC/<id>/OutBox__/OBIndex_____
+        CEC_PATH_INBOX_MSG = 6,    /// data:/CEC/<id>/InBox___/_<message_id>
+        CEC_PATH_OUTBOX_MSG = 7,   /// data:/CEC/<id>/OutBox__/_<message_id>
+        CEC_PATH_ROOT_DIR = 10,    /// data:/CEC
+        CEC_PATH_MBOX_DIR = 11,    /// data:/CEC/<id>
+        CEC_PATH_INBOX_DIR = 12,   /// data:/CEC/<id>/InBox___
+        CEC_PATH_OUTBOX_DIR = 13,  /// data:/CEC/<id>/OutBox__
+        CEC_MBOX_DATA = 100,       /// data:/CEC/<id>/MBoxData.0<i-100>
+        CEC_MBOX_ICON = 101,       /// data:/CEC/<id>/MBoxData.001
+        CEC_MBOX_TITLE = 110,      /// data:/CEC/<id>/MBoxData.010
+        CEC_MBOX_PROGRAM_ID = 150, /// data:/CEC/<id>/MBoxData.050
     };
 
-    class Interface : public ServiceFramework<Interface> {
+    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<Service::FS::File> file;
+    };
+
+    class Interface : public ServiceFramework<Interface, SessionData> {
     public:
         Interface(std::shared_ptr<Module> 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<u8>& message_id = std::vector<u8>());
+    const std::vector<u8> cecd_system_savedata_id = {0x00, 0x00, 0x00, 0x00,
+                                                     0x26, 0x00, 0x01, 0x00};
 
-    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
+    const std::string base64_dict =
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
+
+    /// Encoding function used for the message id
+    std::string EncodeBase64(const std::vector<u8>& in, const std::string& dictionary) const;
+
+    std::string GetCecDataPathTypeAsString(const CecDataPathType type, const u32 program_id,
+                                           const std::vector<u8>& msg_id = std::vector<u8>()) 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 e8d6546c6..b736c689b 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<Module> 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 f89563116..e12b3dde0 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<Module> 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"},