aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--nimgen.nimble1
-rw-r--r--tests/rununittests.nim8
-rw-r--r--tests/unittests/common.nim5
-rw-r--r--tests/unittests/data/teststaticexpectedfrontbraces.h675
-rw-r--r--tests/unittests/data/teststaticexpectednewlinebraces.h68
-rw-r--r--tests/unittests/data/teststaticfrontbraces.h675
-rw-r--r--tests/unittests/data/teststaticnewlinebraces.h71
-rw-r--r--tests/unittests/data/teststaticnewlinebracesreadded.h68
-rw-r--r--tests/unittests/nim.cfg1
-rw-r--r--tests/unittests/testfileops.nim211
10 files changed, 1783 insertions, 0 deletions
diff --git a/nimgen.nimble b/nimgen.nimble
index 59de8ed..3e65545 100644
--- a/nimgen.nimble
+++ b/nimgen.nimble
@@ -14,4 +14,5 @@ skipDirs = @["nimgen", "tests"]
requires "nim >= 0.17.0", "c2nim >= 0.9.13", "regex >= 0.7.1"
task test, "Test nimgen":
+ exec "nim c -r tests/rununittests.nim"
exec "nim e tests/nimgentest.nims"
diff --git a/tests/rununittests.nim b/tests/rununittests.nim
new file mode 100644
index 0000000..a92ff0a
--- /dev/null
+++ b/tests/rununittests.nim
@@ -0,0 +1,8 @@
+import os, osproc, strutils
+
+proc main() =
+ for file in walkFiles(currentSourcePath().splitPath().head / "unittests/*.nim"):
+ let (path, fname, ext) = file.splitFile()
+ if fname.startswith("test"):
+ discard execCmd "nim c -r " & file
+main()
diff --git a/tests/unittests/common.nim b/tests/unittests/common.nim
new file mode 100644
index 0000000..1769b01
--- /dev/null
+++ b/tests/unittests/common.nim
@@ -0,0 +1,5 @@
+import unittest
+
+proc checkFile*(filepath, expected: string) =
+ let result = readFile(filepath)
+ check result == expected
diff --git a/tests/unittests/data/teststaticexpectedfrontbraces.h b/tests/unittests/data/teststaticexpectedfrontbraces.h
new file mode 100644
index 0000000..d8c6a0d
--- /dev/null
+++ b/tests/unittests/data/teststaticexpectedfrontbraces.h
@@ -0,0 +1,675 @@
+/**
+ * @file ipc.h
+ * @brief Inter-process communication handling
+ * @author plutoo
+ * @copyright libnx Authors
+ */
+#pragma once
+#include "../result.h"
+#include "../arm/tls.h"
+#include "../kernel/svc.h"
+
+/// IPC input header magic
+#define SFCI_MAGIC 0x49434653
+/// IPC output header magic
+#define SFCO_MAGIC 0x4f434653
+
+/// IPC invalid object ID
+#define IPC_INVALID_OBJECT_ID UINT32_MAX
+
+///@name IPC request building
+///@{
+
+/// IPC command (request) structure.
+#define IPC_MAX_BUFFERS 8
+#define IPC_MAX_OBJECTS 8
+
+typedef enum {
+ BufferType_Normal=0, ///< Regular buffer.
+ BufferType_Type1=1, ///< Allows ProcessMemory and shared TransferMemory.
+ BufferType_Invalid=2,
+ BufferType_Type3=3 ///< Same as Type1 except remote process is not allowed to use device-mapping.
+} BufferType;
+
+typedef enum {
+ BufferDirection_Send=0,
+ BufferDirection_Recv=1,
+ BufferDirection_Exch=2,
+} BufferDirection;
+
+typedef enum {
+ IpcCommandType_Invalid = 0,
+ IpcCommandType_LegacyRequest = 1,
+ IpcCommandType_Close = 2,
+ IpcCommandType_LegacyControl = 3,
+ IpcCommandType_Request = 4,
+ IpcCommandType_Control = 5,
+ IpcCommandType_RequestWithContext = 6,
+ IpcCommandType_ControlWithContext = 7,
+} IpcCommandType;
+
+typedef enum {
+ DomainMessageType_Invalid = 0,
+ DomainMessageType_SendMessage = 1,
+ DomainMessageType_Close = 2,
+} DomainMessageType;
+
+/// IPC domain message header.
+typedef struct {
+ u8 Type;
+ u8 NumObjectIds;
+ u16 Length;
+ u32 ThisObjectId;
+ u32 Pad[2];
+} DomainMessageHeader;
+
+typedef struct {
+ size_t NumSend; // A
+ size_t NumRecv; // B
+ size_t NumExch; // W
+ const void* Buffers[IPC_MAX_BUFFERS];
+ size_t BufferSizes[IPC_MAX_BUFFERS];
+ BufferType BufferTypes[IPC_MAX_BUFFERS];
+
+ size_t NumStaticIn; // X
+ size_t NumStaticOut; // C
+ const void* Statics[IPC_MAX_BUFFERS];
+ size_t StaticSizes[IPC_MAX_BUFFERS];
+ u8 StaticIndices[IPC_MAX_BUFFERS];
+
+ bool SendPid;
+ size_t NumHandlesCopy;
+ size_t NumHandlesMove;
+ Handle Handles[IPC_MAX_OBJECTS];
+
+ size_t NumObjectIds;
+ u32 ObjectIds[IPC_MAX_OBJECTS];
+} IpcCommand;
+
+/**
+ * @brief Initializes an IPC command structure.
+ * @param cmd IPC command structure.
+ */
+static inline void ipcInitialize(IpcCommand* cmd);//{
+// *cmd = (IpcCommand){0};
+//}
+
+/// IPC buffer descriptor.
+typedef struct {
+ u32 Size; ///< Size of the buffer.
+ u32 Addr; ///< Lower 32-bits of the address of the buffer
+ u32 Packed; ///< Packed data (including higher bits of the address)
+} IpcBufferDescriptor;
+
+/// IPC static send-buffer descriptor.
+typedef struct {
+ u32 Packed; ///< Packed data (including higher bits of the address)
+ u32 Addr; ///< Lower 32-bits of the address
+} IpcStaticSendDescriptor;
+
+/// IPC static receive-buffer descriptor.
+typedef struct {
+ u32 Addr; ///< Lower 32-bits of the address of the buffer
+ u32 Packed; ///< Packed data (including higher bits of the address)
+} IpcStaticRecvDescriptor;
+
+/**
+ * @brief Adds a buffer to an IPC command structure.
+ * @param cmd IPC command structure.
+ * @param buffer Address of the buffer.
+ * @param size Size of the buffer.
+ * @param type Buffer type.
+ */
+static inline void ipcAddSendBuffer(IpcCommand* cmd, const void* buffer, size_t size, BufferType type);//{
+// size_t off = cmd->NumSend;
+// cmd->Buffers[off] = buffer;
+// cmd->BufferSizes[off] = size;
+// cmd->BufferTypes[off] = type;
+// cmd->NumSend++;
+//}
+
+/**
+ * @brief Adds a receive-buffer to an IPC command structure.
+ * @param cmd IPC command structure.
+ * @param buffer Address of the buffer.
+ * @param size Size of the buffer.
+ * @param type Buffer type.
+ */
+static inline void ipcAddRecvBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type);//{
+// size_t off = cmd->NumSend + cmd->NumRecv;
+// cmd->Buffers[off] = buffer;
+// cmd->BufferSizes[off] = size;
+// cmd->BufferTypes[off] = type;
+// cmd->NumRecv++;
+//}
+
+/**
+ * @brief Adds an exchange-buffer to an IPC command structure.
+ * @param cmd IPC command structure.
+ * @param buffer Address of the buffer.
+ * @param size Size of the buffer.
+ * @param type Buffer type.
+ */
+static inline void ipcAddExchBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type);//{
+// size_t off = cmd->NumSend + cmd->NumRecv + cmd->NumExch;
+// cmd->Buffers[off] = buffer;
+// cmd->BufferSizes[off] = size;
+// cmd->BufferTypes[off] = type;
+// cmd->NumExch++;
+//}
+
+/**
+ * @brief Adds a static-buffer to an IPC command structure.
+ * @param cmd IPC command structure.
+ * @param buffer Address of the buffer.
+ * @param size Size of the buffer.
+ * @param index Index of buffer.
+ */
+static inline void ipcAddSendStatic(IpcCommand* cmd, const void* buffer, size_t size, u8 index);//{
+// size_t off = cmd->NumStaticIn;
+// cmd->Statics[off] = buffer;
+// cmd->StaticSizes[off] = size;
+// cmd->StaticIndices[off] = index;
+// cmd->NumStaticIn++;
+//}
+
+/**
+ * @brief Adds a static-receive-buffer to an IPC command structure.
+ * @param cmd IPC command structure.
+ * @param buffer Address of the buffer.
+ * @param size Size of the buffer.
+ * @param index Index of buffer.
+ */
+static inline void ipcAddRecvStatic(IpcCommand* cmd, void* buffer, size_t size, u8 index);//{
+// size_t off = cmd->NumStaticIn + cmd->NumStaticOut;
+// cmd->Statics[off] = buffer;
+// cmd->StaticSizes[off] = size;
+// cmd->StaticIndices[off] = index;
+// cmd->NumStaticOut++;
+//}
+
+/**
+ * @brief Adds a smart-buffer (buffer + static-buffer pair) to an IPC command structure.
+ * @param cmd IPC command structure.
+ * @param ipc_buffer_size IPC buffer size.
+ * @param buffer Address of the buffer.
+ * @param size Size of the buffer.
+ * @param index Index of buffer.
+ */
+static inline void ipcAddSendSmart(IpcCommand* cmd, size_t ipc_buffer_size, const void* buffer, size_t size, u8 index);//{
+// if (ipc_buffer_size != 0 && size <= ipc_buffer_size) {
+// ipcAddSendBuffer(cmd, NULL, 0, BufferType_Normal);
+// ipcAddSendStatic(cmd, buffer, size, index);
+// } else {
+// ipcAddSendBuffer(cmd, buffer, size, BufferType_Normal);
+// ipcAddSendStatic(cmd, NULL, 0, index);
+// }
+//}
+
+/**
+ * @brief Adds a smart-receive-buffer (buffer + static-receive-buffer pair) to an IPC command structure.
+ * @param cmd IPC command structure.
+ * @param ipc_buffer_size IPC buffer size.
+ * @param buffer Address of the buffer.
+ * @param size Size of the buffer.
+ * @param index Index of buffer.
+ */
+static inline void ipcAddRecvSmart(IpcCommand* cmd, size_t ipc_buffer_size, void* buffer, size_t size, u8 index);//{
+// if (ipc_buffer_size != 0 && size <= ipc_buffer_size) {
+// ipcAddRecvBuffer(cmd, NULL, 0, BufferType_Normal);
+// ipcAddRecvStatic(cmd, buffer, size, index);
+// } else {
+// ipcAddRecvBuffer(cmd, buffer, size, BufferType_Normal);
+// ipcAddRecvStatic(cmd, NULL, 0, index);
+// }
+//}
+
+/**
+ * @brief Tags an IPC command structure to send the PID.
+ * @param cmd IPC command structure.
+ */
+static inline void ipcSendPid(IpcCommand* cmd);//{
+// cmd->SendPid = true;
+//}
+
+/**
+ * @brief Adds a copy-handle to be sent through an IPC command structure.
+ * @param cmd IPC command structure.
+ * @param h Handle to send.
+ * @remark The receiving process gets a copy of the handle.
+ */
+static inline void ipcSendHandleCopy(IpcCommand* cmd, Handle h);//{
+// cmd->Handles[cmd->NumHandlesCopy++] = h;
+//}
+
+/**
+ * @brief Adds a move-handle to be sent through an IPC command structure.
+ * @param cmd IPC command structure.
+ * @param h Handle to send.
+ * @remark The sending process loses ownership of the handle, which is transferred to the receiving process.
+ */
+static inline void ipcSendHandleMove(IpcCommand* cmd, Handle h);//{
+// cmd->Handles[cmd->NumHandlesCopy + cmd->NumHandlesMove++] = h;
+//}
+
+/**
+ * @brief Prepares the header of an IPC command structure.
+ * @param cmd IPC command structure.
+ * @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request
+ * @return Pointer to the raw embedded data structure in the request, ready to be filled out.
+ */
+static inline void* ipcPrepareHeader(IpcCommand* cmd, size_t sizeof_raw);//{
+// u32* buf = (u32*)armGetTls();
+// size_t i;
+// *buf++ = IpcCommandType_Request | (cmd->NumStaticIn << 16) | (cmd->NumSend << 20) | (cmd->NumRecv << 24) | (cmd->NumExch << 28);
+//
+// u32* fill_in_size_later = buf;
+//
+// if (cmd->NumStaticOut > 0) {
+// *buf = (cmd->NumStaticOut + 2) << 10;
+// }
+// else {
+// *buf = 0;
+// }
+//
+// if (cmd->SendPid || cmd->NumHandlesCopy > 0 || cmd->NumHandlesMove > 0) {
+// *buf++ |= 0x80000000;
+// *buf++ = (!!cmd->SendPid) | (cmd->NumHandlesCopy << 1) | (cmd->NumHandlesMove << 5);
+//
+// if (cmd->SendPid)
+// buf += 2;
+//
+// for (i=0; i<(cmd->NumHandlesCopy + cmd->NumHandlesMove); i++)
+// *buf++ = cmd->Handles[i];
+// }
+// else {
+// buf++;
+// }
+//
+// for (i=0; i<cmd->NumStaticIn; i++, buf+=2) {
+// IpcStaticSendDescriptor* desc = (IpcStaticSendDescriptor*) buf;
+//
+// uintptr_t ptr = (uintptr_t) cmd->Statics[i];
+// desc->Addr = ptr;
+// desc->Packed = cmd->StaticIndices[i] | (cmd->StaticSizes[i] << 16) |
+// (((ptr >> 32) & 15) << 12) | (((ptr >> 36) & 15) << 6);
+// }
+//
+// for (i=0; i<(cmd->NumSend + cmd->NumRecv + cmd->NumExch); i++, buf+=3) {
+// IpcBufferDescriptor* desc = (IpcBufferDescriptor*) buf;
+// desc->Size = cmd->BufferSizes[i];
+//
+// uintptr_t ptr = (uintptr_t) cmd->Buffers[i];
+// desc->Addr = ptr;
+// desc->Packed = cmd->BufferTypes[i] |
+// (((ptr >> 32) & 15) << 28) | ((ptr >> 36) << 2);
+// }
+//
+// u32 padding = ((16 - (((uintptr_t) buf) & 15)) & 15) / 4;
+// u32* raw = (u32*) (buf + padding);
+//
+// size_t raw_size = (sizeof_raw/4) + 4;
+// buf += raw_size;
+//
+// u16* buf_u16 = (u16*) buf;
+//
+// for (i=0; i<cmd->NumStaticOut; i++) {
+// size_t off = cmd->NumStaticIn + i;
+// size_t sz = (uintptr_t) cmd->StaticSizes[off];
+//
+// buf_u16[i] = (sz > 0xFFFF) ? 0 : sz;
+// }
+//
+// size_t u16s_size = ((2*cmd->NumStaticOut) + 3)/4;
+// buf += u16s_size;
+// raw_size += u16s_size;
+//
+// *fill_in_size_later |= raw_size;
+//
+// for (i=0; i<cmd->NumStaticOut; i++, buf+=2) {
+// IpcStaticRecvDescriptor* desc = (IpcStaticRecvDescriptor*) buf;
+// size_t off = cmd->NumStaticIn + i;
+//
+// uintptr_t ptr = (uintptr_t) cmd->Statics[off];
+// desc->Addr = ptr;
+// desc->Packed = (ptr >> 32) | (cmd->StaticSizes[off] << 16);
+// }
+//
+// return (void*) raw;
+//}
+
+/**
+ * @brief Dispatches an IPC request.
+ * @param session IPC session handle.
+ * @return Result code.
+ */
+static inline Result ipcDispatch(Handle session);//{
+// return svcSendSyncRequest(session);
+//}
+
+///@}
+
+///@name IPC response parsing
+///@{
+
+/// IPC parsed command (response) structure.
+typedef struct {
+ IpcCommandType CommandType; ///< Type of the command
+
+ bool HasPid; ///< true if the 'Pid' field is filled out.
+ u64 Pid; ///< PID included in the response (only if HasPid is true)
+
+ size_t NumHandles; ///< Number of handles copied.
+ Handle Handles[IPC_MAX_OBJECTS]; ///< Handles.
+ bool WasHandleCopied[IPC_MAX_OBJECTS]; ///< true if the handle was moved, false if it was copied.
+
+ bool IsDomainMessage; ///< true if the the message is a Domain message.
+ DomainMessageType MessageType; ///< Type of the domain message.
+ u32 MessageLength; ///< Size of rawdata (for domain messages).
+ u32 ThisObjectId; ///< Object ID to call the command on (for domain messages).
+ size_t NumObjectIds; ///< Number of object IDs (for domain messages).
+ u32 ObjectIds[IPC_MAX_OBJECTS]; ///< Object IDs (for domain messages).
+
+ size_t NumBuffers; ///< Number of buffers in the response.
+ void* Buffers[IPC_MAX_BUFFERS]; ///< Pointers to the buffers.
+ size_t BufferSizes[IPC_MAX_BUFFERS]; ///< Sizes of the buffers.
+ BufferType BufferTypes[IPC_MAX_BUFFERS]; ///< Types of the buffers.
+ BufferDirection BufferDirections[IPC_MAX_BUFFERS]; ///< Direction of each buffer.
+
+ size_t NumStatics; ///< Number of statics in the response.
+ void* Statics[IPC_MAX_BUFFERS]; ///< Pointers to the statics.
+ size_t StaticSizes[IPC_MAX_BUFFERS]; ///< Sizes of the statics.
+ u8 StaticIndices[IPC_MAX_BUFFERS]; ///< Indices of the statics.
+
+ size_t NumStaticsOut; ///< Number of output statics available in the response.
+
+ void* Raw; ///< Pointer to the raw embedded data structure in the response.
+ void* RawWithoutPadding; ///< Pointer to the raw embedded data structure, without padding.
+ size_t RawSize; ///< Size of the raw embedded data.
+} IpcParsedCommand;
+
+/**
+ * @brief Parse an IPC command response into an IPC parsed command structure.
+ * @param IPC parsed command structure to fill in.
+ * @return Result code.
+ */
+static inline Result ipcParse(IpcParsedCommand* r);//{
+// u32* buf = (u32*)armGetTls();
+// u32 ctrl0 = *buf++;
+// u32 ctrl1 = *buf++;
+// size_t i;
+//
+// r->IsDomainMessage = false;
+//
+// r->CommandType = (IpcCommandType) (ctrl0 & 0xffff);
+// r->HasPid = false;
+// r->RawSize = (ctrl1 & 0x1ff) * 4;
+// r->NumHandles = 0;
+//
+// r->NumStaticsOut = (ctrl1 >> 10) & 15;
+// if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 2 -> Single descriptor
+// if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 3+ -> (Value - 2) descriptors
+//
+// if (ctrl1 & 0x80000000) {
+// u32 ctrl2 = *buf++;
+//
+// if (ctrl2 & 1) {
+// r->HasPid = true;
+// r->Pid = *buf++;
+// r->Pid |= ((u64)(*buf++)) << 32;
+// }
+//
+// size_t num_handles_copy = ((ctrl2 >> 1) & 15);
+// size_t num_handles_move = ((ctrl2 >> 5) & 15);
+//
+// size_t num_handles = num_handles_copy + num_handles_move;
+// u32* buf_after_handles = buf + num_handles;
+//
+// if (num_handles > IPC_MAX_OBJECTS)
+// num_handles = IPC_MAX_OBJECTS;
+//
+// for (i=0; i<num_handles; i++)
+// {
+// r->Handles[i] = *(buf+i);
+// r->WasHandleCopied[i] = (i < num_handles_copy);
+// }
+//
+// r->NumHandles = num_handles;
+// buf = buf_after_handles;
+// }
+//
+// size_t num_statics = (ctrl0 >> 16) & 15;
+// u32* buf_after_statics = buf + num_statics*2;
+//
+// if (num_statics > IPC_MAX_BUFFERS)
+// num_statics = IPC_MAX_BUFFERS;
+//
+// for (i=0; i<num_statics; i++, buf+=2) {
+// IpcStaticSendDescriptor* desc = (IpcStaticSendDescriptor*) buf;
+// u64 packed = (u64) desc->Packed;
+//
+// r->Statics[i] = (void*) (desc->Addr | (((packed >> 12) & 15) << 32) | (((packed >> 6) & 15) << 36));
+// r->StaticSizes[i] = packed >> 16;
+// r->StaticIndices[i] = packed & 63;
+// }
+//
+// r->NumStatics = num_statics;
+// buf = buf_after_statics;
+//
+// size_t num_bufs_send = (ctrl0 >> 20) & 15;
+// size_t num_bufs_recv = (ctrl0 >> 24) & 15;
+// size_t num_bufs_exch = (ctrl0 >> 28) & 15;
+//
+// size_t num_bufs = num_bufs_send + num_bufs_recv + num_bufs_exch;
+// r->Raw = (void*)(((uintptr_t)(buf + num_bufs*3) + 15) &~ 15);
+// r->RawWithoutPadding = (void*)((uintptr_t)(buf + num_bufs*3));
+//
+// if (num_bufs > IPC_MAX_BUFFERS)
+// num_bufs = IPC_MAX_BUFFERS;
+//
+// for (i=0; i<num_bufs; i++, buf+=3) {
+// IpcBufferDescriptor* desc = (IpcBufferDescriptor*) buf;
+// u64 packed = (u64) desc->Packed;
+//
+// r->Buffers[i] = (void*) (desc->Addr | ((packed >> 28) << 32) | (((packed >> 2) & 15) << 36));
+// r->BufferSizes[i] = desc->Size;
+// r->BufferTypes[i] = (BufferType) (packed & 3);
+//
+// if (i < num_bufs_send)
+// r->BufferDirections[i] = BufferDirection_Send;
+// else if (i < (num_bufs_send + num_bufs_recv))
+// r->BufferDirections[i] = BufferDirection_Recv;
+// else
+// r->BufferDirections[i] = BufferDirection_Exch;
+// }
+//
+// r->NumBuffers = num_bufs;
+// return 0;
+//}
+
+/**
+ * @brief Queries the size of an IPC pointer buffer.
+ * @param session IPC session handle.
+ * @param size Output variable in which to store the size.
+ * @return Result code.
+ */
+static inline Result ipcQueryPointerBufferSize(Handle session, size_t *size);//{
+// u32* buf = (u32*)armGetTls();
+//
+// buf[0] = IpcCommandType_Control;
+// buf[1] = 8;
+// buf[2] = 0;
+// buf[3] = 0;
+// buf[4] = SFCI_MAGIC;
+// buf[5] = 0;
+// buf[6] = 3;
+// buf[7] = 0;
+//
+// Result rc = ipcDispatch(session);
+//
+// if (R_SUCCEEDED(rc)) {
+// IpcParsedCommand r;
+// ipcParse(&r);
+//
+// struct ipcQueryPointerBufferSizeResponse {
+// u64 magic;
+// u64 result;
+// u32 size;
+// } *raw = (struct ipcQueryPointerBufferSizeResponse*)r.Raw;
+//
+// rc = raw->result;
+//
+// if (R_SUCCEEDED(rc)) {
+// *size = raw->size & 0xffff;
+// }
+// }
+//
+// return rc;
+//}
+
+/**
+ * @brief Closes the IPC session with proper clean up.
+ * @param session IPC session handle.
+ * @return Result code.
+ */
+static inline Result ipcCloseSession(Handle session);//{
+// u32* buf = (u32*)armGetTls();
+// buf[0] = IpcCommandType_Close;
+// buf[1] = 0;
+// return ipcDispatch(session);
+//}
+///@}
+
+///@name IPC domain handling
+///@{
+
+/**
+ * @brief Converts an IPC session handle into a domain.
+ * @param session IPC session handle.
+ * @param object_id_out Output variable in which to store the object ID.
+ * @return Result code.
+ */
+static inline Result ipcConvertSessionToDomain(Handle session, u32* object_id_out);//{
+// u32* buf = (u32*)armGetTls();
+//
+// buf[0] = IpcCommandType_Control;
+// buf[1] = 8;
+// buf[4] = SFCI_MAGIC;
+// buf[5] = 0;
+// buf[6] = 0;
+// buf[7] = 0;
+//
+// Result rc = ipcDispatch(session);
+//
+// if (R_SUCCEEDED(rc)) {
+// IpcParsedCommand r;
+// ipcParse(&r);
+//
+// struct ipcConvertSessionToDomainResponse {
+// u64 magic;
+// u64 result;
+// u32 object_id;
+// } *raw = (struct ipcConvertSessionToDomainResponse*)r.Raw;
+//
+// rc = raw->result;
+//
+// if (R_SUCCEEDED(rc)) {
+// *object_id_out = raw->object_id;
+// }
+// }
+//
+// return rc;
+//}
+
+/**
+ * @brief Adds an object ID to be sent through an IPC domain command structure.
+ * @param cmd IPC domain command structure.
+ * @param object_id Object ID to send.
+ */
+static inline void ipcSendObjectId(IpcCommand* cmd, u32 object_id);//{
+// cmd->ObjectIds[cmd->NumObjectIds++] = object_id;
+//}
+
+/**
+ * @brief Prepares the header of an IPC command structure (domain version).
+ * @param cmd IPC command structure.
+ * @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request
+ * @oaram object_id Domain object ID.
+ * @return Pointer to the raw embedded data structure in the request, ready to be filled out.
+ */
+static inline void* ipcPrepareHeaderForDomain(IpcCommand* cmd, size_t sizeof_raw, u32 object_id);//{
+// void* raw = ipcPrepareHeader(cmd, sizeof_raw + sizeof(DomainMessageHeader));
+// DomainMessageHeader* hdr = (DomainMessageHeader*) raw;
+// u32 *object_ids = (u32*)(((uintptr_t) raw) + sizeof(DomainMessageHeader) + sizeof_raw);
+//
+// hdr->Type = DomainMessageType_SendMessage;
+// hdr->NumObjectIds = (u8)cmd->NumObjectIds;
+// hdr->Length = sizeof_raw;
+// hdr->ThisObjectId = object_id;
+// hdr->Pad[0] = hdr->Pad[1] = 0;
+//
+// for(size_t i = 0; i < cmd->NumObjectIds; i++)
+// object_ids[i] = cmd->ObjectIds[i];
+// return (void*)(((uintptr_t) raw) + sizeof(DomainMessageHeader));
+//}
+
+/**
+ * @brief Parse an IPC command response into an IPC parsed command structure (domain version).
+ * @param IPC parsed command structure to fill in.
+ * @return Result code.
+ */
+static inline Result ipcParseForDomain(IpcParsedCommand* r);//{
+// Result rc = ipcParse(r);
+// DomainMessageHeader *hdr;
+// u32 *object_ids;
+// if(R_FAILED(rc))
+// return rc;
+//
+// hdr = (DomainMessageHeader*) r->Raw;
+// object_ids = (u32*)(((uintptr_t) hdr) + sizeof(DomainMessageHeader) + hdr->Length);
+// r->Raw = (void*)(((uintptr_t) r->Raw) + sizeof(DomainMessageHeader));
+//
+// r->IsDomainMessage = true;
+// r->MessageType = (DomainMessageType)(hdr->Type);
+// switch (r->MessageType) {
+// case DomainMessageType_SendMessage:
+// case DomainMessageType_Close:
+// break;
+// default:
+// return MAKERESULT(Module_Libnx, LibnxError_DomainMessageUnknownType);
+// }
+// r->ThisObjectId = hdr->ThisObjectId;
+// r->NumObjectIds = hdr->NumObjectIds > 8 ? 8 : hdr->NumObjectIds;
+// if ((uintptr_t)object_ids + sizeof(u32) * r->NumObjectIds - (uintptr_t)armGetTls() >= 0x100) {
+// return MAKERESULT(Module_Libnx, LibnxError_DomainMessageTooManyObjectIds);
+// }
+// for(size_t i = 0; i < r->NumObjectIds; i++)
+// r->ObjectIds[i] = object_ids[i];
+//
+// return rc;
+//}
+
+/**
+ * @brief Closes a domain object by ID.
+ * @param session IPC session handle.
+ * @param object_id ID of the object to close.
+ * @return Result code.
+ */
+static inline Result ipcCloseObjectById(Handle session, u32 object_id);//{
+// IpcCommand c;
+// DomainMessageHeader* hdr;
+//
+// ipcInitialize(&c);
+// hdr = (DomainMessageHeader*)ipcPrepareHeader(&c, sizeof(DomainMessageHeader));
+//
+// hdr->Type = 2;
+// hdr->NumObjectIds = 0;
+// hdr->Length = 0;
+// hdr->ThisObjectId = object_id;
+// hdr->Pad[0] = hdr->Pad[1] = 0;
+//
+// return ipcDispatch(session); // this command has no associated response
+//}
+
+///@}
+
diff --git a/tests/unittests/data/teststaticexpectednewlinebraces.h b/tests/unittests/data/teststaticexpectednewlinebraces.h
new file mode 100644
index 0000000..3f90ab6
--- /dev/null
+++ b/tests/unittests/data/teststaticexpectednewlinebraces.h
@@ -0,0 +1,68 @@
+/**
+ * @file condvar.h
+ * @brief Condition variable synchronization primitive.
+ * @author plutoo
+ * @copyright libnx Authors
+ */
+#pragma once
+#include "../types.h"
+#include "../kernel/mutex.h"
+
+/// Condition variable structure.
+typedef struct {
+ u32 tag;
+ Mutex* mutex;
+} CondVar;
+
+/**
+ * @brief Initializes a condition variable.
+ * @param[in] c Condition variable object.
+ * @param[in] m Mutex object to use inside the condition variable.
+ */
+void condvarInit(CondVar* c, Mutex* m);
+
+/**
+ * @brief Waits on a condition variable with a timeout.
+ * @param[in] c Condition variable object.
+ * @param[in] timeout Timeout in nanoseconds.
+ * @return Result code (0xEA01 on timeout).
+ * @remark On function return, the underlying mutex is acquired.
+ */
+Result condvarWaitTimeout(CondVar* c, u64 timeout);
+
+/**
+ * @brief Waits on a condition variable.
+ * @param[in] c Condition variable object.
+ * @return Result code.
+ * @remark On function return, the underlying mutex is acquired.
+ */
+static inline Result condvarWait(CondVar* c);//{
+// return condvarWaitTimeout(c, -1ull);
+//}
+
+/**
+ * @brief Wakes up up to the specified number of threads waiting on a condition variable.
+ * @param[in] c Condition variable object.
+ * @param[in] num Maximum number of threads to wake up (or -1 to wake them all up).
+ * @return Result code.
+ */
+Result condvarWake(CondVar* c, int num);
+
+/**
+ * @brief Wakes up a single thread waiting on a condition variable.
+ * @param[in] c Condition variable object.
+ * @return Result code.
+ */
+static inline Result condvarWakeOne(CondVar* c);//{
+// return condvarWake(c, 1);
+//}
+
+/**
+ * @brief Wakes up all thread waiting on a condition variable.
+ * @param[in] c Condition variable object.
+ * @return Result code.
+ */
+static inline Result condvarWakeAll(CondVar* c);//{
+// return condvarWake(c, -1);
+//}
+
diff --git a/tests/unittests/data/teststaticfrontbraces.h b/tests/unittests/data/teststaticfrontbraces.h
new file mode 100644
index 0000000..d62f694
--- /dev/null
+++ b/tests/unittests/data/teststaticfrontbraces.h
@@ -0,0 +1,675 @@
+/**
+ * @file ipc.h
+ * @brief Inter-process communication handling
+ * @author plutoo
+ * @copyright libnx Authors
+ */
+#pragma once
+#include "../result.h"
+#include "../arm/tls.h"
+#include "../kernel/svc.h"
+
+/// IPC input header magic
+#define SFCI_MAGIC 0x49434653
+/// IPC output header magic
+#define SFCO_MAGIC 0x4f434653
+
+/// IPC invalid object ID
+#define IPC_INVALID_OBJECT_ID UINT32_MAX
+
+///@name IPC request building
+///@{
+
+/// IPC command (request) structure.
+#define IPC_MAX_BUFFERS 8
+#define IPC_MAX_OBJECTS 8
+
+typedef enum {
+ BufferType_Normal=0, ///< Regular buffer.
+ BufferType_Type1=1, ///< Allows ProcessMemory and shared TransferMemory.
+ BufferType_Invalid=2,
+ BufferType_Type3=3 ///< Same as Type1 except remote process is not allowed to use device-mapping.
+} BufferType;
+
+typedef enum {
+ BufferDirection_Send=0,
+ BufferDirection_Recv=1,
+ BufferDirection_Exch=2,
+} BufferDirection;
+
+typedef enum {
+ IpcCommandType_Invalid = 0,
+ IpcCommandType_LegacyRequest = 1,
+ IpcCommandType_Close = 2,
+ IpcCommandType_LegacyControl = 3,
+ IpcCommandType_Request = 4,
+ IpcCommandType_Control = 5,
+ IpcCommandType_RequestWithContext = 6,
+ IpcCommandType_ControlWithContext = 7,
+} IpcCommandType;
+
+typedef enum {
+ DomainMessageType_Invalid = 0,
+ DomainMessageType_SendMessage = 1,
+ DomainMessageType_Close = 2,
+} DomainMessageType;
+
+/// IPC domain message header.
+typedef struct {
+ u8 Type;
+ u8 NumObjectIds;
+ u16 Length;
+ u32 ThisObjectId;
+ u32 Pad[2];
+} DomainMessageHeader;
+
+typedef struct {
+ size_t NumSend; // A
+ size_t NumRecv; // B
+ size_t NumExch; // W
+ const void* Buffers[IPC_MAX_BUFFERS];
+ size_t BufferSizes[IPC_MAX_BUFFERS];
+ BufferType BufferTypes[IPC_MAX_BUFFERS];
+
+ size_t NumStaticIn; // X
+ size_t NumStaticOut; // C
+ const void* Statics[IPC_MAX_BUFFERS];
+ size_t StaticSizes[IPC_MAX_BUFFERS];
+ u8 StaticIndices[IPC_MAX_BUFFERS];
+
+ bool SendPid;
+ size_t NumHandlesCopy;
+ size_t NumHandlesMove;
+ Handle Handles[IPC_MAX_OBJECTS];
+
+ size_t NumObjectIds;
+ u32 ObjectIds[IPC_MAX_OBJECTS];
+} IpcCommand;
+
+/**
+ * @brief Initializes an IPC command structure.
+ * @param cmd IPC command structure.
+ */
+static inline void ipcInitialize(IpcCommand* cmd) {
+ *cmd = (IpcCommand){0};
+}
+
+/// IPC buffer descriptor.
+typedef struct {
+ u32 Size; ///< Size of the buffer.
+ u32 Addr; ///< Lower 32-bits of the address of the buffer
+ u32 Packed; ///< Packed data (including higher bits of the address)
+} IpcBufferDescriptor;
+
+/// IPC static send-buffer descriptor.
+typedef struct {
+ u32 Packed; ///< Packed data (including higher bits of the address)
+ u32 Addr; ///< Lower 32-bits of the address
+} IpcStaticSendDescriptor;
+
+/// IPC static receive-buffer descriptor.
+typedef struct {
+ u32 Addr; ///< Lower 32-bits of the address of the buffer
+ u32 Packed; ///< Packed data (including higher bits of the address)
+} IpcStaticRecvDescriptor;
+
+/**
+ * @brief Adds a buffer to an IPC command structure.
+ * @param cmd IPC command structure.
+ * @param buffer Address of the buffer.
+ * @param size Size of the buffer.
+ * @param type Buffer type.
+ */
+static inline void ipcAddSendBuffer(IpcCommand* cmd, const void* buffer, size_t size, BufferType type) {
+ size_t off = cmd->NumSend;
+ cmd->Buffers[off] = buffer;
+ cmd->BufferSizes[off] = size;
+ cmd->BufferTypes[off] = type;
+ cmd->NumSend++;
+}
+
+/**
+ * @brief Adds a receive-buffer to an IPC command structure.
+ * @param cmd IPC command structure.
+ * @param buffer Address of the buffer.
+ * @param size Size of the buffer.
+ * @param type Buffer type.
+ */
+static inline void ipcAddRecvBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type) {
+ size_t off = cmd->NumSend + cmd->NumRecv;
+ cmd->Buffers[off] = buffer;
+ cmd->BufferSizes[off] = size;
+ cmd->BufferTypes[off] = type;
+ cmd->NumRecv++;
+}
+
+/**
+ * @brief Adds an exchange-buffer to an IPC command structure.
+ * @param cmd IPC command structure.
+ * @param buffer Address of the buffer.
+ * @param size Size of the buffer.
+ * @param type Buffer type.
+ */
+static inline void ipcAddExchBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type) {
+ size_t off = cmd->NumSend + cmd->NumRecv + cmd->NumExch;
+ cmd->Buffers[off] = buffer;
+ cmd->BufferSizes[off] = size;
+ cmd->BufferTypes[off] = type;
+ cmd->NumExch++;
+}
+
+/**
+ * @brief Adds a static-buffer to an IPC command structure.
+ * @param cmd IPC command structure.
+ * @param buffer Address of the buffer.
+ * @param size Size of the buffer.
+ * @param index Index of buffer.
+ */
+static inline void ipcAddSendStatic(IpcCommand* cmd, const void* buffer, size_t size, u8 index) {
+ size_t off = cmd->NumStaticIn;
+ cmd->Statics[off] = buffer;
+ cmd->StaticSizes[off] = size;
+ cmd->StaticIndices[off] = index;
+ cmd->NumStaticIn++;
+}
+
+/**
+ * @brief Adds a static-receive-buffer to an IPC command structure.
+ * @param cmd IPC command structure.
+ * @param buffer Address of the buffer.
+ * @param size Size of the buffer.
+ * @param index Index of buffer.
+ */
+static inline void ipcAddRecvStatic(IpcCommand* cmd, void* buffer, size_t size, u8 index) {
+ size_t off = cmd->NumStaticIn + cmd->NumStaticOut;
+ cmd->Statics[off] = buffer;
+ cmd->StaticSizes[off] = size;
+ cmd->StaticIndices[off] = index;
+ cmd->NumStaticOut++;
+}
+
+/**
+ * @brief Adds a smart-buffer (buffer + static-buffer pair) to an IPC command structure.
+ * @param cmd IPC command structure.
+ * @param ipc_buffer_size IPC buffer size.
+ * @param buffer Address of the buffer.
+ * @param size Size of the buffer.
+ * @param index Index of buffer.
+ */
+static inline void ipcAddSendSmart(IpcCommand* cmd, size_t ipc_buffer_size, const void* buffer, size_t size, u8 index) {
+ if (ipc_buffer_size != 0 && size <= ipc_buffer_size) {
+ ipcAddSendBuffer(cmd, NULL, 0, BufferType_Normal);
+ ipcAddSendStatic(cmd, buffer, size, index);
+ } else {
+ ipcAddSendBuffer(cmd, buffer, size, BufferType_Normal);
+ ipcAddSendStatic(cmd, NULL, 0, index);
+ }
+}
+
+/**
+ * @brief Adds a smart-receive-buffer (buffer + static-receive-buffer pair) to an IPC command structure.
+ * @param cmd IPC command structure.
+ * @param ipc_buffer_size IPC buffer size.
+ * @param buffer Address of the buffer.
+ * @param size Size of the buffer.
+ * @param index Index of buffer.
+ */
+static inline void ipcAddRecvSmart(IpcCommand* cmd, size_t ipc_buffer_size, void* buffer, size_t size, u8 index) {
+ if (ipc_buffer_size != 0 && size <= ipc_buffer_size) {
+ ipcAddRecvBuffer(cmd, NULL, 0, BufferType_Normal);
+ ipcAddRecvStatic(cmd, buffer, size, index);
+ } else {
+ ipcAddRecvBuffer(cmd, buffer, size, BufferType_Normal);
+ ipcAddRecvStatic(cmd, NULL, 0, index);
+ }
+}
+
+/**
+ * @brief Tags an IPC command structure to send the PID.
+ * @param cmd IPC command structure.
+ */
+static inline void ipcSendPid(IpcCommand* cmd) {
+ cmd->SendPid = true;
+}
+
+/**
+ * @brief Adds a copy-handle to be sent through an IPC command structure.
+ * @param cmd IPC command structure.
+ * @param h Handle to send.
+ * @remark The receiving process gets a copy of the handle.
+ */
+static inline void ipcSendHandleCopy(IpcCommand* cmd, Handle h) {
+ cmd->Handles[cmd->NumHandlesCopy++] = h;
+}
+
+/**
+ * @brief Adds a move-handle to be sent through an IPC command structure.
+ * @param cmd IPC command structure.
+ * @param h Handle to send.
+ * @remark The sending process loses ownership of the handle, which is transferred to the receiving process.
+ */
+static inline void ipcSendHandleMove(IpcCommand* cmd, Handle h) {
+ cmd->Handles[cmd->NumHandlesCopy + cmd->NumHandlesMove++] = h;
+}
+
+/**
+ * @brief Prepares the header of an IPC command structure.
+ * @param cmd IPC command structure.
+ * @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request
+ * @return Pointer to the raw embedded data structure in the request, ready to be filled out.
+ */
+static inline void* ipcPrepareHeader(IpcCommand* cmd, size_t sizeof_raw) {
+ u32* buf = (u32*)armGetTls();
+ size_t i;
+ *buf++ = IpcCommandType_Request | (cmd->NumStaticIn << 16) | (cmd->NumSend << 20) | (cmd->NumRecv << 24) | (cmd->NumExch << 28);
+
+ u32* fill_in_size_later = buf;
+
+ if (cmd->NumStaticOut > 0) {
+ *buf = (cmd->NumStaticOut + 2) << 10;
+ }
+ else {
+ *buf = 0;
+ }
+
+ if (cmd->SendPid || cmd->NumHandlesCopy > 0 || cmd->NumHandlesMove > 0) {
+ *buf++ |= 0x80000000;
+ *buf++ = (!!cmd->SendPid) | (cmd->NumHandlesCopy << 1) | (cmd->NumHandlesMove << 5);
+
+ if (cmd->SendPid)
+ buf += 2;
+
+ for (i=0; i<(cmd->NumHandlesCopy + cmd->NumHandlesMove); i++)
+ *buf++ = cmd->Handles[i];
+ }
+ else {
+ buf++;
+ }
+
+ for (i=0; i<cmd->NumStaticIn; i++, buf+=2) {
+ IpcStaticSendDescriptor* desc = (IpcStaticSendDescriptor*) buf;
+
+ uintptr_t ptr = (uintptr_t) cmd->Statics[i];
+ desc->Addr = ptr;
+ desc->Packed = cmd->StaticIndices[i] | (cmd->StaticSizes[i] << 16) |
+ (((ptr >> 32) & 15) << 12) | (((ptr >> 36) & 15) << 6);
+ }
+
+ for (i=0; i<(cmd->NumSend + cmd->NumRecv + cmd->NumExch); i++, buf+=3) {
+ IpcBufferDescriptor* desc = (IpcBufferDescriptor*) buf;
+ desc->Size = cmd->BufferSizes[i];
+
+ uintptr_t ptr = (uintptr_t) cmd->Buffers[i];
+ desc->Addr = ptr;
+ desc->Packed = cmd->BufferTypes[i] |
+ (((ptr >> 32) & 15) << 28) | ((ptr >> 36) << 2);
+ }
+
+ u32 padding = ((16 - (((uintptr_t) buf) & 15)) & 15) / 4;
+ u32* raw = (u32*) (buf + padding);
+
+ size_t raw_size = (sizeof_raw/4) + 4;
+ buf += raw_size;
+
+ u16* buf_u16 = (u16*) buf;
+
+ for (i=0; i<cmd->NumStaticOut; i++) {
+ size_t off = cmd->NumStaticIn + i;
+ size_t sz = (uintptr_t) cmd->StaticSizes[off];
+
+ buf_u16[i] = (sz > 0xFFFF) ? 0 : sz;
+ }
+
+ size_t u16s_size = ((2*cmd->NumStaticOut) + 3)/4;
+ buf += u16s_size;
+ raw_size += u16s_size;
+
+ *fill_in_size_later |= raw_size;
+
+ for (i=0; i<cmd->NumStaticOut; i++, buf+=2) {
+ IpcStaticRecvDescriptor* desc = (IpcStaticRecvDescriptor*) buf;
+ size_t off = cmd->NumStaticIn + i;
+
+ uintptr_t ptr = (uintptr_t) cmd->Statics[off];
+ desc->Addr = ptr;
+ desc->Packed = (ptr >> 32) | (cmd->StaticSizes[off] << 16);
+ }
+
+ return (void*) raw;
+}
+
+/**
+ * @brief Dispatches an IPC request.
+ * @param session IPC session handle.
+ * @return Result code.
+ */
+static inline Result ipcDispatch(Handle session) {
+ return svcSendSyncRequest(session);
+}
+
+///@}
+
+///@name IPC response parsing
+///@{
+
+/// IPC parsed command (response) structure.
+typedef struct {
+ IpcCommandType CommandType; ///< Type of the command
+
+ bool HasPid; ///< true if the 'Pid' field is filled out.
+ u64 Pid; ///< PID included in the response (only if HasPid is true)
+
+ size_t NumHandles; ///< Number of handles copied.
+ Handle Handles[IPC_MAX_OBJECTS]; ///< Handles.
+ bool WasHandleCopied[IPC_MAX_OBJECTS]; ///< true if the handle was moved, false if it was copied.
+
+ bool IsDomainMessage; ///< true if the the message is a Domain message.
+ DomainMessageType MessageType; ///< Type of the domain message.
+ u32 MessageLength; ///< Size of rawdata (for domain messages).
+ u32 ThisObjectId; ///< Object ID to call the command on (for domain messages).
+ size_t NumObjectIds; ///< Number of object IDs (for domain messages).
+ u32 ObjectIds[IPC_MAX_OBJECTS]; ///< Object IDs (for domain messages).
+
+ size_t NumBuffers; ///< Number of buffers in the response.
+ void* Buffers[IPC_MAX_BUFFERS]; ///< Pointers to the buffers.
+ size_t BufferSizes[IPC_MAX_BUFFERS]; ///< Sizes of the buffers.
+ BufferType BufferTypes[IPC_MAX_BUFFERS]; ///< Types of the buffers.
+ BufferDirection BufferDirections[IPC_MAX_BUFFERS]; ///< Direction of each buffer.
+
+ size_t NumStatics; ///< Number of statics in the response.
+ void* Statics[IPC_MAX_BUFFERS]; ///< Pointers to the statics.
+ size_t StaticSizes[IPC_MAX_BUFFERS]; ///< Sizes of the statics.
+ u8 StaticIndices[IPC_MAX_BUFFERS]; ///< Indices of the statics.
+
+ size_t NumStaticsOut; ///< Number of output statics available in the response.
+
+ void* Raw; ///< Pointer to the raw embedded data structure in the response.
+ void* RawWithoutPadding; ///< Pointer to the raw embedded data structure, without padding.
+ size_t RawSize; ///< Size of the raw embedded data.
+} IpcParsedCommand;
+
+/**
+ * @brief Parse an IPC command response into an IPC parsed command structure.
+ * @param IPC parsed command structure to fill in.
+ * @return Result code.
+ */
+static inline Result ipcParse(IpcParsedCommand* r) {
+ u32* buf = (u32*)armGetTls();
+ u32 ctrl0 = *buf++;
+ u32 ctrl1 = *buf++;
+ size_t i;
+
+ r->IsDomainMessage = false;
+
+ r->CommandType = (IpcCommandType) (ctrl0 & 0xffff);
+ r->HasPid = false;
+ r->RawSize = (ctrl1 & 0x1ff) * 4;
+ r->NumHandles = 0;
+
+ r->NumStaticsOut = (ctrl1 >> 10) & 15;
+ if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 2 -> Single descriptor
+ if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 3+ -> (Value - 2) descriptors
+
+ if (ctrl1 & 0x80000000) {
+ u32 ctrl2 = *buf++;
+
+ if (ctrl2 & 1) {
+ r->HasPid = true;
+ r->Pid = *buf++;
+ r->Pid |= ((u64)(*buf++)) << 32;
+ }
+
+ size_t num_handles_copy = ((ctrl2 >> 1) & 15);
+ size_t num_handles_move = ((ctrl2 >> 5) & 15);
+
+ size_t num_handles = num_handles_copy + num_handles_move;
+ u32* buf_after_handles = buf + num_handles;
+
+ if (num_handles > IPC_MAX_OBJECTS)
+ num_handles = IPC_MAX_OBJECTS;
+
+ for (i=0; i<num_handles; i++)
+ {
+ r->Handles[i] = *(buf+i);
+ r->WasHandleCopied[i] = (i < num_handles_copy);
+ }
+
+ r->NumHandles = num_handles;
+ buf = buf_after_handles;
+ }
+
+ size_t num_statics = (ctrl0 >> 16) & 15;
+ u32* buf_after_statics = buf + num_statics*2;
+
+ if (num_statics > IPC_MAX_BUFFERS)
+ num_statics = IPC_MAX_BUFFERS;
+
+ for (i=0; i<num_statics; i++, buf+=2) {
+ IpcStaticSendDescriptor* desc = (IpcStaticSendDescriptor*) buf;
+ u64 packed = (u64) desc->Packed;
+
+ r->Statics[i] = (void*) (desc->Addr | (((packed >> 12) & 15) << 32) | (((packed >> 6) & 15) << 36));
+ r->StaticSizes[i] = packed >> 16;
+ r->StaticIndices[i] = packed & 63;
+ }
+
+ r->NumStatics = num_statics;
+ buf = buf_after_statics;
+
+ size_t num_bufs_send = (ctrl0 >> 20) & 15;
+ size_t num_bufs_recv = (ctrl0 >> 24) & 15;
+ size_t num_bufs_exch = (ctrl0 >> 28) & 15;
+
+ size_t num_bufs = num_bufs_send + num_bufs_recv + num_bufs_exch;
+ r->Raw = (void*)(((uintptr_t)(buf + num_bufs*3) + 15) &~ 15);
+ r->RawWithoutPadding = (void*)((uintptr_t)(buf + num_bufs*3));
+
+ if (num_bufs > IPC_MAX_BUFFERS)
+ num_bufs = IPC_MAX_BUFFERS;
+
+ for (i=0; i<num_bufs; i++, buf+=3) {
+ IpcBufferDescriptor* desc = (IpcBufferDescriptor*) buf;
+ u64 packed = (u64) desc->Packed;
+
+ r->Buffers[i] = (void*) (desc->Addr | ((packed >> 28) << 32) | (((packed >> 2) & 15) << 36));
+ r->BufferSizes[i] = desc->Size;
+ r->BufferTypes[i] = (BufferType) (packed & 3);
+
+ if (i < num_bufs_send)
+ r->BufferDirections[i] = BufferDirection_Send;
+ else if (i < (num_bufs_send + num_bufs_recv))
+ r->BufferDirections[i] = BufferDirection_Recv;
+ else
+ r->BufferDirections[i] = BufferDirection_Exch;
+ }
+
+ r->NumBuffers = num_bufs;
+ return 0;
+}
+
+/**
+ * @brief Queries the size of an IPC pointer buffer.
+ * @param session IPC session handle.
+ * @param size Output variable in which to store the size.
+ * @return Result code.
+ */
+static inline Result ipcQueryPointerBufferSize(Handle session, size_t *size) {
+ u32* buf = (u32*)armGetTls();
+
+ buf[0] = IpcCommandType_Control;
+ buf[1] = 8;
+ buf[2] = 0;
+ buf[3] = 0;
+ buf[4] = SFCI_MAGIC;
+ buf[5] = 0;
+ buf[6] = 3;
+ buf[7] = 0;
+
+ Result rc = ipcDispatch(session);
+
+ if (R_SUCCEEDED(rc)) {
+ IpcParsedCommand r;
+ ipcParse(&r);
+
+ struct ipcQueryPointerBufferSizeResponse {
+ u64 magic;
+ u64 result;
+ u32 size;
+ } *raw = (struct ipcQueryPointerBufferSizeResponse*)r.Raw;
+
+ rc = raw->result;
+
+ if (R_SUCCEEDED(rc)) {
+ *size = raw->size & 0xffff;
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * @brief Closes the IPC session with proper clean up.
+ * @param session IPC session handle.
+ * @return Result code.
+ */
+static inline Result ipcCloseSession(Handle session) {
+ u32* buf = (u32*)armGetTls();
+ buf[0] = IpcCommandType_Close;
+ buf[1] = 0;
+ return ipcDispatch(session);
+}
+///@}
+
+///@name IPC domain handling
+///@{
+
+/**
+ * @brief Converts an IPC session handle into a domain.
+ * @param session IPC session handle.
+ * @param object_id_out Output variable in which to store the object ID.
+ * @return Result code.
+ */
+static inline Result ipcConvertSessionToDomain(Handle session, u32* object_id_out) {
+ u32* buf = (u32*)armGetTls();
+
+ buf[0] = IpcCommandType_Control;
+ buf[1] = 8;
+ buf[4] = SFCI_MAGIC;
+ buf[5] = 0;
+ buf[6] = 0;
+ buf[7] = 0;
+
+ Result rc = ipcDispatch(session);
+
+ if (R_SUCCEEDED(rc)) {
+ IpcParsedCommand r;
+ ipcParse(&r);
+
+ struct ipcConvertSessionToDomainResponse {
+ u64 magic;
+ u64 result;
+ u32 object_id;
+ } *raw = (struct ipcConvertSessionToDomainResponse*)r.Raw;
+
+ rc = raw->result;
+
+ if (R_SUCCEEDED(rc)) {
+ *object_id_out = raw->object_id;
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * @brief Adds an object ID to be sent through an IPC domain command structure.
+ * @param cmd IPC domain command structure.
+ * @param object_id Object ID to send.
+ */
+static inline void ipcSendObjectId(IpcCommand* cmd, u32 object_id) {
+ cmd->ObjectIds[cmd->NumObjectIds++] = object_id;
+}
+
+/**
+ * @brief Prepares the header of an IPC command structure (domain version).
+ * @param cmd IPC command structure.
+ * @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request
+ * @oaram object_id Domain object ID.
+ * @return Pointer to the raw embedded data structure in the request, ready to be filled out.
+ */
+static inline void* ipcPrepareHeaderForDomain(IpcCommand* cmd, size_t sizeof_raw, u32 object_id) {
+ void* raw = ipcPrepareHeader(cmd, sizeof_raw + sizeof(DomainMessageHeader));
+ DomainMessageHeader* hdr = (DomainMessageHeader*) raw;
+ u32 *object_ids = (u32*)(((uintptr_t) raw) + sizeof(DomainMessageHeader) + sizeof_raw);
+
+ hdr->Type = DomainMessageType_SendMessage;
+ hdr->NumObjectIds = (u8)cmd->NumObjectIds;
+ hdr->Length = sizeof_raw;
+ hdr->ThisObjectId = object_id;
+ hdr->Pad[0] = hdr->Pad[1] = 0;
+
+ for(size_t i = 0; i < cmd->NumObjectIds; i++)
+ object_ids[i] = cmd->ObjectIds[i];
+ return (void*)(((uintptr_t) raw) + sizeof(DomainMessageHeader));
+}
+
+/**
+ * @brief Parse an IPC command response into an IPC parsed command structure (domain version).
+ * @param IPC parsed command structure to fill in.
+ * @return Result code.
+ */
+static inline Result ipcParseForDomain(IpcParsedCommand* r) {
+ Result rc = ipcParse(r);
+ DomainMessageHeader *hdr;
+ u32 *object_ids;
+ if(R_FAILED(rc))
+ return rc;
+
+ hdr = (DomainMessageHeader*) r->Raw;
+ object_ids = (u32*)(((uintptr_t) hdr) + sizeof(DomainMessageHeader) + hdr->Length);
+ r->Raw = (void*)(((uintptr_t) r->Raw) + sizeof(DomainMessageHeader));
+
+ r->IsDomainMessage = true;
+ r->MessageType = (DomainMessageType)(hdr->Type);
+ switch (r->MessageType) {
+ case DomainMessageType_SendMessage:
+ case DomainMessageType_Close:
+ break;
+ default:
+ return MAKERESULT(Module_Libnx, LibnxError_DomainMessageUnknownType);
+ }
+ r->ThisObjectId = hdr->ThisObjectId;
+ r->NumObjectIds = hdr->NumObjectIds > 8 ? 8 : hdr->NumObjectIds;
+ if ((uintptr_t)object_ids + sizeof(u32) * r->NumObjectIds - (uintptr_t)armGetTls() >= 0x100) {
+ return MAKERESULT(Module_Libnx, LibnxError_DomainMessageTooManyObjectIds);
+ }
+ for(size_t i = 0; i < r->NumObjectIds; i++)
+ r->ObjectIds[i] = object_ids[i];
+
+ return rc;
+}
+
+/**
+ * @brief Closes a domain object by ID.
+ * @param session IPC session handle.
+ * @param object_id ID of the object to close.
+ * @return Result code.
+ */
+static inline Result ipcCloseObjectById(Handle session, u32 object_id) {
+ IpcCommand c;
+ DomainMessageHeader* hdr;
+
+ ipcInitialize(&c);
+ hdr = (DomainMessageHeader*)ipcPrepareHeader(&c, sizeof(DomainMessageHeader));
+
+ hdr->Type = 2;
+ hdr->NumObjectIds = 0;
+ hdr->Length = 0;
+ hdr->ThisObjectId = object_id;
+ hdr->Pad[0] = hdr->Pad[1] = 0;
+
+ return ipcDispatch(session); // this command has no associated response
+}
+
+///@}
+
diff --git a/tests/unittests/data/teststaticnewlinebraces.h b/tests/unittests/data/teststaticnewlinebraces.h
new file mode 100644
index 0000000..c72c11a
--- /dev/null
+++ b/tests/unittests/data/teststaticnewlinebraces.h
@@ -0,0 +1,71 @@
+/**
+ * @file condvar.h
+ * @brief Condition variable synchronization primitive.
+ * @author plutoo
+ * @copyright libnx Authors
+ */
+#pragma once
+#include "../types.h"
+#include "../kernel/mutex.h"
+
+/// Condition variable structure.
+typedef struct {
+ u32 tag;
+ Mutex* mutex;
+} CondVar;
+
+/**
+ * @brief Initializes a condition variable.
+ * @param[in] c Condition variable object.
+ * @param[in] m Mutex object to use inside the condition variable.
+ */
+void condvarInit(CondVar* c, Mutex* m);
+
+/**
+ * @brief Waits on a condition variable with a timeout.
+ * @param[in] c Condition variable object.
+ * @param[in] timeout Timeout in nanoseconds.
+ * @return Result code (0xEA01 on timeout).
+ * @remark On function return, the underlying mutex is acquired.
+ */
+Result condvarWaitTimeout(CondVar* c, u64 timeout);
+
+/**
+ * @brief Waits on a condition variable.
+ * @param[in] c Condition variable object.
+ * @return Result code.
+ * @remark On function return, the underlying mutex is acquired.
+ */
+static inline Result condvarWait(CondVar* c)
+{
+ return condvarWaitTimeout(c, -1ull);
+}
+
+/**
+ * @brief Wakes up up to the specified number of threads waiting on a condition variable.
+ * @param[in] c Condition variable object.
+ * @param[in] num Maximum number of threads to wake up (or -1 to wake them all up).
+ * @return Result code.
+ */
+Result condvarWake(CondVar* c, int num);
+
+/**
+ * @brief Wakes up a single thread waiting on a condition variable.
+ * @param[in] c Condition variable object.
+ * @return Result code.
+ */
+static inline Result condvarWakeOne(CondVar* c)
+{
+ return condvarWake(c, 1);
+}
+
+/**
+ * @brief Wakes up all thread waiting on a condition variable.
+ * @param[in] c Condition variable object.
+ * @return Result code.
+ */
+static inline Result condvarWakeAll(CondVar* c)
+{
+ return condvarWake(c, -1);
+}
+
diff --git a/tests/unittests/data/teststaticnewlinebracesreadded.h b/tests/unittests/data/teststaticnewlinebracesreadded.h
new file mode 100644
index 0000000..176f4fc
--- /dev/null
+++ b/tests/unittests/data/teststaticnewlinebracesreadded.h
@@ -0,0 +1,68 @@
+/**
+ * @file condvar.h
+ * @brief Condition variable synchronization primitive.
+ * @author plutoo
+ * @copyright libnx Authors
+ */
+#pragma once
+#include "../types.h"
+#include "../kernel/mutex.h"
+
+/// Condition variable structure.
+typedef struct {
+ u32 tag;
+ Mutex* mutex;
+} CondVar;
+
+/**
+ * @brief Initializes a condition variable.
+ * @param[in] c Condition variable object.
+ * @param[in] m Mutex object to use inside the condition variable.
+ */
+void condvarInit(CondVar* c, Mutex* m);
+
+/**
+ * @brief Waits on a condition variable with a timeout.
+ * @param[in] c Condition variable object.
+ * @param[in] timeout Timeout in nanoseconds.
+ * @return Result code (0xEA01 on timeout).
+ * @remark On function return, the underlying mutex is acquired.
+ */
+Result condvarWaitTimeout(CondVar* c, u64 timeout);
+
+/**
+ * @brief Waits on a condition variable.
+ * @param[in] c Condition variable object.
+ * @return Result code.
+ * @remark On function return, the underlying mutex is acquired.
+ */
+static inline Result condvarWait(CondVar* c) {
+ return condvarWaitTimeout(c, -1ull);
+}
+
+/**
+ * @brief Wakes up up to the specified number of threads waiting on a condition variable.
+ * @param[in] c Condition variable object.
+ * @param[in] num Maximum number of threads to wake up (or -1 to wake them all up).
+ * @return Result code.
+ */
+Result condvarWake(CondVar* c, int num);
+
+/**
+ * @brief Wakes up a single thread waiting on a condition variable.
+ * @param[in] c Condition variable object.
+ * @return Result code.
+ */
+static inline Result condvarWakeOne(CondVar* c) {
+ return condvarWake(c, 1);
+}
+
+/**
+ * @brief Wakes up all thread waiting on a condition variable.
+ * @param[in] c Condition variable object.
+ * @return Result code.
+ */
+static inline Result condvarWakeAll(CondVar* c) {
+ return condvarWake(c, -1);
+}
+
diff --git a/tests/unittests/nim.cfg b/tests/unittests/nim.cfg
new file mode 100644
index 0000000..e9b9a2e
--- /dev/null
+++ b/tests/unittests/nim.cfg
@@ -0,0 +1 @@
+--path="../../src"
diff --git a/tests/unittests/testfileops.nim b/tests/unittests/testfileops.nim
new file mode 100644
index 0000000..2fff387
--- /dev/null
+++ b/tests/unittests/testfileops.nim
@@ -0,0 +1,211 @@
+import nimgen/fileops, common, regex, os
+
+import unittest
+
+let testFileContent = """
+this is text
+this is text
+replace me
+prepend me
+end
+"""
+
+let prependMiddleExpected = """
+this is text
+this is text
+replace me
+prepended data
+prepend me
+end
+"""
+
+let prependEndExpected = """
+this is text
+this is text
+replace me
+prepend me
+data
+end
+"""
+
+let appendEndExpected = """
+this is text
+this is text
+replace me
+prepend me
+end
+data
+"""
+
+let appendMiddleExpected = """
+this is data
+ text
+this is text
+replace me
+prepend me
+end
+"""
+
+let freplaceDefaultExpected = """
+
+
+replace me
+prepend me
+end
+"""
+
+let freplaceWithExpected = """
+this is text
+this is text
+foobar
+prepend me
+end
+"""
+
+let freplaceRegexExpected = """
+foobar
+foobar
+replace me
+prepend me
+end
+"""
+
+let commentExpected = """
+this is text
+this is text
+//replace me
+//prepend me
+//end
+"""
+
+let commentMiddleExpected = """
+this //is text
+//this is text
+replace me
+prepend me
+end
+"""
+
+
+let dataDir = currentSourcePath().splitPath().head / "data"
+
+let testfilename = dataDir / "testing.txt"
+
+
+suite "test file ops":
+ if not dataDir.dirExists():
+ dataDir.createDir()
+
+ setup:
+ if testfilename.existsFile():
+ removeFile(testfilename)
+ writeFile(testfilename, testFileContent)
+
+ ################### Prepend #######################
+
+ test "prepend at beginning of file":
+ prepend(testfilename, "data\n")
+ let expected = "data\n" & testFileContent
+ testfilename.checkFile(expected)
+
+ test "prepend at middle of file":
+ prepend(testfilename, "prepended data\n", "prepend me")
+ testfilename.checkFile(prependMiddleExpected)
+
+ test "prepend at end of file":
+ prepend(testfilename, "data\n", "end\n")
+ testfilename.checkFile(prependEndExpected)
+
+ ################### Pipe #########################
+
+ test "pipe command into file":
+ when defined(windows):
+ pipe(testfilename, "(ECHO foo)>>$file")
+ testfilename.checkFile("foo")
+ else:
+ pipe(testfilename, "cat $file | grep 'this is text'")
+ testfilename.checkFile("this is text\nthis is text")
+
+ ################# Append #########################
+
+ test "append file end":
+ append(testfilename, "data\n")
+ testfilename.checkFile(appendEndExpected)
+
+ test "append file middle":
+ append(testfilename, " data\n", "this is")
+ testfilename.checkFile(appendMiddleExpected)
+
+ ################# FReplace #########################
+
+ test "freplace default empty":
+ freplace(testfilename, "this is text")
+ testfilename.checkFile(freplaceDefaultExpected)
+
+ test "freplace with content":
+ freplace(testfilename, "replace me", "foobar")
+ testfilename.checkFile(freplaceWithExpected)
+
+ test "freplace regex":
+ freplace(testfilename, re"this .*", "foobar")
+ testfilename.checkFile(freplaceRegexExpected)
+
+ ####################### Comment ######################
+
+ test "comment":
+ comment(testfilename, "replace me", "3")
+ testfilename.checkFile(commentExpected)
+
+ test "comment over length":
+ comment(testfilename, "replace me", "10")
+ testfilename.checkFile(commentExpected)
+
+ test "comment negative":
+ comment(testfilename, "replace me", "-3")
+ testfilename.checkFile(testFileContent)
+
+ test "comment zero":
+ comment(testfilename, "replace me", "0")
+ testfilename.checkFile(testFileContent)
+
+ test "comment middle":
+ comment(testfilename, "is text", "2")
+ testfilename.checkFile(commentMiddleExpected)
+
+ ############### Static inline removal ################
+
+ test "replace static inline with front braces at end of line":
+
+ let
+ file = dataDir / "teststaticfrontbraces.h"
+ resFile = dataDir / "teststaticexpectedfrontbraces.h"
+
+ test = readFile(file)
+ expected = readFile(resFile)
+
+ writeFile(testfilename, test)
+
+ removeStatic(testfilename)
+ testfilename.checkFile(expected)
+
+ reAddStatic(testfilename)
+ testfilename.checkFile(test)
+
+ test "replace static inline with newline before brace":
+
+ let
+ file = dataDir / "teststaticnewlinebraces.h"
+ resFile = dataDir / "teststaticexpectednewlinebraces.h"
+ reAddedFile = dataDir / "teststaticnewlinebracesreadded.h"
+
+ test = readFile(file)
+ expected = readFile(resFile)
+ reAdded = readFile(reAddedFile)
+
+ writeFile(testfilename, test)
+
+ removeStatic(testfilename)
+ testfilename.checkFile(expected)
+
+ reAddStatic(testfilename)
+ testfilename.checkFile(reAdded)