diff --git a/libctru/include/3ds.h b/libctru/include/3ds.h index 571565f45..009338bd0 100644 --- a/libctru/include/3ds.h +++ b/libctru/include/3ds.h @@ -41,6 +41,7 @@ extern "C" { #include <3ds/services/apt.h> #include <3ds/services/boss.h> #include <3ds/services/cam.h> +#include <3ds/services/cecd.h> #include <3ds/services/cfgnor.h> #include <3ds/services/cfgu.h> #include <3ds/services/csnd.h> diff --git a/libctru/include/3ds/services/boss.h b/libctru/include/3ds/services/boss.h index 10f9202ac..b9f29e22f 100644 --- a/libctru/include/3ds/services/boss.h +++ b/libctru/include/3ds/services/boss.h @@ -4,6 +4,8 @@ */ #pragma once +#include <3ds/types.h> + /// BOSS context. typedef struct { @@ -95,6 +97,15 @@ Result bossUnregisterStorage(void); */ Result bossRegisterTask(const char *taskID, u8 unk0, u8 unk1); +/// Loads the task id list +Result bossGetTaskIdList(void); + +/** + * @brief Loads the Step ID list + * @param task_id The task id for which to load the step id list + */ +Result bossGetStepIdList(const char* task_id); + /** * @brief Send a property. * @param PropertyID PropertyID @@ -103,6 +114,94 @@ Result bossRegisterTask(const char *taskID, u8 unk0, u8 unk1); */ Result bossSendProperty(u16 PropertyID, const void* buf, u32 size); +/** + * @brief Receives a property + * @param property_id The property id to receive + * @param buffer The read buffer + * @param size The size of the read buffer + */ +Result bossReceiveProperty(u16 property_id, void* buffer, u32 size); + +/** + * @brief Get the interval of a task, in seconds + * @param task_id The task id + * @param interval The output interval + */ +Result bossGetTaskInterval(const char* task_id, u32* interval); + +/** + * @brief Gets the count of the given task + * @param task_id The task id + * @param count The count returned + */ +Result bossGetTaskCount(const char* task_id, u32* count); + +/** + * @brief Get the service status of given task + * @param task_id The task id + * @param service_status The service status returned + */ +Result bossGetTaskServiceStatus(const char* task_id, u8* service_status); + +/** + * @brief Starts a boss task + * @param task_id The task id + */ +Result bossStartTask(const char* task_id); + +/** + * @brief Starts a task soon after running this command. + * @param taskID BOSS taskID. + */ +Result bossStartTaskImmediate(const char *taskID); + +/** + * @brief Deletes a task by using CancelTask and UnregisterTask internally. + * @param taskID BOSS taskID. + * @param unk Unknown, usually zero? + */ +Result bossDeleteTask(const char *taskID, u32 unk); + +/** + * @brief Returns task state. + * @param taskID BOSS taskID. + * @param inval Unknown, normally 0? + * @param status Output status, see bossTaskStatus. + * @param out1 Output field. + * @param out2 Output field. + */ +Result bossGetTaskState(const char *taskID, s8 inval, u8 *status, u32 *out1, u8 *out2); + +/** + * @brief Gets the communication error code (http status code) of a given task id + * @param task_id The task id + * @param err_code The error code (http status code) returned + * @param count The count returned + * @param current_step The current step returned + */ +Result bossGetTaskCommErrorCode(const char* task_id, u32* err_code, u32* count, u8* current_step); + +/** + * @brief Load status of a given task and step + * @param task_id The task id + * @param step_id The step id + */ +Result bossGetTaskStatus(const char* task_id, u8 step_id); + +/** + * @brief Loads the error for a given task and step + * @param task_id The task id + * @param step_id The step id + */ +Result bossGetTaskError(const char* task_id, u8 step_id); + +/** + * @brief Loads the info for a given task and step + * @param task_id The task id + * @param step_id The step id + */ +Result bossGetTaskInfo(const char* task_id, u8 step_id); + /** * @brief Deletes the content file for the specified NsDataId. * @param NsDataId NsDataId @@ -129,12 +228,6 @@ Result bossGetNsDataHeaderInfo(u32 NsDataId, u8 type, void* buffer, u32 size); */ Result bossReadNsData(u32 NsDataId, u64 offset, void* buffer, u32 size, u32 *transfer_total, u32 *unk_out); -/** - * @brief Starts a task soon after running this command. - * @param taskID BOSS taskID. - */ -Result bossStartTaskImmediate(const char *taskID); - /** * @brief Similar to bossStartTaskImmediate? * @param taskID BOSS taskID. @@ -142,21 +235,16 @@ Result bossStartTaskImmediate(const char *taskID); Result bossStartBgImmediate(const char *taskID); /** - * @brief Deletes a task by using CancelTask and UnregisterTask internally. - * @param taskID BOSS taskID. - * @param unk Unknown, usually zero? + * @brief Gets the priority of a given task + * @param task_id The task id + * @param priority The priority returned */ -Result bossDeleteTask(const char *taskID, u32 unk); +Result bossGetTaskPriority(const char* task_id, u8* priority); /** - * @brief Returns task state. - * @param taskID BOSS taskID. - * @param inval Unknown, normally 0? - * @param status Output status, see bossTaskStatus. - * @param out1 Output field. - * @param out2 Output field. + * @brief Loads the list of application ids registered with boss */ -Result bossGetTaskState(const char *taskID, s8 inval, u8 *status, u32 *out1, u8 *out2); +Result bossGetAppIdList(void); /** * @brief This loads the current state of PropertyID 0x0 for the specified task. @@ -177,4 +265,3 @@ void bossSetupContextDefault(bossContext *ctx, u32 seconds_interval, const char * @param bossContext BOSS context. */ Result bossSendContextConfig(bossContext *ctx); - diff --git a/libctru/include/3ds/services/cecd.h b/libctru/include/3ds/services/cecd.h new file mode 100644 index 000000000..5d8109c4c --- /dev/null +++ b/libctru/include/3ds/services/cecd.h @@ -0,0 +1,248 @@ +/** + * @file cecd.h + * @brief CECD service, see also: https://www.3dbrew.org/wiki/CECD_Services + */ +#pragma once + +#include <3ds/types.h> + +typedef enum { + 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, +} CecPath; + +#define CECMESSAGE_BOX_ICON 101 +#define CECMESSAGE_BOX_TITLE 110 + +typedef enum { + CEC_COMMAND_NONE = 0, + CEC_COMMAND_START = 1, + CEC_COMMAND_RESET_START = 2, + CEC_COMMAND_READYSCAN = 3, + CEC_COMMAND_READYSCANWAIT = 4, + CEC_COMMAND_STARTSCAN = 5, + CEC_COMMAND_RESCAN = 6, + CEC_COMMAND_NDM_RESUME = 7, + CEC_COMMAND_NDM_SUSPEND = 8, + CEC_COMMAND_NDM_SUSPEND_IMMEDIATE = 9, + CEC_COMMAND_STOPWAIT = 0xA, + CEC_COMMAND_STOP = 0xB, + CEC_COMMAND_STOP_FORCE = 0xC, + CEC_COMMAND_STOP_FORCE_WAIT = 0xD, + CEC_COMMAND_RESET_FILTER = 0xE, + CEC_COMMAND_DAEMON_STOP = 0xF, + CEC_COMMAND_DAEMON_START = 0x10, + CEC_COMMAND_EXIT = 0x11, + CEC_COMMAND_OVER_BOSS = 0x12, + CEC_COMMAND_OVER_BOSS_FORCE = 0x13, + CEC_COMMAND_OVER_BOSS_FORCE_WAIT = 0x14, + CEC_COMMAND_END = 0x15, +} CecCommand; + +typedef enum { + CEC_STATE_ABBREV_IDLE = 1, + CEC_STATE_ABBREV_INACTIVE = 2, + CEC_STATE_ABBREV_SCANNING = 3, + CEC_STATE_ABBREV_WLREADY = 4, + CEC_STATE_ABBREV_OTHER = 5, +} CecStateAbbrev; + +typedef enum { + CEC_EXT_HEADER_TYPE_ICON = 2, + CEC_EXT_HEADER_TYPE_GAME_NAME = 3, + CEC_EXT_HEADER_TYPE_NOTIFICATION_TEXT = 4, + CEC_EXT_HEADER_TYPE_REGION = 5, +} CecExtHeaderType; + +typedef u8 CecMessageId[8]; + +typedef struct SlotMetadata { + int send_method; + u32 title_id; + u32 size; +} CecSlotMetadata; + +/** + * @brief Initializes CECD. + * @param force_user When true, just use cecdU instead of trying to initialize with cecdS first. + */ +Result cecdInit(bool force_user); + +/** + * @brief Reads a CEC message + * @param title_id The title id of the message we want to fetch + * @param is_outbox Are we fetching from an outbox? + * @param message_id The message id of the message to fetch + * @param buf the output buffer + * @param size the size of the output buffer + */ +Result cecdReadMessage(u32 title_id, bool is_outbox, CecMessageId message_id, u8* buf, u32 size); + +/** + * @brief Reads a CEC message with hmac + * @param title_id The title id of the message we want to fetch + * @param is_outbox Are we fetching from an outbox? + * @param message_id The message id of the message to fetch + * @param buf the output buffer + * @param size the size of the output buffer + * @param hmac the hmac of the message + */ +Result cecdReadMessageWithHMAC(u32 title_id, bool is_outbox, CecMessageId message_id, u8* buf, u32 size, u8* hmac); + +/** + * @brief Writes a CEC message + * @param title_id The title id of the message we want to write + * @param is_outbox Are we fetching from an outbox? + * @param message_id The message id of the message to write + * @param buf the input buffer + * @param size the size of the input buffer + */ +Result cecdWriteMessage(u32 title_id, bool is_outbox, CecMessageId message_id, u8* buf, u32 size); + +/** + * @brief Writes a CEC message with hmac + * @param title_id The title id of the message we want to write + * @param is_outbox Are we fetching from an outbox? + * @param message_id The message id of the message to write + * @param buf the input buffer + * @param size the size of the input buffer + * @param hmac the hmac of the message + */ +Result cecdWriteMessageWithHMAC(u32 title_id, bool is_outbox, CecMessageId message_id, u8* buf, u32 size, u8* hmac); + +/** + * @brief Executes start for a given command + * @param command Command to start + */ +Result cecdStart(CecCommand command); + +/** + * @brief Executes stop for a given command + * @param command Command to stop + */ +Result cecdStop(CecCommand command); + +/** + * @brief Get the state of CECD + * @param state The output state + */ +Result cecdGetState(CecStateAbbrev* state); + +/** + * @brief Get the info event handle + * @param handle The handle + */ +Result cecdGetCecInfoEventHandle(Handle* handle); + +/** + * @brief Gets the change state event handle + * @param handle The handle + */ +Result cecdGetChangeStateEventHandle(Handle* handle); + +/** + * @brief Opens and writes to a CEC data file + * @param title_id The title id to manipulate + * @param path_type The path of what is being written to + * @param buf The write buffer + * @param size The size of the write buffer + */ +Result cecdOpenAndWrite(u32 title_id, CecPath path_type, u8* buf, u32 size); + +/** + * @brief Opens and reads from a CEC data file + * @param title_id The title id to manipulate + * @param path_type The path of what is being read from + * @param buf The read buffer + * @param size The size of the read buffer + */ +Result cecdOpenAndRead(u32 title_id, CecPath path_type, u8* buf, u32 size); + +/** + * @brief Creates an SPR request + */ +Result cecdSprCreate(void); + +/** + * @brief Initialises an SPR request + */ +Result cecdSprInitialise(void); + +/** + * @brief Get the metadata for all slots to send + * @param buf The destination buffer of the slot metadata + * @param size The size of the destination buffer + * @param slots_total Outputs the total amount of slots being written + */ +Result cecdSprGetSlotsMetadata(CecSlotMetadata* buf, u32 size, u32* slots_total); + +/** + * @brief Get the slot to send + * @param title_id The title id of the slot to get + * @param buf The read buffer + * qparam size The size of the read buffer + */ +Result cecdSprGetSlot(u32 title_id, u8* buf, u32 size); + +/** + * @brief Set if a title was sent + * @param title_id The title id we want to set as sent + * @param success If the title id was sent successfully + */ +Result cecdSprSetTitleSent(u32 title_id, bool success); + +/** + * @brief Finalise sending slots in an SPR request + */ +Result cecdSprFinaliseSend(void); + +/** + * @brief Start receiving remote data of an SPR request + */ +Result cecdSprStartRecv(void); + +/** + * @brief Adds the slots metadata you received + * @param buf The buffer of the slots metadata + * @param size The size of the buffer + */ +Result cecdSprAddSlotsMetadata(u8* buf, u32 size); + +/** + * @brief Add a received slot + * @param title_id The title_id of the slot to add + * @param buf The buffer of the slot to add + * @param size The size of the slot to add + */ +Result cecdSprAddSlot(u32 title_id, u8* buf, u32 size); + +/** + * @brief Finalise receiving the SPR request + */ +Result cecdSprFinaliseRecv(void); + +/** + * @brief Signals that the SPR request is done + * @param success Set if the request was successful + */ +Result cecdSprDone(bool success); + +/** + * @brief Gets the boss user ID needed to send SPR requests + * @param out The output of the boss user id + */ +Result cecdGetBossUserid(u64* out); + +/** + * @brief Gets the open CECD handle + */ +Handle cecdGetServHandle(void); diff --git a/libctru/source/services/boss.c b/libctru/source/services/boss.c index df2da1565..6074d4593 100644 --- a/libctru/source/services/boss.c +++ b/libctru/source/services/boss.c @@ -149,6 +149,26 @@ static Result bossipc_UnregisterTask(const char *taskID, u32 unk) return (Result)cmdbuf[1]; } +Result bossGetTaskIdList(void) { + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0xE, 0, 0); + if (R_FAILED(ret = svcSendSyncRequest(bossHandle))) return ret; + return (Result)cmdbuf[1]; +} + +Result bossGetStepIdList(const char* task_id) { + u32 size = strlen(task_id) + 1; // include 0 pointer + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0xF, 1, 2); + cmdbuf[1] = size; + cmdbuf[2] = IPC_Desc_Buffer(size, IPC_BUFFER_R); + cmdbuf[3] = (u32)task_id; + if (R_FAILED(ret = svcSendSyncRequest(bossHandle))) return ret; + return (Result)cmdbuf[1]; +} + Result bossSendProperty(u16 PropertyID, const void* buf, u32 size) { Result ret = 0; @@ -165,6 +185,73 @@ Result bossSendProperty(u16 PropertyID, const void* buf, u32 size) return (Result)cmdbuf[1]; } +Result bossReceiveProperty(u16 property_id, void* buffer, u32 size) { + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x16, 2, 2); + cmdbuf[1] = property_id; + cmdbuf[2] = size; + cmdbuf[3] = IPC_Desc_Buffer(size, IPC_BUFFER_W); + cmdbuf[4] = (u32)buffer; + if (R_FAILED(ret = svcSendSyncRequest(bossHandle))) return ret; + return (Result)cmdbuf[1]; +} + +Result bossGetTaskInterval(const char* task_id, u32* interval) { + u32 size = strlen(task_id) + 1; // include 0 pointer + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x19, 1, 2); + cmdbuf[1] = size; + cmdbuf[2] = IPC_Desc_Buffer(size, IPC_BUFFER_R); + cmdbuf[3] = (u32)task_id; + if (R_FAILED(ret = svcSendSyncRequest(bossHandle))) return ret; + ret = (Result)cmdbuf[1]; + if (R_SUCCEEDED(ret) && interval) *interval = cmdbuf[2]; + return ret; +} + +Result bossGetTaskCount(const char* task_id, u32* count) { + u32 size = strlen(task_id) + 1; // include 0 pointer + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x1A, 1, 2); + cmdbuf[1] = size; + cmdbuf[2] = IPC_Desc_Buffer(size, IPC_BUFFER_R); + cmdbuf[3] = (u32)task_id; + if (R_FAILED(ret = svcSendSyncRequest(bossHandle))) return ret; + ret = (Result)cmdbuf[1]; + if (R_SUCCEEDED(ret) && count) *count = cmdbuf[2]; + return ret; +} + +Result bossGetTaskServiceStatus(const char* task_id, u8* service_status) { + u32 size = strlen(task_id) + 1; // include 0 pointer + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x1B, 1, 2); + cmdbuf[1] = size; + cmdbuf[2] = IPC_Desc_Buffer(size, IPC_BUFFER_R); + cmdbuf[3] = (u32)task_id; + if (R_FAILED(ret = svcSendSyncRequest(bossHandle))) return ret; + ret = (Result)cmdbuf[1]; + if (R_SUCCEEDED(ret) && service_status) *service_status = (u8)cmdbuf[2]; + return ret; +} + +Result bossStartTask(const char* task_id) { + u32 size = strlen(task_id) + 1; // include 0 pointer + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x1C, 1, 2); + cmdbuf[1] = size; + cmdbuf[2] = IPC_Desc_Buffer(size, IPC_BUFFER_R); + cmdbuf[3] = (u32)task_id; + if (R_FAILED(ret = svcSendSyncRequest(bossHandle))) return ret; + ret = (Result)cmdbuf[1]; + return ret; +} + Result bossStartTaskImmediate(const char *taskID) { Result ret = 0; @@ -234,6 +321,64 @@ Result bossGetTaskState(const char *taskID, s8 inval, u8 *status, u32 *out1, u8 return ret; } +Result bossGetTaskCommErrorCode(const char* task_id, u32* err_code, u32* count, u8* current_step) { + u32 size = strlen(task_id) + 1; // include 0 pointer + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x22, 1, 2); + cmdbuf[1] = size; + cmdbuf[2] = IPC_Desc_Buffer(size, IPC_BUFFER_R); + cmdbuf[3] = (u32)task_id; + if (R_FAILED(ret = svcSendSyncRequest(bossHandle))) return ret; + ret = (Result)cmdbuf[1]; + if (R_SUCCEEDED(ret)) { + if (err_code) *err_code = cmdbuf[2]; + if (count) *count = cmdbuf[3]; + if (current_step) *current_step = (u8)cmdbuf[4]; + } + return ret; +} + +Result bossGetTaskStatus(const char* task_id, u8 step_id) { + u32 size = strlen(task_id) + 1; // include 0 pointer + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x23, 3, 2); + cmdbuf[1] = size; + cmdbuf[2] = true; + cmdbuf[3] = step_id; + cmdbuf[4] = IPC_Desc_Buffer(size, IPC_BUFFER_R); + cmdbuf[5] = (u32)task_id; + if (R_FAILED(ret = svcSendSyncRequest(bossHandle))) return ret; + return (Result)cmdbuf[1]; +} + +Result bossGetTaskError(const char* task_id, u8 step_id) { + u32 size = strlen(task_id) + 1; // include 0 pointer + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x24, 2, 2); + cmdbuf[1] = size; + cmdbuf[2] = step_id; + cmdbuf[3] = IPC_Desc_Buffer(size, IPC_BUFFER_R); + cmdbuf[4] = (u32)task_id; + if (R_FAILED(ret = svcSendSyncRequest(bossHandle))) return ret; + return (Result)cmdbuf[1]; +} + +Result bossGetTaskInfo(const char* task_id, u8 step_id) { + u32 size = strlen(task_id) + 1; // include 0 pointer + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x25, 2, 2); + cmdbuf[1] = size; + cmdbuf[2] = step_id; + cmdbuf[3] = IPC_Desc_Buffer(size, IPC_BUFFER_R); + cmdbuf[4] = (u32)task_id; + if (R_FAILED(ret = svcSendSyncRequest(bossHandle))) return ret; + return (Result)cmdbuf[1]; +} + Result bossDeleteNsData(u32 NsDataId) { Result ret = 0; @@ -304,23 +449,31 @@ Result bossStartBgImmediate(const char *taskID) return (Result)cmdbuf[1]; } -Result bossGetTaskProperty0(const char *taskID, u8 *out) -{ +Result bossGetTaskPriority(const char* task_id, u8* priority) { + u32 size = strlen(task_id) + 1; // include 0 pointer Result ret = 0; - u32 *cmdbuf = getThreadCommandBuffer(); - u32 size = strlen(taskID)+1; - - cmdbuf[0] = IPC_MakeHeader(0x34,1,2); // 0x340042 + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x34, 1, 2); cmdbuf[1] = size; cmdbuf[2] = IPC_Desc_Buffer(size, IPC_BUFFER_R); - cmdbuf[3] = (u32)taskID; - - if(R_FAILED(ret = svcSendSyncRequest(bossHandle)))return ret; + cmdbuf[3] = (u32)task_id; + if (R_FAILED(ret = svcSendSyncRequest(bossHandle))) return ret; ret = (Result)cmdbuf[1]; + if (R_SUCCEEDED(ret) && priority) *priority = (u8)cmdbuf[2]; + return ret; +} - if(R_SUCCEEDED(ret) && out)*out = cmdbuf[2]; +Result bossGetTaskProperty0(const char *taskID, u8 *out) +{ + return bossGetTaskPriority(taskID, out); +} - return ret; +Result bossGetAppIdList(void) { + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x40A, 0, 0); + if (R_FAILED(ret = svcSendSyncRequest(bossHandle))) return ret; + return (Result)cmdbuf[1]; } void bossSetupContextDefault(bossContext *ctx, u32 seconds_interval, const char *url) @@ -445,4 +598,3 @@ Result bossSendContextConfig(bossContext *ctx) return ret; } - diff --git a/libctru/source/services/cecd.c b/libctru/source/services/cecd.c new file mode 100644 index 000000000..e24db2188 --- /dev/null +++ b/libctru/source/services/cecd.c @@ -0,0 +1,366 @@ +#include <3ds/ipc.h> +#include <3ds/result.h> +#include <3ds/services/cecd.h> +#include <3ds/srv.h> +#include <3ds/synchronization.h> + +static Handle cecdHandle; +static int cecdRefCount; + +Result cecdInit(bool force_user) { + Result res = -1; + + if (AtomicPostIncrement(&cecdRefCount)) return 0; + + if (!force_user) { + res = srvGetServiceHandle(&cecdHandle, "cecd:s"); + } + + if (R_FAILED(res)) { + res = srvGetServiceHandle(&cecdHandle, "cecd:u"); + } + + if (R_FAILED(res)) AtomicDecrement(&cecdRefCount); + return res; +} + +Result cecdReadMessage(u32 title_id, bool is_outbox, CecMessageId message_id, u8* buf, u32 size) { + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x03, 4, 4); + cmdbuf[1] = title_id; + cmdbuf[2] = (u32)is_outbox; + cmdbuf[3] = 8; // message id size + cmdbuf[4] = size; + + cmdbuf[5] = IPC_Desc_Buffer(8, IPC_BUFFER_R); + cmdbuf[6] = (u32)message_id; + cmdbuf[7] = IPC_Desc_Buffer(size, IPC_BUFFER_W); + cmdbuf[8] = (u32)buf; + + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + + return res; +} + +Result cecdReadMessageWithHMAC(u32 title_id, bool is_outbox, CecMessageId message_id, u8* buf, u32 size, u8* hmac) { + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x03, 4, 4); + cmdbuf[1] = title_id; + cmdbuf[2] = (u32)is_outbox; + cmdbuf[3] = 8; // message id size + cmdbuf[4] = size; + + cmdbuf[5] = IPC_Desc_Buffer(8, IPC_BUFFER_R); + cmdbuf[6] = (u32)message_id; + cmdbuf[7] = IPC_Desc_Buffer(32, IPC_BUFFER_R); + cmdbuf[8] = (u32)hmac; + cmdbuf[9] = IPC_Desc_Buffer(size, IPC_BUFFER_W); + cmdbuf[10] = (u32)buf; + + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + + return res; +} + +Result cecdWriteMessage(u32 title_id, bool is_outbox, CecMessageId message_id, u8* buf, u32 size) { + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x06, 4, 4); + cmdbuf[1] = title_id; + cmdbuf[2] = (u32)is_outbox; + cmdbuf[3] = 8; // message id size + cmdbuf[4] = size; + + cmdbuf[5] = IPC_Desc_Buffer(size, IPC_BUFFER_R); + cmdbuf[6] = (u32)buf; + cmdbuf[7] = IPC_Desc_Buffer(8, IPC_BUFFER_RW); + cmdbuf[8] = (u32)message_id; + + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + + return res; +} + +Result cecdWriteMessageWithHMAC(u32 title_id, bool is_outbox, CecMessageId message_id, u8* buf, u32 size, u8* hmac) { + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x07, 4, 6); + cmdbuf[1] = title_id; + cmdbuf[2] = (u32)is_outbox; + cmdbuf[3] = 8; // message id size + cmdbuf[4] = size; + + cmdbuf[5] = IPC_Desc_Buffer(size, IPC_BUFFER_R); + cmdbuf[6] = (u32)buf; + cmdbuf[7] = IPC_Desc_Buffer(32, IPC_BUFFER_R); + cmdbuf[8] = (u32)hmac; + cmdbuf[9] = IPC_Desc_Buffer(8, IPC_BUFFER_RW); + cmdbuf[10] = (u32)message_id; + + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + + return res; +} + +Result cecdStart(CecCommand command) { + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x0B, 1, 0); + cmdbuf[1] = command; + + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + + return res; +} + +Result cecdStop(CecCommand command) { + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x0C, 1, 0); + cmdbuf[1] = command; + + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + + return res; +} + +Result cecdGetState(CecStateAbbrev* state) { + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0xE, 0, 0); + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + *state = cmdbuf[2]; + + return res; +} + +Result cecdGetCecInfoEventHandle(Handle* handle) { + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0xF, 0, 0); + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + *handle = cmdbuf[3]; + + return res; +} + +Result cecdGetChangeStateEventHandle(Handle* handle) { + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x10, 0, 0); + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + *handle = cmdbuf[3]; + + return res; +} + +Result cecdOpenAndWrite(u32 title_id, CecPath path_type, u8* buf, u32 size) { + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x11, 4, 4); + cmdbuf[1] = size; + cmdbuf[2] = title_id; + cmdbuf[3] = path_type; + cmdbuf[4] = 0; + + cmdbuf[5] = IPC_Desc_CurProcessId(); + cmdbuf[6] = 0; + cmdbuf[7] = IPC_Desc_Buffer(size, IPC_BUFFER_R); + cmdbuf[8] = (u32)buf; + + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + + return res; +} + +Result cecdOpenAndRead(u32 title_id, CecPath path_type, u8* buf, u32 size) { + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x12, 4, 4); + cmdbuf[1] = size; + cmdbuf[2] = title_id; + cmdbuf[3] = path_type; + cmdbuf[4] = 0; + + cmdbuf[5] = IPC_Desc_CurProcessId(); + cmdbuf[6] = 0; + cmdbuf[7] = IPC_Desc_Buffer(size, IPC_BUFFER_W); + cmdbuf[8] = (u32)buf; + + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + + return res; +} + +Result cecdSprCreate(void) { + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x40A, 0, 0); + + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + + return res; +} + +Result cecdSprInitialise(void) { + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x40B, 0, 0); + + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + + return res; +} + +Result cecdSprGetSlotsMetadata(CecSlotMetadata* buf, u32 size, u32* slots_total) { + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x40C, 1, 2); + cmdbuf[1] = size; + + cmdbuf[2] = IPC_Desc_Buffer(size, IPC_BUFFER_W); + cmdbuf[3] = (u32)buf; + + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + *slots_total = cmdbuf[2]; + + return res; +} + +Result cecdSprGetSlot(u32 title_id, u8* buf, u32 size) { + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x40D, 2, 2); + cmdbuf[1] = title_id; + cmdbuf[2] = size; + + cmdbuf[3] = IPC_Desc_Buffer(size, IPC_BUFFER_W); + cmdbuf[4] = (u32)buf; + + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + + return res; +} + +Result cecdSprSetTitleSent(u32 title_id, bool success) { // set send result + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x40E, 2, 0); + cmdbuf[1] = title_id; + cmdbuf[2] = success; + + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + + return res; +} + +Result cecdSprFinaliseSend(void) { + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x40F, 0, 0); + + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + + return res; +} + +Result cecdSprStartRecv(void) { + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x410, 0, 0); + + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + + return res; +} + +Result cecdSprAddSlotsMetadata(u8* buf, u32 size) { + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x411, 1, 2); + cmdbuf[1] = size; + + cmdbuf[2] = IPC_Desc_Buffer(size, IPC_BUFFER_R); + cmdbuf[3] = (u32)buf; + + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + + return res; +} + +Result cecdSprAddSlot(u32 title_id, u8* buf, u32 size) { + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x412, 3, 2); + cmdbuf[1] = title_id; + cmdbuf[2] = 0xFF; // flags + cmdbuf[3] = size; + + cmdbuf[4] = IPC_Desc_Buffer(size, IPC_BUFFER_R); + cmdbuf[5] = (u32)buf; + + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + + return res; +} + +Result cecdSprFinaliseRecv(void) { + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x413, 0, 0); + + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + + return res; +} + +Result cecdSprDone(bool success) { + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x414, 1, 0); + cmdbuf[1] = success; + + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + + return res; +} + +Result cecdGetBossUserid(u64* out) { + Result res = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = IPC_MakeHeader(0x415, 0, 0); + + if (R_FAILED(res = svcSendSyncRequest(cecdHandle))) return res; + res = (Result)cmdbuf[1]; + *out = (u64)cmdbuf[2] | ((u64)cmdbuf[3] << 32); + + return res; +} + +Handle cecdGetServHandle(void) { + return cecdHandle; +}