#include <3ds/services/csnd.h>
#include <3ds/services/dsp.h>
#include <3ds/services/fs.h>
+#include <3ds/services/fsreg.h>
#include <3ds/services/gspgpu.h>
#include <3ds/services/gsplcd.h>
#include <3ds/services/hid.h>
--- /dev/null
+/**
+ * @file exheader.h
+ * @brief Structures describing extended headers as in use by the system
+ */
+#pragma once
+
+#include <3ds/types.h>
+
+typedef struct
+{
+ u32 text_addr;
+ u32 text_size;
+ u32 ro_addr;
+ u32 ro_size;
+ u32 data_addr;
+ u32 data_size;
+ u32 total_size;
+} PACKED EXHEADER_prog_addrs;
+
+typedef struct
+{
+ u8 reserved[5];
+ u8 flag; // Maybe a feature - Bits 2-7 are unused. We could allow uh, custom flags here. Like zlib compression on code rather than lzss.
+ u8 remasterversion[2];
+} PACKED EXHEADER_systeminfoflags;
+
+typedef struct
+{
+ u32 address;
+ u32 nummaxpages;
+ u32 codesize;
+} PACKED EXHEADER_codesegmentinfo;
+
+typedef struct
+{
+ u8 name[8];
+ EXHEADER_systeminfoflags flags;
+ EXHEADER_codesegmentinfo text;
+ u8 stacksize[4];
+ EXHEADER_codesegmentinfo ro;
+ u8 reserved[4];
+ EXHEADER_codesegmentinfo data;
+ u32 bsssize;
+} PACKED EXHEADER_codesetinfo;
+
+typedef struct
+{
+ u64 programid[0x30];
+} PACKED EXHEADER_dependencylist;
+
+typedef struct
+{
+ u8 savedatasize[4];
+ u8 reserved[4];
+ u8 jumpid[8];
+ u8 reserved2[0x30];
+} PACKED EXHEADER_systeminfo;
+
+typedef struct
+{
+ u8 extsavedataid[8];
+ u8 systemsavedataid[8];
+ u8 reserved[8];
+ u8 accessinfo[7];
+ u8 otherattributes;
+} PACKED EXHEADER_storageinfo;
+
+// New3DS speed is flags[1]:1
+
+typedef struct
+{
+ u64 programid;
+ u8 coreVersion[4]; // Kernel version required for this.
+ u8 flag2;
+ u8 flag1;
+ u8 flag0; // CPU speed settings.
+ u8 priority;
+ u16 resourcelimitdescriptor[0x10];
+ EXHEADER_storageinfo storageinfo;
+ u64 serviceaccesscontrol[0x20];
+ u8 reserved[0x1f];
+ u8 resourcelimitcategory;
+} PACKED EXHEADER_arm11systemlocalcaps;
+
+typedef struct
+{
+ u32 descriptors[28];
+ u8 reserved[0x10];
+} PACKED EXHEADER_arm11kernelcapabilities;
+
+typedef struct
+{
+ u8 descriptors[15];
+ u8 descversion;
+} PACKED EXHEADER_arm9accesscontrol;
+
+typedef struct
+{
+ u8 signature[0x100];
+ u8 ncchpubkeymodulus[0x100];
+ EXHEADER_arm11systemlocalcaps arm11systemlocalcaps;
+ EXHEADER_arm11kernelcapabilities arm11kernelcaps;
+ EXHEADER_arm9accesscontrol arm9accesscontrol;
+} PACKED EXHEADER_accessdesc;
+
+typedef struct
+{
+ EXHEADER_codesetinfo codesetinfo;
+ EXHEADER_dependencylist deplist;
+ EXHEADER_systeminfo systeminfo;
+ EXHEADER_arm11systemlocalcaps arm11systemlocalcaps;
+ EXHEADER_arm11kernelcapabilities arm11kernelcaps;
+ EXHEADER_arm9accesscontrol arm9accesscontrol;
+ EXHEADER_accessdesc accessdesc;
+} PACKED EXHEADER_header;
--- /dev/null
+/**
+ * @file fsreg.h
+ * @brief Program Registry Service
+ */
+#pragma once
+
+#include <3ds/types.h>
+#include <3ds/services/fs.h>
+#include <3ds/exheader.h>
+
+/// Initialize the fs:REG service. Bear in mind a limited number of handles exist for this.
+Result fsregInit(void);
+
+/// Close and deinitialize fs:REG
+void fsregExit(void);
+
+/**
+ * @brief Checks if a program handle is valid?
+ * @param prog_handle Program handle to check validity of
+ */
+Result FSREG_CheckHostLoadId(u64 prog_handle);
+
+/**
+ * @brief Registers a loading program.
+ * @param prog_handle Program handle
+ * @param title Program information
+ */
+Result FSREG_LoadProgram(u64 *prog_handle, FS_ProgramInfo *title);
+
+/**
+ * @brief Retrieves the SystemControlInfo and AccessControlInfo for a program.
+ * @param exheader Exheader struct to place output in
+ * @param entry_count How many entries to get info for (?)
+ * @param prog_handle Program handle to get info for
+ */
+Result FSREG_GetProgramInfo(EXHEADER_header *exheader, u32 entry_count, u64 prog_handle);
+
+/**
+ * @brief Unregisters a currently registered program.
+ * @param prog_handle Handle of program to unregister.
+ */
+Result FSREG_UnloadProgram(u64 prog_handle);
+
+/**
+ * @brief Unregister a program previously registered.
+ * @param pid Process ID to unregister.
+ */
+Result FSREG_Unregister(u32 pid);
+
+/**
+ * @brief Register a program.
+ * @param pid Process ID to register.
+ * @param prog_handle Handle of program to register
+ * @param info Program info
+ * @param storageinfo Storage access flags
+ */
+Result FSREG_Register(u32 pid, u64 prog_handle, FS_ProgramInfo *info, void *storageinfo);
--- /dev/null
+#include <3ds.h>
+#include <string.h>
+// #include "srvsys.h"
+
+static Handle fsregHandle;
+static int fsregRefCount;
+
+Result fsregInit(void)
+{
+ Result ret = 0;
+
+ if (AtomicPostIncrement(&fsregRefCount))
+ return 0;
+
+ ret = srvGetServiceHandle(&fsregHandle, "fs:REG");
+
+ if (R_FAILED(ret))
+ AtomicDecrement(&fsregRefCount);
+
+ return ret;
+}
+
+void fsregExit(void)
+{
+ if (AtomicDecrement(&fsregRefCount))
+ return;
+
+ svcCloseHandle(fsregHandle);
+}
+
+Result FSREG_CheckHostLoadId(u64 prog_handle)
+{
+ u32 *cmdbuf = getThreadCommandBuffer();
+
+ cmdbuf[0] = IPC_MakeHeader(0x406, 2, 0); // 0x4060080
+ cmdbuf[1] = (u32)(prog_handle);
+ cmdbuf[2] = (u32)(prog_handle >> 32);
+
+ Result ret = 0;
+ if (R_FAILED(ret = svcSendSyncRequest(fsregHandle)))
+ return ret;
+
+ return cmdbuf[1];
+}
+
+Result FSREG_LoadProgram(u64 *prog_handle, FS_ProgramInfo *title)
+{
+ u32 *cmdbuf = getThreadCommandBuffer();
+
+ cmdbuf[0] = IPC_MakeHeader(0x404, 4, 0); // 0x4040100
+ memcpy(&cmdbuf[1], &title->programId, sizeof(u64));
+ *(u8 *)&cmdbuf[3] = title->mediaType;
+ memcpy(((u8 *)&cmdbuf[3]) + 1, &title->padding, 7);
+
+ Result ret = 0;
+ if (R_FAILED(ret = svcSendSyncRequest(fsregHandle)))
+ return ret;
+ *prog_handle = *(u64 *)&cmdbuf[2];
+
+ return cmdbuf[1];
+}
+
+Result FSREG_GetProgramInfo(EXHEADER_header *exheader, u32 entry_count, u64 prog_handle)
+{
+ u32 *cmdbuf = getThreadCommandBuffer();
+
+ cmdbuf[0] = IPC_MakeHeader(0x403, 3, 0); // 0x40300C0
+ cmdbuf[1] = entry_count;
+ *(u64 *)&cmdbuf[2] = prog_handle;
+ cmdbuf[64] = ((entry_count << 10) << 14) | 2;
+ cmdbuf[65] = (u32)exheader;
+
+ Result ret = 0;
+ if (R_FAILED(ret = svcSendSyncRequest(fsregHandle)))
+ return ret;
+
+ return cmdbuf[1];
+}
+
+Result FSREG_UnloadProgram(u64 prog_handle)
+{
+ u32 *cmdbuf = getThreadCommandBuffer();
+
+ cmdbuf[0] = IPC_MakeHeader(0x405, 2, 0); // 0x4050080
+ cmdbuf[1] = (u32)(prog_handle);
+ cmdbuf[2] = (u32)(prog_handle >> 32);
+
+ Result ret = 0;
+ if (R_FAILED(ret = svcSendSyncRequest(fsregHandle)))
+ return ret;
+
+ return cmdbuf[1];
+}
+
+Result FSREG_Unregister(u32 pid)
+{
+ u32 *cmdbuf = getThreadCommandBuffer();
+
+ cmdbuf[0] = IPC_MakeHeader(0x402, 1, 0); // 0x4020040
+ cmdbuf[1] = pid;
+
+ Result ret = 0;
+ if (R_FAILED(ret = svcSendSyncRequest(fsregHandle)))
+ return ret;
+
+ return cmdbuf[1];
+}
+
+Result FSREG_Register(u32 pid, u64 prog_handle, FS_ProgramInfo *info, void *storageinfo)
+{
+ u32 *cmdbuf = getThreadCommandBuffer();
+
+ cmdbuf[0] = IPC_MakeHeader(0x401, 0xf, 0); // 0x40103C0
+ cmdbuf[1] = pid;
+ *(u64 *)&cmdbuf[2] = prog_handle;
+ memcpy(&cmdbuf[4], &info->programId, sizeof(u64));
+ *(u8 *)&cmdbuf[6] = info->mediaType;
+ memcpy(((u8 *)&cmdbuf[6]) + 1, &info->padding, 7);
+ memcpy((u8 *)&cmdbuf[8], storageinfo, 32);
+
+ Result ret = 0;
+ if (R_FAILED(ret = svcSendSyncRequest(fsregHandle)))
+ return ret;
+
+ return cmdbuf[1];
+}