]> Chaos Git - corbenik/ctrulib.git/commitdiff
Add more filesystem service commands.
authormtheall <pigman46@gmail.com>
Fri, 22 Aug 2014 02:47:25 +0000 (21:47 -0500)
committermtheall <pigman46@gmail.com>
Fri, 22 Aug 2014 02:47:25 +0000 (21:47 -0500)
libctru/include/3ds/FS.h
libctru/source/services/fs.c

index 8fcef07a5c6032c6af03cb0a941dc8ee42e0ee37..b3e2a6ae0f367c2c27c6feafdf265389ce5b0cd3 100644 (file)
+#pragma once
 #ifndef FS_H
 #define FS_H
 
-#define FS_OPEN_READ (1<<0)
-#define FS_OPEN_WRITE (1<<1)
+/*! @file FS.h
+ *
+ *  Filesystem Services
+ */
+
+#include <string.h>
+#include <3ds/types.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/*! @defgroup fs_open_flags FS Open Flags
+ *
+ *  @sa FSUSER_OpenFile
+ *  @sa FSUSER_OpenFileDirectly
+ *
+ *  @{
+ */
+
+/*! Open file for read. */
+#define FS_OPEN_READ   (1<<0)
+/*! Open file for write. */
+#define FS_OPEN_WRITE  (1<<1)
+/*! Create file if it doesn't exist. */
 #define FS_OPEN_CREATE (1<<2)
+/* @} */
 
-#define FS_ATTRIBUTE_NONE (0x00000000)
-#define FS_ATTRIBUTE_READONLY (0x00000001)
-#define FS_ATTRIBUTE_ARCHIVE (0x00000100)
-#define FS_ATTRIBUTE_HIDDEN (0x00010000)
+/*! @defgroup fs_create_attributes FS Create Attributes
+ *
+ *  @sa FSUSER_OpenFile
+ *  @sa FSUSER_OpenFileDirectly
+ *
+ *  @{
+ */
+
+/*! No attributes. */
+#define FS_ATTRIBUTE_NONE      (0x00000000)
+/*! Create with read-only attribute. */
+#define FS_ATTRIBUTE_READONLY  (0x00000001)
+/*! Create with archive attribute. */
+#define FS_ATTRIBUTE_ARCHIVE   (0x00000100)
+/*! Create with hidden attribute. */
+#define FS_ATTRIBUTE_HIDDEN    (0x00010000)
+/*! Create with directory attribute. */
 #define FS_ATTRIBUTE_DIRECTORY (0x01000000)
+/*! @} */
+
+/*! @defgroup fs_write_flush_flags FS Flush Flags
+ *
+ *  @sa FSFILE_Write
+ *
+ *  @{
+ */
+
+/*! Don't flush */
+#define FS_WRITE_NOFLUSH (0x00000000)
+/*! Flush */
+#define FS_WRITE_FLUSH   (0x00010001)
 
-typedef enum{
-       PATH_INVALID = 0,       // Specifies an invalid path.
-       PATH_EMPTY = 1,         // Specifies an empty path.
-       PATH_BINARY = 2,        // Specifies a binary path, which is non-text based.
-       PATH_CHAR = 3,          // Specifies a text based path with a 8-bit byte per character.
-       PATH_WCHAR = 4,         // Specifies a text based path with a 16-bit short per character.
-}FS_pathType;
-
-typedef struct{
-       FS_pathType type;
-       u32 size;
-       u8* data;
-}FS_path;
-
-typedef struct{
-       u32 id;
-       FS_path lowPath;
-       Handle handleLow, handleHigh;
-}FS_archive;
-
-static inline FS_path FS_makePath(FS_pathType type, char* path)
+/* @} */
+
+/*! FS path type */
+typedef enum
 {
-       return (FS_path){type, strlen(path)+1, (u8*)path};
+       PATH_INVALID = 0, //!< Specifies an invalid path.
+       PATH_EMPTY   = 1, //!< Specifies an empty path.
+       PATH_BINARY  = 2, //!< Specifies a binary path, which is non-text based.
+       PATH_CHAR    = 3, //!< Specifies a text based path with a 8-bit byte per character.
+       PATH_WCHAR   = 4, //!< Specifies a text based path with a 16-bit short per character.
+} FS_pathType;
+
+/*! FS path */
+typedef struct
+{
+       FS_pathType type;  //!< FS path type.
+       u32         size;  //!< FS path size.
+       const u8    *data; //!< Pointer to FS path data.
+} FS_path;
+
+/*! FS archive */
+typedef struct
+{
+       u32     id;         //!< Archive ID.
+       FS_path lowPath;    //!< FS path.
+       Handle  handleLow;  //!< High word of handle.
+       Handle  handleHigh; //!< Low word of handle.
+} FS_archive;
+
+/*! Directory entry */
+typedef struct
+{
+  // 0x00
+  u16 name[0x106];     //!< UTF-16 encoded name
+  // 0x20C
+  u8  shortName[0x09]; //!< 8.3 file name
+  // 0x215
+  u8  unknown1;        //!< ???
+  // 0x216
+  u8  shortExt[0x04];  //!< 8.3 file extension (set to spaces for directories)
+  // 0x21A
+  u8  unknown2;        //!< ???
+  // 0x21B
+  u8  unknown3;        //!< ???
+  // 0x21C
+  u8  isDirectory;     //!< 0x00 for files, 0x01 for directories
+  // 0x21D
+  u8  unknown4;        //!< ???
+  // 0x21E
+  u8  isFile;          //!< 0x01 for files, 0x00 for directories
+  // 0x21F
+  u8  unknown5;        //!< ???
+  // 0x220
+  u8  unknown6;        //!< ???
+  // 0x221
+  u8  unknown7;        //!< ???
+  // 0x222
+  u8  unknown8;        //!< ???
+  // 0x223
+  u8  unknown9;        //!< ???
+  // 0x224
+  u8  padding[0x04];   //!< ???
+} FS_dirent;
+
+/*! Create an FS_path from a type and data pointer.
+ *
+ *  @param[in] type Path type.
+ *  @param[in] path Pointer to path data.
+ *
+ *  @returns FS_path
+ *
+ *  @sa FS_pathType
+ */
+static inline FS_path
+FS_makePath(FS_pathType type,
+            const char  *path)
+{
+       return (FS_path){type, strlen(path)+1, (const u8*)path};
 }
 
 Result fsInit(void);
@@ -45,14 +152,24 @@ Result FSUSER_OpenDirectory(Handle* handle, Handle* out, FS_archive archive, FS_
 Result FSUSER_OpenFile(Handle* handle, Handle* out, FS_archive archive, FS_path fileLowPath, u32 openflags, u32 attributes);
 Result FSUSER_OpenFileDirectly(Handle* handle, Handle* out, FS_archive archive, FS_path fileLowPath, u32 openflags, u32 attributes);
 Result FSUSER_CloseArchive(Handle* handle, FS_archive* archive);
+Result FSUSER_CreateDirectory(Handle* handle, FS_archive archive, FS_path dirLowPath);
+Result FSUSER_DeleteFile(Handle *handle, FS_archive archive, FS_path fileLowPath);
+Result FSUSER_DeleteDirectory(Handle *handle, FS_archive archive, FS_path dirLowPath);
 
 Result FSFILE_Close(Handle handle);
-Result FSFILE_Read(Handle handle, u32 *bytesRead, u64 offset, u32 *buffer, u32 size);
-Result FSFILE_Write(Handle handle, u32 *bytesWritten, u64 offset, u32 *buffer, u32 size, u32 flushFlags);
+Result FSFILE_Read(Handle handle, u32 *bytesRead, u64 offset, void *buffer, u32 size);
+Result FSFILE_Write(Handle handle, u32 *bytesWritten, u64 offset, const void *buffer, u32 size, u32 flushFlags);
 Result FSFILE_GetSize(Handle handle, u64 *size);
 Result FSFILE_SetSize(Handle handle, u64 size);
+Result FSFILE_GetAttributes(Handle handle, u32 *attributes);
+Result FSFILE_SetAttributes(Handle handle, u32 attributes);
+Result FSFILE_Flush(Handle handle);
 
-Result FSDIR_Read(Handle handle, u32 *entriesRead, u32 entrycount, u16 *buffer);
+Result FSDIR_Read(Handle handle, u32 *entriesRead, u32 entrycount, FS_dirent *buffer);
 Result FSDIR_Close(Handle handle);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif
index 08e3719a4ba42ba8739edcf05a7334c0c477b9aa..566cc43280a1b10d0ef762e9d5bf17cb41b3b917 100644 (file)
 #include <3ds/srv.h>
 #include <3ds/svc.h>
 
-Handle fsuHandle;
+/*! @internal
+ *
+ *  @file fs.c
+ * 
+ *  Filesystem Services
+ */
 
-Result fsInit(void)
+/*! FSUSER handle */
+static Handle fsuHandle;
+
+/*! Initialize FS service
+ *
+ *  @returns error
+ */
+Result
+fsInit(void)
 {
        return srvGetServiceHandle(&fsuHandle, "fs:USER");
 }
 
-Result fsExit(void)
+/*! Deinitialize FS service
+ *
+ *  @returns error
+ */
+Result
+fsExit(void)
 {
        return svcCloseHandle(fsuHandle);
 }
 
-Result FSUSER_Initialize(Handle* handle)
-{
-       if(!handle)handle=&fsuHandle;
-       u32* cmdbuf=getThreadCommandBuffer();
-       cmdbuf[0]=0x08010002; //request header code
-       cmdbuf[1]=32;
-       
-       Result ret=0;
-       if((ret=svcSendSyncRequest(*handle)))return ret;
-       
-       return cmdbuf[1];
-}
+/*! Initialize FS service handle
+ *
+ *  If @a handle is NULL, this initializes @ref fsuHandle.
+ *
+ *  @param[in] handle fs:USER service handle
+ *
+ *  @returns error
+ *
+ *  @internal
+ *
+ *  #### Request
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code [0x08010002]
+ *  1          | 0x20 (ProcessID header)
+ *
+ *  #### Response
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code
+ *  1          | Result code
+ */
+Result
+FSUSER_Initialize(Handle* handle)
+{
+       if(!handle)
+               handle = &fsuHandle;
+
+       u32 *cmdbuf = getThreadCommandBuffer();
+
+       cmdbuf[0] = 0x08010002;
+       cmdbuf[1] = 0x20;
+
+       Result ret = 0;
+       if((ret = svcSendSyncRequest(*handle)))
+               return ret;
 
-Result FSUSER_OpenFile(Handle* handle, Handle* out, FS_archive archive, FS_path fileLowPath, u32 openflags, u32 attributes) //archive needs to have been opened
-{
-       if(!handle)handle=&fsuHandle;
-       u32* cmdbuf=getThreadCommandBuffer();
-
-       cmdbuf[0]=0x080201C2;
-       cmdbuf[1]=0;
-       cmdbuf[2]=archive.handleLow;
-       cmdbuf[3]=archive.handleHigh;
-       cmdbuf[4]=fileLowPath.type;
-       cmdbuf[5]=fileLowPath.size;
-       cmdbuf[6]=openflags;
-       cmdbuf[7]=attributes;
-       cmdbuf[8]=(fileLowPath.size<<14)|2;
-       cmdbuf[9]=(u32)fileLowPath.data;
-       Result ret=0;
-       if((ret=svcSendSyncRequest(*handle)))return ret;
-       if(out)*out=cmdbuf[3];
        return cmdbuf[1];
 }
 
-Result FSUSER_OpenFileDirectly(Handle* handle, Handle* out, FS_archive archive, FS_path fileLowPath, u32 openflags, u32 attributes) //no need to have archive opened
+/*! Open a file
+ *
+ *  @param[in]  handle      fs:USER handle
+ *  @param[out] out         Output handle
+ *  @param[in]  archive     Open archive
+ *  @param[in]  fileLowPath File path
+ *  @param[in]  openFlags   Open flags
+ *  @param[in]  attributes  Create attributes
+ *
+ *  @note This requires @a archive to have been opened
+ *
+ *  @returns error
+ *
+ *  @sa fs_open_flags
+ *  @sa fs_create_attributes
+ *
+ *  @internal
+ *
+ *  #### Request
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code [0x080201C2]
+ *  1          | Transaction (usually 0)
+ *  2          | archive.handleLow
+ *  3          | archive.handleHigh
+ *  4          | fileLowPath.type
+ *  5          | fileLowPath.size
+ *  6          | openFlags
+ *  7          | attributes
+ *  8          | (fileLowPath.size << 14) \| 0x2
+ *  9          | fileLowPath.data
+ *
+ *  #### Response
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code
+ *  1          | Result code
+ *  2          | ???
+ *  3          | File handle
+ */
+Result
+FSUSER_OpenFile(Handle     *handle,
+                Handle     *out,
+                FS_archive archive,
+                FS_path    fileLowPath,
+                u32        openFlags,
+                u32        attributes)
 {
-       if(!handle)handle=&fsuHandle;
-       u32* cmdbuf=getThreadCommandBuffer();
+       if(!handle)
+               handle = &fsuHandle;
+
+       u32 *cmdbuf = getThreadCommandBuffer();
+
+       cmdbuf[0] = 0x080201C2;
+       cmdbuf[1] = 0;
+       cmdbuf[2] = archive.handleLow;
+       cmdbuf[3] = archive.handleHigh;
+       cmdbuf[4] = fileLowPath.type;
+       cmdbuf[5] = fileLowPath.size;
+       cmdbuf[6] = openFlags;
+       cmdbuf[7] = attributes;
+       cmdbuf[8] = (fileLowPath.size << 14) | 0x2;
+       cmdbuf[9] = (u32)fileLowPath.data;
+
+       Result ret = 0;
+       if((ret = svcSendSyncRequest(*handle)))
+               return ret;
+
+       if(out)
+               *out = cmdbuf[3];
 
-       cmdbuf[0]=0x08030204;
-       cmdbuf[1]=0;
-       cmdbuf[2]=archive.id;
-       cmdbuf[3]=archive.lowPath.type;
-       cmdbuf[4]=archive.lowPath.size;
-       cmdbuf[5]=fileLowPath.type;
-       cmdbuf[6]=fileLowPath.size;
-       cmdbuf[7]=openflags;
-       cmdbuf[8]=attributes;
-       cmdbuf[9]=(archive.lowPath.size<<14)|0x802;
-       cmdbuf[10]=(u32)archive.lowPath.data;
-       cmdbuf[11]=(fileLowPath.size<<14)|2;
-       cmdbuf[12]=(u32)fileLowPath.data;
-       Result ret=0;
-       if((ret=svcSendSyncRequest(*handle)))return ret;
-       if(out)*out=cmdbuf[3];
        return cmdbuf[1];
 }
 
-Result FSUSER_OpenArchive(Handle* handle, FS_archive* archive)
+/*! Open a file
+ *
+ *  @param[in]  handle      fs:USER handle
+ *  @param[out] out         Output handle
+ *  @param[in]  archive     Open archive
+ *  @param[in]  fileLowPath File path
+ *  @param[in]  openFlags   Open flags
+ *  @param[in]  attributes  Create attributes
+ *
+ *  @note This does not require @a archive to have been opened
+ *
+ *  @returns error
+ *
+ *  @sa fs_open_flags
+ *  @sa fs_create_attributes
+ *
+ *  @internal
+ *
+ *  #### Request
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *   0         | Header code [0x08030204]
+ *   1         | Transaction (usually 0)
+ *   2         | archive.id
+ *   3         | archive.lowPath.type
+ *   4         | archive.lowPath.Size
+ *   5         | fileLowPath.type
+ *   6         | fileLowPath.size
+ *   7         | openFlags
+ *   8         | attributes
+ *   9         | (archive.lowPath.size << 14 \| 0x802
+ *  10         | archive.lowPath.data
+ *  11         | (fileLowPath.size << 14) \| 0x2
+ *  12         | fileLowPath.data
+ *
+ *  #### Response
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code
+ *  1          | Result code
+ *  2          | ???
+ *  3          | File handle
+ */
+Result
+FSUSER_OpenFileDirectly(Handle     *handle,
+                        Handle     *out,
+                        FS_archive archive,
+                        FS_path    fileLowPath,
+                        u32        openFlags,
+                        u32        attributes)
 {
-       if(!archive)return -2;
-       if(!handle)handle=&fsuHandle;
-       u32* cmdbuf=getThreadCommandBuffer();
+       if(!handle)
+               handle = &fsuHandle;
+
+       u32 *cmdbuf = getThreadCommandBuffer();
+
+       cmdbuf[ 0] = 0x08030204;
+       cmdbuf[ 1] = 0;
+       cmdbuf[ 2] = archive.id;
+       cmdbuf[ 3] = archive.lowPath.type;
+       cmdbuf[ 4] = archive.lowPath.size;
+       cmdbuf[ 5] = fileLowPath.type;
+       cmdbuf[ 6] = fileLowPath.size;
+       cmdbuf[ 7] = openFlags;
+       cmdbuf[ 8] = attributes;
+       cmdbuf[ 9] = (archive.lowPath.size << 14) | 0x802;
+       cmdbuf[10] = (u32)archive.lowPath.data;
+       cmdbuf[11] = (fileLowPath.size << 14) | 0x2;
+       cmdbuf[12] = (u32)fileLowPath.data;
+
+       Result ret = 0;
+       if((ret = svcSendSyncRequest(*handle)))
+               return ret;
+
+       if(out)
+               *out = cmdbuf[3];
 
-       cmdbuf[0]=0x080C00C2;
-       cmdbuf[1]=archive->id;
-       cmdbuf[2]=archive->lowPath.type;
-       cmdbuf[3]=archive->lowPath.size;
-       cmdbuf[4]=(archive->lowPath.size<<14)|0x2;
-       cmdbuf[5]=(u32)archive->lowPath.data;
-       Result ret=0;
-       if((ret=svcSendSyncRequest(*handle)))return ret;
-       archive->handleLow=cmdbuf[2];
-       archive->handleHigh=cmdbuf[3];
        return cmdbuf[1];
 }
 
-Result FSUSER_OpenDirectory(Handle* handle, Handle* out, FS_archive archive, FS_path dirLowPath)
+/*! Delete a file
+ *
+ *  @param[in] handle      fs:USER handle
+ *  @param[in] archive     Open archive
+ *  @param[in] fileLowPath File path
+ *
+ *  @returns error
+ *
+ *  @internal
+ *
+ *  #### Request
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code [0x08040142]
+ *  1          | 0
+ *  2          | archive.handleLow
+ *  3          | archive.handleHigh
+ *  4          | fileLowPath.type
+ *  5          | fileLowPath.size
+ *  6          | (fileLowPath.size << 14) \| 0x2
+ *  7          | fileLowPath.data
+ *
+ *  #### Response
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code
+ *  1          | Result code
+ */
+Result
+FSUSER_DeleteFile(Handle     *handle,
+                  FS_archive archive,
+                  FS_path    fileLowPath)
 {
-       if(!handle)handle=&fsuHandle;
-       u32* cmdbuf=getThreadCommandBuffer();
+       if(!handle)
+               handle = &fsuHandle;
+
+       u32 *cmdbuf = getThreadCommandBuffer();
+
+       cmdbuf[0] = 0x08040142;
+       cmdbuf[1] = 0;
+       cmdbuf[2] = archive.handleLow;
+       cmdbuf[3] = archive.handleHigh;
+       cmdbuf[4] = fileLowPath.type;
+       cmdbuf[5] = fileLowPath.size;
+       cmdbuf[6] = (fileLowPath.size << 14) | 0x2;
+       cmdbuf[7] = (u32)fileLowPath.data;
+
+       Result ret = 0;
+       if((ret = svcSendSyncRequest(*handle)))
+               return ret;
 
-       cmdbuf[0]=0x080B0102;
-       cmdbuf[1]=archive.handleLow;
-       cmdbuf[2]=archive.handleHigh;
-       cmdbuf[3]=dirLowPath.type;
-       cmdbuf[4]=dirLowPath.size;
-       cmdbuf[5]=(dirLowPath.size<<14)|0x2;
-       cmdbuf[6]=(u32)dirLowPath.data;
-       Result ret=0;
-       if((ret=svcSendSyncRequest(*handle)))return ret;
-       if(out)*out=cmdbuf[3];
        return cmdbuf[1];
 }
 
-Result FSUSER_CloseArchive(Handle* handle, FS_archive* archive)
+/* stub */
+Result
+FSUSER_RenameFile(void)
+{
+       return -1;
+}
+
+/*! Delete a directory
+ *
+ *  @param[in] handle     fs:USER handle
+ *  @param[in] archive    Open archive
+ *  @param[in] dirLowPath Directory path
+ *
+ *  @returns error
+ *
+ *  @internal
+ *
+ *  #### Request
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code [0x08060142]
+ *  1          | 0
+ *  2          | archive.handleLow
+ *  3          | archive.handleHigh
+ *  4          | dirLowPath.type
+ *  5          | dirLowPath.size
+ *  6          | (dirLowPath.size << 14) \| 0x2
+ *  7          | dirLowPath.data
+ *
+ *  #### Response
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code
+ *  1          | Result code
+ */
+Result
+FSUSER_DeleteDirectory(Handle     *handle,
+                       FS_archive archive,
+                       FS_path    dirLowPath)
 {
-       if(!archive)return -2;
-       if(!handle)handle=&fsuHandle;
-       u32* cmdbuf=getThreadCommandBuffer();
+       if(!handle)
+               handle = &fsuHandle;
+
+       u32 *cmdbuf = getThreadCommandBuffer();
+
+       cmdbuf[0] = 0x08060142;
+       cmdbuf[1] = 0;
+       cmdbuf[2] = archive.handleLow;
+       cmdbuf[3] = archive.handleHigh;
+       cmdbuf[4] = dirLowPath.type;
+       cmdbuf[5] = dirLowPath.size;
+       cmdbuf[6] = (dirLowPath.size << 14) | 0x2;
+       cmdbuf[7] = (u32)dirLowPath.data;
+
+       Result ret = 0;
+       if((ret = svcSendSyncRequest(*handle)))
+               return ret;
 
-       cmdbuf[0]=0x080E0080;
-       cmdbuf[1]=archive->handleLow;
-       cmdbuf[2]=archive->handleLow;
-       Result ret=0;
-       if((ret=svcSendSyncRequest(*handle)))return ret;
        return cmdbuf[1];
 }
 
-Result FSFILE_Close(Handle handle)
+/* stub */
+Result
+FSUSER_DeleteDirectoryRecursively(void)
 {
-       u32* cmdbuf=getThreadCommandBuffer();
+       return -1;
+}
 
-       cmdbuf[0]=0x08080000;
+/* stub */
+Result
+FSUSER_CreateFile(void)
+{
+       return -1;
+}
 
-       Result ret=0;
-       if((ret=svcSendSyncRequest(handle)))return ret;
+/*! Create a directory
+ *
+ *  @param[in] handle     fs:USER handle
+ *  @param[in] archive    Open archive
+ *  @param[in] dirLowPath Directory path to create
+ *
+ *  @returns error
+ *
+ *  @internal
+ *
+ *  #### Request
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code [0x08090182]
+ *  1          | 0
+ *  2          | archive.handleLow
+ *  3          | archive.handleHigh
+ *  4          | dirLowPath.type
+ *  5          | dirLowPath.size
+ *  6          | 0
+ *  7          | (dirLowPath.size << 14) \| 0x2
+ *  8          | dirLowPath.data
+ *
+ *  #### Response
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code
+ *  1          | Result code
+ */
+Result
+FSUSER_CreateDirectory(Handle     *handle,
+                       FS_archive archive,
+                       FS_path    dirLowPath)
+{
+       if(!handle)
+               handle = &fsuHandle;
+
+       u32 *cmdbuf = getThreadCommandBuffer();
+
+       cmdbuf[0] = 0x08090182;
+       cmdbuf[1] = 0;
+       cmdbuf[2] = archive.handleLow;
+       cmdbuf[3] = archive.handleHigh;
+       cmdbuf[4] = dirLowPath.type;
+       cmdbuf[5] = dirLowPath.size;
+       cmdbuf[6] = 0;
+       cmdbuf[7] = (dirLowPath.size << 14) | 0x2;
+       cmdbuf[8] = (u32)dirLowPath.data;
+
+       Result ret = 0;
+       if((ret = svcSendSyncRequest(*handle)))
+               return ret;
+
+       return cmdbuf[1];
+}
+
+/* stub */
+Result
+FSUSER_RenameDirectory(void)
+{
+       return -1;
+}
+
+/*! Open a directory
+ *
+ *  @param[in]  handle     fs:USER handle
+ *  @param[out] out        Output handle
+ *  @param[in]  archive    Open archive
+ *  @param[in]  dirLowPath Directory path
+ *
+ *  @returns error
+ *
+ *  @internal
+ *
+ *  #### Request
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code [0x080B0102]
+ *  1          | archive.handleLow
+ *  2          | archive.handleHigh
+ *  3          | dirLowPath.type
+ *  4          | dirLowPath.size
+ *  5          | (dirLowPath.size << 14) \| 0x2
+ *  6          | dirLowPath.data
+ *
+ *  #### Response
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code
+ *  1          | Result code
+ *  2          | Directory handle
+ */
+Result
+FSUSER_OpenDirectory(Handle     *handle,
+                     Handle     *out,
+                     FS_archive archive,
+                     FS_path    dirLowPath)
+{
+       if(!handle)
+               handle = &fsuHandle;
+
+       u32 *cmdbuf = getThreadCommandBuffer();
+
+       cmdbuf[0] = 0x080B0102;
+       cmdbuf[1] = archive.handleLow;
+       cmdbuf[2] = archive.handleHigh;
+       cmdbuf[3] = dirLowPath.type;
+       cmdbuf[4] = dirLowPath.size;
+       cmdbuf[5] = (dirLowPath.size << 14) | 0x2;
+       cmdbuf[6] = (u32)dirLowPath.data;
+
+       Result ret = 0;
+       if((ret = svcSendSyncRequest(*handle)))
+               return ret;
+
+       if(out)
+               *out = cmdbuf[3];
 
        return cmdbuf[1];
 }
 
-Result FSFILE_Read(Handle handle, u32 *bytesRead, u64 offset, u32 *buffer, u32 size)
+/*! Open an archive
+ *
+ *  @param[in]     handle  fs:USER handle
+ *  @param[in,out] archive Archive to open
+ *
+ *  @returns error
+ *
+ *  @internal
+ *
+ *  #### Request
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code [0x080C00C2]
+ *  1          | archive->id
+ *  2          | archive->lowPath.type
+ *  3          | archive->lowPath.size
+ *  4          | (archive->lowPath.size << 14) \| 0x2
+ *  5          | archive->lowPath.data
+ *
+ *  #### Response
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code
+ *  1          | Result code
+ *  2          | archive->handleLow
+ *  3          | archive->handleHigh
+ */
+Result
+FSUSER_OpenArchive(Handle     *handle,
+                   FS_archive *archive)
 {
-       u32 *cmdbuf=getThreadCommandBuffer();
-       cmdbuf[0]=0x080200C2;
-       cmdbuf[1]=(u32)offset;
-       cmdbuf[2]=(u32)(offset>>32);
-       cmdbuf[3]=size;
-       cmdbuf[4]=(size<<4)|12;
-       cmdbuf[5]=(u32)buffer;
-       Result ret=0;
-       if((ret=svcSendSyncRequest(handle)))return ret;
+       if(!archive)
+               return -2;
+
+       if(!handle)
+               handle = &fsuHandle;
+
+       u32 *cmdbuf = getThreadCommandBuffer();
 
-       if(bytesRead)*bytesRead=cmdbuf[2];
+       cmdbuf[0] = 0x080C00C2;
+       cmdbuf[1] = archive->id;
+       cmdbuf[2] = archive->lowPath.type;
+       cmdbuf[3] = archive->lowPath.size;
+       cmdbuf[4] = (archive->lowPath.size << 14) | 0x2;
+       cmdbuf[5] = (u32)archive->lowPath.data;
+
+       Result ret = 0;
+       if((ret = svcSendSyncRequest(*handle)))
+               return ret;
+
+       archive->handleLow  = cmdbuf[2];
+       archive->handleHigh = cmdbuf[3];
 
        return cmdbuf[1];
 }
 
-//WARNING : using wrong flushFlags CAN corrupt the archive you're writing to.
-//another warning : data should *not* be in RO memory
-Result FSFILE_Write(Handle handle, u32 *bytesWritten, u64 offset, u32 *data, u32 size, u32 flushFlags)
+
+/*! Close an open archive
+ *
+ *  @param[in]     handle  fs:USER handle
+ *  @param[in,out] archive Archive to close
+ *
+ *  @returns error
+ *
+ *  @internal
+ *
+ *  #### Request
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code [0x080B0102]
+ *  1          | archive->handleLow
+ *  2          | archive->handleHigh
+ *
+ *  #### Response
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code
+ *  1          | Result code
+ */
+Result
+FSUSER_CloseArchive(Handle     *handle,
+                    FS_archive *archive)
 {
-       u32 *cmdbuf=getThreadCommandBuffer();
+       if(!archive)
+               return -2;
 
-       cmdbuf[0]=0x08030102;
-       cmdbuf[1]=(u32)offset;
-       cmdbuf[2]=(u32)(offset>>32);
-       cmdbuf[3]=size;
-       cmdbuf[4]=flushFlags;
-       cmdbuf[5]=(size<<4)|10;
-       cmdbuf[6]=(u32)data;
+       if(!handle)
+               handle = &fsuHandle;
 
-       Result ret=0;
-       if((ret=svcSendSyncRequest(handle)))return ret;
+       u32 *cmdbuf = getThreadCommandBuffer();
 
-       if(bytesWritten)*bytesWritten=cmdbuf[2];
+       cmdbuf[0] = 0x080E0080;
+       cmdbuf[1] = archive->handleLow;
+       cmdbuf[2] = archive->handleHigh;
+
+       Result ret = 0;
+       if((ret = svcSendSyncRequest(*handle)))
+               return ret;
 
        return cmdbuf[1];
 }
 
-Result FSFILE_GetSize(Handle handle, u64 *size)
+/*! Close an open file
+ *
+ *  @param[in] handle Open file handle
+ *
+ *  @returns error
+ *
+ *  @internal
+ *
+ *  #### Request
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code [0x08080000]
+ *
+ *  #### Response
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code
+ *  1          | Result code
+ */
+Result
+FSFILE_Close(Handle handle)
 {
-       u32 *cmdbuf=getThreadCommandBuffer();
+       u32* cmdbuf = getThreadCommandBuffer();
+
+       cmdbuf[0] = 0x08080000;
+
+       Result ret = 0;
+       if((ret = svcSendSyncRequest(handle)))
+               return ret;
+
+       return cmdbuf[1];
+}
+
+/*! Read data from an open file
+ *
+ *  @param[in]  handle    Open file handle
+ *  @param[out] bytesRead Number of bytes read
+ *  @param[in]  offset    File offset to read from
+ *  @param[out] buffer    Buffer to read into
+ *  @param[in]  size      Number of bytes to read
+ *
+ *  @returns error
+ *
+ *  @internal
+ *
+ *  #### Request
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code [0x080200C2]
+ *  1          | offset (low word)
+ *  2          | offset (high word)
+ *  3          | size
+ *  4          | (size << 4) \| 0xC
+ *  5          | buffer
+ *
+ *  #### Response
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code
+ *  1          | Result code
+ *  2          | Number of bytes read
+ */
+Result
+FSFILE_Read(Handle handle,
+            u32    *bytesRead,
+            u64    offset,
+            void   *buffer,
+            u32    size)
+{
+       u32 *cmdbuf = getThreadCommandBuffer();
+
+       cmdbuf[0] = 0x080200C2;
+       cmdbuf[1] = (u32)offset;
+       cmdbuf[2] = (u32)(offset >> 32);
+       cmdbuf[3] = size;
+       cmdbuf[4] = (size << 4) | 0xC;
+       cmdbuf[5] = (u32)buffer;
+
+       Result ret = 0;
+       if((ret = svcSendSyncRequest(handle)))
+               return ret;
+
+       if(bytesRead)
+               *bytesRead = cmdbuf[2];
+
+       return cmdbuf[1];
+}
+
+/*! Write data to an open file
+ *
+ *  @param[in]  handle       Open file handle
+ *  @param[out] bytesWritten Number of bytes read
+ *  @param[in]  offset       File offset to write to
+ *  @param[in]  buffer       Buffer to write from
+ *  @param[in]  size         Number of bytes to write
+ *  @param[in]  flushFlags   Flush flags
+ *
+ *  @returns error
+ *
+ *  @sa fs_write_flush_flags
+ *
+ *  @warning
+ *    Using invalid flushFlags can corrupt the archive you're writing to.
+ *
+ *  @warning
+ *    Data should not be in read-only memory.
+ *
+ *  @internal
+ *
+ *  #### Request
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code [0x08030102]
+ *  1          | offset (low word)
+ *  2          | offset (high word)
+ *  3          | size
+ *  4          | flushFlags
+ *  5          | (size << 4) \| 0xA
+ *  6          | buffer
+ *
+ *  #### Response
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code
+ *  1          | Result code
+ *  2          | Number of bytes written
+ */
+Result
+FSFILE_Write(Handle     handle,
+             u32        *bytesWritten,
+             u64        offset,
+             const void *buffer,
+             u32        size,
+             u32        flushFlags)
+{
+       u32 *cmdbuf = getThreadCommandBuffer();
+
+       cmdbuf[0] = 0x08030102;
+       cmdbuf[1] = (u32)offset;
+       cmdbuf[2] = (u32)(offset >> 32);
+       cmdbuf[3] = size;
+       cmdbuf[4] = flushFlags;
+       cmdbuf[5] = (size << 4) | 0xA;
+       cmdbuf[6] = (u32)buffer;
+
+       Result ret = 0;
+       if((ret = svcSendSyncRequest(handle)))
+               return ret;
+
+       if(bytesWritten)
+               *bytesWritten = cmdbuf[2];
+
+       return cmdbuf[1];
+}
+
+/*! Get the size of an open file
+ *
+ *  @param[in]  handle Open file handle
+ *  @param[out] size   Output size
+ *
+ *  @returns error
+ *
+ *  @internal
+ *
+ *  #### Request
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code [0x08040000]
+ *
+ *  #### Response
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code
+ *  1          | Result code
+ *  2          | File size (lower word)
+ *  3          | File size (upper word)
+ */
+Result
+FSFILE_GetSize(Handle handle,
+               u64    *size)
+{
+       u32 *cmdbuf = getThreadCommandBuffer();
+
        cmdbuf[0] = 0x08040000;
-       Result ret=0;
-       if((ret=svcSendSyncRequest(handle)))return ret;
-       if(size)*size = *((u64*)&cmdbuf[2]);
+
+       Result ret = 0;
+       if((ret = svcSendSyncRequest(handle)))
+               return ret;
+
+       if(size)
+               *size = (u64)cmdbuf[2] | ((u64)cmdbuf[3] << 32);
+
+       return cmdbuf[1];
+}
+
+/*! Set the size of an open file
+ *
+ *  @param[in] handle Open file handle
+ *  @param[in] size   Size to set
+ *
+ *  @returns error
+ *
+ *  @internal
+ *
+ *  #### Request
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code [0x08050080]
+ *  1          | size (lower word)
+ *  2          | size (upper word)
+ *
+ *  #### Response
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code
+ *  1          | Result code
+ */
+Result
+FSFILE_SetSize(Handle handle,
+               u64    size)
+{
+       u32 *cmdbuf = getThreadCommandBuffer();
+
+       cmdbuf[0] = 0x08050080;
+       cmdbuf[1] = (u32)size;
+       cmdbuf[2] = (u32)(size >> 32);
+
+       Result ret = 0;
+       if((ret = svcSendSyncRequest(handle)))
+               return ret;
+
+
+       return cmdbuf[1];
+}
+
+/*! Get attributes for an open file
+ *
+ *  @param[in]  handle     Open file handle
+ *  @param[out] attributes Output attributes
+ *
+ *  @returns error
+ *
+ *  @internal
+ *
+ *  #### Request
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code [0x08060000]
+ *
+ *  #### Response
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code
+ *  1          | Result code
+ *  2          | Attributes
+ */
+Result
+FSFILE_GetAttributes(Handle handle,
+                     u32    *attributes)
+{
+       u32 *cmdbuf = getThreadCommandBuffer();
+
+       cmdbuf[0] = 0x08060000;
+
+       Result ret = 0;
+       if((ret = svcSendSyncRequest(handle)))
+               return ret;
+
+       if(attributes)
+               *attributes = cmdbuf[2];
+
        return cmdbuf[1];
 }
 
-Result FSFILE_SetSize(Handle handle, u64 size)
+/*! Set attributes for an open file
+ *
+ *  @param[in] handle     Open file handle
+ *  @param[in] attributes Attributes to set
+ *
+ *  @returns error
+ *
+ *  @internal
+ *
+ *  #### Request
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code [0x08070040]
+ *  1          | Attributes
+ *
+ *  #### Response
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code
+ *  1          | Result code
+ */
+Result
+FSFILE_SetAttributes(Handle handle,
+                     u32    attributes)
 {
-    u32 *cmdbuf = getThreadCommandBuffer();
+       u32 *cmdbuf = getThreadCommandBuffer();
+
+       cmdbuf[0] = 0x08070040;
+       cmdbuf[1] = attributes;
 
-    cmdbuf[0] = 0x08050080;
-    cmdbuf[1] = (u32)size;
-    cmdbuf[2] = (u32)(size >> 32);
+       Result ret = 0;
+       if((ret = svcSendSyncRequest(handle)))
+               return ret;
 
-    Result ret = 0;
-    if ((ret = svcSendSyncRequest(handle)))return ret;
+       return cmdbuf[1];
+}
+
+/*! Flush an open file
+ *
+ *  @param[in] handle Open file handle
+ *
+ *  @returns error
+ *
+ *  @internal
+ *
+ *  #### Request
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code [0x08090000]
+ *
+ *  #### Response
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code
+ *  1          | Result code
+ */
+Result
+FSFILE_Flush(Handle handle)
+{
+       u32 *cmdbuf = getThreadCommandBuffer();
+
+       cmdbuf[0] = 0x08090000;
 
+       Result ret = 0;
+       if((ret = svcSendSyncRequest(handle)))
+               return ret;
 
-    return cmdbuf[1];
+       return cmdbuf[1];
 }
 
-Result FSDIR_Read(Handle handle, u32 *entriesRead, u32 entrycount, u16 *buffer)
+/*! Read a directory entry from an open directory
+ *
+ *  @param[in]  handle      Open directory handle
+ *  @param[out] entriesRead Output number of entries read
+ *  @param[in]  entryCount  Number of entries to read
+ *  @param[out] buffer      Output buffer
+ *
+ *  @returns error
+ *
+ *  @internal
+ *
+ *  #### Request
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code [0x08010042]
+ *  1          | entryCount
+ *  2          | ((entrycount*0x228) << 4) \| 0xC
+ *  3          | buffer
+ *
+ *  #### Response
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code
+ *  1          | Result code
+ *  2          | Number of entries read
+ */
+Result
+FSDIR_Read(Handle    handle,
+           u32       *entriesRead,
+           u32       entryCount,
+           FS_dirent *buffer)
 {
-       u32 *cmdbuf=getThreadCommandBuffer();
+       u32 *cmdbuf = getThreadCommandBuffer();
 
-       cmdbuf[0]=0x08010042;
-       cmdbuf[1]=entrycount;
-       cmdbuf[2]=((entrycount*0x228)<<4)|0xC;
-       cmdbuf[3]=(u32)buffer;
+       cmdbuf[0] = 0x08010042;
+       cmdbuf[1] = entryCount;
+       cmdbuf[2] = ((entryCount*0x228) << 4) | 0xC;
+       cmdbuf[3] = (u32)buffer;
 
-       Result ret=0;
-       if((ret=svcSendSyncRequest(handle)))return ret;
+       Result ret = 0;
+       if((ret = svcSendSyncRequest(handle)))
+               return ret;
 
-       if(entriesRead)*entriesRead=cmdbuf[2];
+       if(entriesRead)
+               *entriesRead = cmdbuf[2];
 
        return cmdbuf[1];
 }
 
-Result FSDIR_Close(Handle handle)
+/*! Close an open directory
+ *
+ *  @param[in] handle Open directory handle
+ *
+ *  @returns error
+ *
+ *  @internal
+ *
+ *  #### Request
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code [0x08020000]
+ *
+ *  #### Response
+ *
+ *  Index Word | Description
+ *  -----------|-------------------------
+ *  0          | Header code
+ *  1          | Result code
+ */
+Result
+FSDIR_Close(Handle handle)
 {
-       u32* cmdbuf=getThreadCommandBuffer();
+       u32 *cmdbuf = getThreadCommandBuffer();
 
-       cmdbuf[0]=0x08020000;
+       cmdbuf[0] = 0x08020000;
 
-       Result ret=0;
-       if((ret=svcSendSyncRequest(handle)))return ret;
+       Result ret = 0;
+       if((ret = svcSendSyncRequest(handle)))
+               return ret;
 
        return cmdbuf[1];
 }