Merge branch 'utf'

Conflicts:
	libctru/source/sdmc_dev.c
This commit is contained in:
Dave Murphy 2015-02-15 13:28:34 +00:00
commit 727d5d4767
24 changed files with 917 additions and 159 deletions

View File

@ -29,7 +29,8 @@ SOURCES := source \
source/gpu \ source/gpu \
source/services \ source/services \
source/services/soc \ source/services/soc \
source/util \ source/util/rbtree \
source/util/utf \
source/system source/system
DATA := data DATA := data

View File

@ -0,0 +1,108 @@
#pragma once
#include <stdint.h>
#include <sys/types.h>
/*! Convert a UTF-8 sequence into a UTF-32 codepoint
*
* @param[out] out Output codepoint
* @param[in] in Input sequence
*
* @returns number of input code units consumed
* @returns -1 for error
*/
ssize_t decode_utf8 (uint32_t *out, const uint8_t *in);
/*! Convert a UTF-16 sequence into a UTF-32 codepoint
*
* @param[out] out Output codepoint
* @param[in] in Input sequence
*
* @returns number of input code units consumed
* @returns -1 for error
*/
ssize_t decode_utf16(uint32_t *out, const uint16_t *in);
/*! Convert a UTF-32 codepoint into a UTF-8 sequence
*
* @param[out] out Output sequence
* @param[in] in Input codepoint
*
* @returns number of output code units produced
* @returns -1 for error
*
* @note \a out must be able to store 4 code units
*/
ssize_t encode_utf8 (uint8_t *out, uint32_t in);
/*! Convert a UTF-32 codepoint into a UTF-16 sequence
*
* @param[out] out Output sequence
* @param[in] in Input codepoint
*
* @returns number of output code units produced
* @returns -1 for error
*
* @note \a out must be able to store 2 code units
*/
ssize_t encode_utf16(uint16_t *out, uint32_t in);
/*! Convert a UTF-8 sequence into a UTF-16 sequence
*
* @param[out] out Output sequence
* @param[in] in Input sequence
*
* @returns number of output code units produced
* @returns -1 for error
*/
size_t utf8_to_utf16(uint16_t *out, const uint8_t *in, size_t len);
/*! Convert a UTF-8 sequence into a UTF-32 sequence
*
* @param[out] out Output sequence
* @param[in] in Input sequence
*
* @returns number of output code units produced
* @returns -1 for error
*/
size_t utf8_to_utf32(uint32_t *out, const uint8_t *in, size_t len);
/*! Convert a UTF-16 sequence into a UTF-8 sequence
*
* @param[out] out Output sequence
* @param[in] in Input sequence
*
* @returns number of output code units produced
* @returns -1 for error
*/
size_t utf16_to_utf8(uint8_t *out, const uint16_t *in, size_t len);
/*! Convert a UTF-16 sequence into a UTF-32 sequence
*
* @param[out] out Output sequence
* @param[in] in Input sequence
*
* @returns number of output code units produced
* @returns -1 for error
*/
size_t utf16_to_utf32(uint32_t *out, const uint16_t *in, size_t len);
/*! Convert a UTF-32 sequence into a UTF-8 sequence
*
* @param[out] out Output sequence
* @param[in] in Input sequence
*
* @returns number of output code units produced
* @returns -1 for error
*/
size_t utf32_to_utf8(uint8_t *out, const uint32_t *in, size_t len);
/*! Convert a UTF-32 sequence into a UTF-16 sequence
*
* @param[out] out Output sequence
* @param[in] in Input sequence
*
* @returns number of output code units produced
* @returns -1 for error
*/
size_t utf32_to_utf16(uint16_t *out, const uint32_t *in, size_t len);

View File

@ -1,15 +1,17 @@
#include <fcntl.h>
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <fcntl.h>
#include <limits.h> #include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <sys/dirent.h>
#include <sys/iosupport.h> #include <sys/iosupport.h>
#include <sys/param.h> #include <sys/param.h>
#include <unistd.h>
#include <string.h>
#include <3ds/types.h> #include <3ds/types.h>
#include <3ds/sdmc.h> #include <3ds/sdmc.h>
#include <3ds/services/fs.h> #include <3ds/services/fs.h>
#include <3ds/util/utf.h>
/*! @internal /*! @internal
@ -19,6 +21,8 @@
* SDMC Device * SDMC Device
*/ */
static int sdmc_translate_error(Result error);
static int sdmc_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode); static int sdmc_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode);
static int sdmc_close(struct _reent *r, int fd); static int sdmc_close(struct _reent *r, int fd);
static ssize_t sdmc_write(struct _reent *r, int fd, const char *ptr, size_t len); static ssize_t sdmc_write(struct _reent *r, int fd, const char *ptr, size_t len);
@ -40,6 +44,7 @@ static int sdmc_ftruncate(struct _reent *r, int fd, off_t len);
static int sdmc_fsync(struct _reent *r, int fd); static int sdmc_fsync(struct _reent *r, int fd);
static int sdmc_chmod(struct _reent *r, const char *path, mode_t mode); static int sdmc_chmod(struct _reent *r, const char *path, mode_t mode);
static int sdmc_fchmod(struct _reent *r, int fd, mode_t mode); static int sdmc_fchmod(struct _reent *r, int fd, mode_t mode);
static int sdmc_rmdir(struct _reent *r, const char *name);
/*! @cond INTERNAL */ /*! @cond INTERNAL */
@ -87,6 +92,7 @@ sdmc_devoptab =
.deviceData = NULL, .deviceData = NULL,
.chmod_r = sdmc_chmod, .chmod_r = sdmc_chmod,
.fchmod_r = sdmc_fchmod, .fchmod_r = sdmc_fchmod,
.rmdir_r = sdmc_rmdir,
}; };
/*! SDMC archive handle */ /*! SDMC archive handle */
@ -103,28 +109,106 @@ static FS_archive sdmcArchive =
/*! @endcond */ /*! @endcond */
static char __cwd[PATH_MAX+1] = "/"; static char __cwd[PATH_MAX+1] = "/";
static char __fixedpath[PATH_MAX+1]; static char __fixedpath[PATH_MAX+1];
static uint16_t __utf16path[PATH_MAX+1];
static const char *sdmc_fixpath(const char *path) static const char*
sdmc_fixpath(struct _reent *r,
const char *path)
{ {
size_t units;
uint32_t code;
const uint8_t *p = (const uint8_t*)path;
// Move the path pointer to the start of the actual path // Move the path pointer to the start of the actual path
if (strchr (path, ':') != NULL) do
{ {
path = strchr (path, ':') + 1; units = decode_utf8(&code, p);
if(units == (size_t)-1)
{
r->_errno = EILSEQ;
return NULL;
}
p += units;
} while(code != ':' && code != 0);
// We found a colon; p points to the actual path
if(code == ':')
path = (const char*)p;
// Make sure there are no more colons and that the
// remainder of the filename is valid UTF-8
p = (const uint8_t*)path;
do
{
units = decode_utf8(&code, p);
if(units == (size_t)-1)
{
r->_errno = EILSEQ;
return NULL;
}
if(code == ':')
{
r->_errno = EINVAL;
return NULL;
}
p += units;
} while(code != 0);
if(path[0] == '/')
strncpy(__fixedpath, path, PATH_MAX+1);
else
{
strncpy(__fixedpath, __cwd, PATH_MAX+1);
strncat(__fixedpath, path, PATH_MAX+1);
} }
if (strchr (path, ':') != NULL) return NULL; if(__fixedpath[PATH_MAX] != 0)
{
__fixedpath[PATH_MAX] = 0;
if (path[0]=='/') return path; r->_errno = ENAMETOOLONG;
return NULL;
strncpy(__fixedpath,__cwd,PATH_MAX); }
strncat(__fixedpath,path,PATH_MAX);
__fixedpath[PATH_MAX] = 0;
return __fixedpath; return __fixedpath;
}
static const FS_path
sdmc_utf16path(struct _reent *r,
const char *path)
{
size_t units;
FS_path fspath;
fspath.data = NULL;
if(sdmc_fixpath(r, path) == NULL)
return fspath;
units = utf8_to_utf16(__utf16path, (const uint8_t*)__fixedpath, PATH_MAX+1);
if(units == (size_t)-1)
{
r->_errno = EILSEQ;
return fspath;
}
if(__utf16path[PATH_MAX] != 0)
{
r->_errno = ENAMETOOLONG;
return fspath;
}
__utf16path[units] = 0;
fspath.type = PATH_WCHAR;
fspath.size = (units+1)*sizeof(uint16_t);
fspath.data = (const u8*)__utf16path;
return fspath;
} }
extern int __system_argc; extern int __system_argc;
@ -135,29 +219,56 @@ static bool sdmcInitialised = false;
/*! Initialize SDMC device */ /*! Initialize SDMC device */
Result sdmcInit(void) Result sdmcInit(void)
{ {
Result rc = 0; size_t units;
uint32_t code;
char *p;
Result rc = 0;
if (sdmcInitialised) return rc; if(sdmcInitialised)
return rc;
rc = FSUSER_OpenArchive(NULL, &sdmcArchive); rc = FSUSER_OpenArchive(NULL, &sdmcArchive);
if(rc == 0) if(rc == 0)
{ {
int dev = AddDevice(&sdmc_devoptab); int dev = AddDevice(&sdmc_devoptab);
if (dev != -1) { if(dev != -1)
{
setDefaultDevice(dev); setDefaultDevice(dev);
if (__system_argc != 0 && __system_argv[0] != NULL) if(__system_argc != 0 && __system_argv[0] != NULL)
{ {
if (FindDevice(__system_argv[0]) == dev) if(FindDevice(__system_argv[0]) == dev)
{ {
strncpy(__fixedpath,__system_argv[0],PATH_MAX); strncpy(__fixedpath,__system_argv[0],PATH_MAX);
char *last_slash = strrchr(__fixedpath,'/'); if(__fixedpath[PATH_MAX] != 0)
if (last_slash != NULL) { {
last_slash[0] = 0; __fixedpath[PATH_MAX] = 0;
chdir(__fixedpath); }
else
{
char *last_slash = NULL;
p = __fixedpath;
do
{
units = decode_utf8(&code, (const uint8_t*)p);
if(units == (size_t)-1)
{
last_slash = NULL;
break;
}
if(code == '/')
last_slash = p;
p += units;
} while(code != 0);
if(last_slash != NULL)
{
last_slash[0] = 0;
chdir(__fixedpath);
}
} }
} }
} }
@ -174,13 +285,14 @@ Result sdmcExit(void)
{ {
Result rc = 0; Result rc = 0;
if (!sdmcInitialised) return rc; if(!sdmcInitialised) return rc;
rc = FSUSER_CloseArchive(NULL, &sdmcArchive); rc = FSUSER_CloseArchive(NULL, &sdmcArchive);
if(rc == 0) if(rc == 0)
{
RemoveDevice("sdmc"); RemoveDevice("sdmc");
sdmcInitialised = false;
sdmcInitialised = false; }
return rc; return rc;
} }
@ -207,15 +319,11 @@ sdmc_open(struct _reent *r,
Result rc; Result rc;
u32 sdmc_flags = 0; u32 sdmc_flags = 0;
u32 attributes = FS_ATTRIBUTE_NONE; u32 attributes = FS_ATTRIBUTE_NONE;
const char *pathptr = NULL; FS_path fs_path;
pathptr = sdmc_fixpath(path); fs_path = sdmc_utf16path(r, path);
if(fs_path.data == NULL)
if(pathptr==NULL)
{
r->_errno=EINVAL;
return -1; return -1;
}
/* get pointer to our data */ /* get pointer to our data */
sdmc_file_t *file = (sdmc_file_t*)fileStruct; sdmc_file_t *file = (sdmc_file_t*)fileStruct;
@ -256,14 +364,10 @@ sdmc_open(struct _reent *r,
/* Test O_EXCL. */ /* Test O_EXCL. */
if((flags & O_CREAT) && (flags & O_EXCL)) if((flags & O_CREAT) && (flags & O_EXCL))
{ {
rc = FSUSER_CreateFile(NULL, sdmcArchive, FS_makePath(PATH_CHAR, pathptr), 0); rc = FSUSER_CreateFile(NULL, sdmcArchive, fs_path, 0);
if(rc != 0) if(rc != 0)
{ {
r->_errno = rc; r->_errno = sdmc_translate_error(rc);
if(rc == 0x82044BE)
r->_errno = EEXIST;
if(rc == 0x86044D2)
r->_errno = ENOSPC;
return -1; return -1;
} }
} }
@ -273,7 +377,7 @@ sdmc_open(struct _reent *r,
attributes |= FS_ATTRIBUTE_READONLY;*/ attributes |= FS_ATTRIBUTE_READONLY;*/
/* open the file */ /* open the file */
rc = FSUSER_OpenFile(NULL, &fd, sdmcArchive, FS_makePath(PATH_CHAR, pathptr), rc = FSUSER_OpenFile(NULL, &fd, sdmcArchive, fs_path,
sdmc_flags, attributes); sdmc_flags, attributes);
if(rc == 0) if(rc == 0)
{ {
@ -283,17 +387,18 @@ sdmc_open(struct _reent *r,
if(rc != 0) if(rc != 0)
{ {
FSFILE_Close(fd); FSFILE_Close(fd);
r->_errno = rc; r->_errno = sdmc_translate_error(rc);
return -1; return -1;
} }
} }
file->fd = fd; file->fd = fd;
file->flags = (flags & (O_ACCMODE|O_APPEND|O_SYNC)); file->flags = (flags & (O_ACCMODE|O_APPEND|O_SYNC));
file->offset = 0; file->offset = 0;
return 0; return 0;
} }
r->_errno = rc; r->_errno = sdmc_translate_error(rc);
return -1; return -1;
} }
@ -318,7 +423,7 @@ sdmc_close(struct _reent *r,
if(rc == 0) if(rc == 0)
return 0; return 0;
r->_errno = rc; r->_errno = sdmc_translate_error(rc);
return -1; return -1;
} }
@ -339,9 +444,8 @@ sdmc_write(struct _reent *r,
size_t len) size_t len)
{ {
Result rc; Result rc;
u32 bytes; u32 bytes, bytesWritten = 0;
u32 sync = 0; u32 sync = 0;
u64 offset;
/* get pointer to our data */ /* get pointer to our data */
sdmc_file_t *file = (sdmc_file_t*)fd; sdmc_file_t *file = (sdmc_file_t*)fd;
@ -355,38 +459,52 @@ sdmc_write(struct _reent *r,
/* check if this is synchronous or not */ /* check if this is synchronous or not */
if(file->flags & O_SYNC) if(file->flags & O_SYNC)
sync = 0x10001; sync = FS_WRITE_FLUSH;
/* initialize offset */
offset = file->offset;
if(file->flags & O_APPEND) if(file->flags & O_APPEND)
{ {
/* append means write from the end of the file */ /* append means write from the end of the file */
rc = FSFILE_GetSize(file->fd, &offset); rc = FSFILE_GetSize(file->fd, &file->offset);
if(rc != 0) if(rc != 0)
{ {
r->_errno = rc; r->_errno = sdmc_translate_error(rc);
return -1; return -1;
} }
} }
/* TODO: Copy to internal buffer and write in chunks. /* Copy to internal buffer and write in chunks.
* You cannot write from read-only memory. * You cannot write from read-only memory.
*/ */
static char tmp_buffer[8192];
/* write the data */ while(len > 0)
rc = FSFILE_Write(file->fd, &bytes, offset, (u32*)ptr, (u32)len, sync);
if(rc == 0)
{ {
/* update current file offset; if O_APPEND, this moves it to the size_t toWrite = len;
* new end-of-file if(toWrite > sizeof(tmp_buffer))
*/ toWrite = sizeof(tmp_buffer);
file->offset = offset + bytes;
return (ssize_t)bytes; /* copy to internal buffer */
memcpy(tmp_buffer, ptr, toWrite);
/* write the data */
rc = FSFILE_Write(file->fd, &bytes, file->offset,
(u32*)tmp_buffer, (u32)toWrite, sync);
if(rc != 0)
{
/* return partial transfer */
if(bytesWritten > 0)
return bytesWritten;
r->_errno = sdmc_translate_error(rc);
return -1;
}
file->offset += bytes;
bytesWritten += bytes;
ptr += bytes;
len -= bytes;
} }
r->_errno = rc; return bytesWritten;
return -1;
} }
/*! Read from an open file /*! Read from an open file
@ -427,7 +545,7 @@ sdmc_read(struct _reent *r,
return (ssize_t)bytes; return (ssize_t)bytes;
} }
r->_errno = rc; r->_errno = sdmc_translate_error(rc);
return -1; return -1;
} }
@ -471,7 +589,7 @@ sdmc_seek(struct _reent *r,
rc = FSFILE_GetSize(file->fd, &offset); rc = FSFILE_GetSize(file->fd, &offset);
if(rc != 0) if(rc != 0)
{ {
r->_errno = rc; r->_errno = sdmc_translate_error(rc);
return -1; return -1;
} }
break; break;
@ -509,7 +627,21 @@ sdmc_fstat(struct _reent *r,
int fd, int fd,
struct stat *st) struct stat *st)
{ {
r->_errno = ENOSYS; Result rc;
u64 size;
sdmc_file_t *file = (sdmc_file_t*)fd;
rc = FSFILE_GetSize(file->fd, &size);
if(rc == 0)
{
memset(st, 0, sizeof(struct stat));
st->st_size = (off_t)size;
st->st_nlink = 1;
st->st_mode = S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
return 0;
}
r->_errno = sdmc_translate_error(rc);
return -1; return -1;
} }
@ -527,49 +659,33 @@ sdmc_stat(struct _reent *r,
const char *file, const char *file,
struct stat *st) struct stat *st)
{ {
Handle fd; Handle fd;
Result rc; Result rc;
const char *pathptr = NULL; FS_path fs_path;
pathptr = sdmc_fixpath(file); fs_path = sdmc_utf16path(r, file);
if(fs_path.data == NULL)
if(pathptr==NULL)
{
r->_errno=EINVAL;
return -1; return -1;
}
if( (rc = FSUSER_OpenFile(NULL, &fd, sdmcArchive, FS_makePath(PATH_CHAR, pathptr), if((rc = FSUSER_OpenFile(NULL, &fd, sdmcArchive, fs_path,
FS_OPEN_READ, FS_ATTRIBUTE_NONE))==0) FS_OPEN_READ, FS_ATTRIBUTE_NONE)) == 0)
{ {
u64 tmpsize = 0; sdmc_file_t tmpfd = { .fd = fd };
rc = FSFILE_GetSize(fd, &tmpsize); rc = sdmc_fstat(r, (int)&tmpfd, st);
FSFILE_Close(fd); FSFILE_Close(fd);
if(rc==0) return rc;
{ }
memset(st, 0, sizeof(struct stat)); else if((rc = FSUSER_OpenDirectory(NULL, &fd, sdmcArchive, fs_path)) == 0)
st->st_size = (off_t)tmpsize; {
st->st_nlink = 1; memset(st, 0, sizeof(struct stat));
st->st_uid = 1; st->st_nlink = 1;
st->st_gid = 2; st->st_mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO;
st->st_mode = S_IFREG | S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH; FSDIR_Close(fd);
return 0; return 0;
}
} }
if( (rc = FSUSER_OpenDirectory(NULL, &fd, sdmcArchive, FS_makePath(PATH_CHAR, pathptr))) == 0 )
{
memset(st, 0, sizeof(struct stat));
st->st_nlink = 1;
st->st_uid = 1;
st->st_gid = 2;
st->st_mode = S_IFDIR | S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH;
FSFILE_Close(fd);
return 0;
}
r->_errno = EBADF; r->_errno = sdmc_translate_error(rc);
return -1; return -1;
} }
@ -603,7 +719,18 @@ static int
sdmc_unlink(struct _reent *r, sdmc_unlink(struct _reent *r,
const char *name) const char *name)
{ {
r->_errno = ENOSYS; Result rc;
FS_path fs_path;
fs_path = sdmc_utf16path(r, name);
if(fs_path.data == NULL)
return -1;
rc = FSUSER_DeleteFile(NULL, sdmcArchive, fs_path);
if(rc == 0)
return 0;
r->_errno = sdmc_translate_error(rc);
return -1; return -1;
} }
@ -619,32 +746,24 @@ static int
sdmc_chdir(struct _reent *r, sdmc_chdir(struct _reent *r,
const char *name) const char *name)
{ {
Handle fd; Handle fd;
Result rc; Result rc;
const char *pathptr = NULL; FS_path fs_path;
pathptr = sdmc_fixpath(name); fs_path = sdmc_utf16path(r, name);
if(fs_path.data == NULL)
if(pathptr==NULL)
{
r->_errno=EINVAL;
return -1; return -1;
}
rc = FSUSER_OpenDirectory(NULL, &fd, sdmcArchive, FS_makePath(PATH_CHAR, pathptr)); rc = FSUSER_OpenDirectory(NULL, &fd, sdmcArchive, fs_path);
if(rc == 0) if(rc == 0)
{ {
FSDIR_Close(fd); FSDIR_Close(fd);
strncpy(__cwd,pathptr,PATH_MAX); strncpy(__cwd, __fixedpath, PATH_MAX);
} return 0;
else
{
r->_errno=EINVAL;
return -1;
} }
return 0; r->_errno = sdmc_translate_error(rc);
return -1;
} }
/*! Rename a file /*! Rename a file
@ -661,7 +780,30 @@ sdmc_rename(struct _reent *r,
const char *oldName, const char *oldName,
const char *newName) const char *newName)
{ {
r->_errno = ENOSYS; Result rc;
FS_path fs_path_old, fs_path_new;
static uint16_t __utf16path_old[PATH_MAX+1];
fs_path_old = sdmc_utf16path(r, oldName);
if(fs_path_old.data == NULL)
return -1;
memcpy(__utf16path_old, __utf16path, sizeof(__utf16path));
fs_path_old.data = (const u8*)__utf16path_old;
fs_path_new = sdmc_utf16path(r, newName);
if(fs_path_new.data == NULL)
return -1;
rc = FSUSER_RenameFile(NULL, sdmcArchive, fs_path_old, sdmcArchive, fs_path_new);
if(rc == 0)
return 0;
rc = FSUSER_RenameDirectory(NULL, sdmcArchive, fs_path_old, sdmcArchive, fs_path_new);
if(rc == 0)
return 0;
r->_errno = sdmc_translate_error(rc);
return -1; return -1;
} }
@ -680,23 +822,19 @@ sdmc_mkdir(struct _reent *r,
int mode) int mode)
{ {
Result rc; Result rc;
const char *pathptr = NULL; FS_path fs_path;
pathptr = sdmc_fixpath(path); fs_path = sdmc_utf16path(r, path);
if(fs_path.data == NULL)
if(pathptr==NULL)
{
r->_errno=EINVAL;
return -1; return -1;
}
/* TODO: Use mode to set directory attributes. */ /* TODO: Use mode to set directory attributes. */
rc = FSUSER_CreateDirectory(NULL, sdmcArchive, FS_makePath(PATH_CHAR, pathptr)); rc = FSUSER_CreateDirectory(NULL, sdmcArchive, fs_path);
if(rc == 0) if(rc == 0)
return 0; return 0;
r->_errno = ENOSYS; r->_errno = sdmc_translate_error(rc);
return -1; return -1;
} }
@ -714,23 +852,20 @@ sdmc_diropen(struct _reent *r,
DIR_ITER *dirState, DIR_ITER *dirState,
const char *path) const char *path)
{ {
Handle fd; Handle fd;
Result rc; Result rc;
const char *pathptr = NULL; FS_path fs_path;
pathptr = sdmc_fixpath(path); fs_path = sdmc_utf16path(r, path);
if(pathptr==NULL) if(fs_path.data == NULL)
{
r->_errno=EINVAL;
return NULL; return NULL;
}
/* get pointer to our data */ /* get pointer to our data */
sdmc_dir_t *dir = (sdmc_dir_t*)(dirState->dirStruct); sdmc_dir_t *dir = (sdmc_dir_t*)(dirState->dirStruct);
/* open the directory */ /* open the directory */
rc = FSUSER_OpenDirectory(NULL, &fd, sdmcArchive, FS_makePath(PATH_CHAR, pathptr)); rc = FSUSER_OpenDirectory(NULL, &fd, sdmcArchive, fs_path);
if(rc == 0) if(rc == 0)
{ {
dir->fd = fd; dir->fd = fd;
@ -738,7 +873,7 @@ sdmc_diropen(struct _reent *r,
return dirState; return dirState;
} }
r->_errno = rc; r->_errno = sdmc_translate_error(rc);
return NULL; return NULL;
} }
@ -774,14 +909,15 @@ sdmc_dirnext(struct _reent *r,
char *filename, char *filename,
struct stat *filestat) struct stat *filestat)
{ {
Result rc; Result rc;
u32 entries; u32 entries;
u16 *name; size_t units;
/* get pointer to our data */ /* get pointer to our data */
sdmc_dir_t *dir = (sdmc_dir_t*)(dirState->dirStruct); sdmc_dir_t *dir = (sdmc_dir_t*)(dirState->dirStruct);
/* fetch the next entry */ /* fetch the next entry */
memset(&dir->entry_data, 0, sizeof(dir->entry_data));
rc = FSDIR_Read(dir->fd, &entries, 1, &dir->entry_data); rc = FSDIR_Read(dir->fd, &entries, 1, &dir->entry_data);
if(rc == 0) if(rc == 0)
{ {
@ -799,16 +935,25 @@ sdmc_dirnext(struct _reent *r,
else else
filestat->st_mode = S_IFREG; filestat->st_mode = S_IFREG;
/* copy the name */ /* convert name from UTF-16 to UTF-8 */
name = dir->entry_data.name; memset(filename, 0, NAME_MAX);
while(*name) units = utf16_to_utf8((uint8_t*)filename, dir->entry_data.name, NAME_MAX);
*filename++ = *name++; if(units == (size_t)-1)
*filename = 0; {
r->_errno = EILSEQ;
return -1;
}
if(filename[NAME_MAX-1] != 0)
{
r->_errno = ENAMETOOLONG;
return -1;
}
return 0; return 0;
} }
r->_errno = rc; r->_errno = sdmc_translate_error(rc);
return -1; return -1;
} }
@ -834,7 +979,7 @@ sdmc_dirclose(struct _reent *r,
if(rc == 0) if(rc == 0)
return 0; return 0;
r->_errno = rc; r->_errno = sdmc_translate_error(rc);
return -1; return -1;
} }
@ -883,7 +1028,7 @@ sdmc_statvfs(struct _reent *r,
return 0; return 0;
} }
r->_errno = rc; r->_errno = sdmc_translate_error(rc);
return -1; return -1;
} }
@ -918,7 +1063,7 @@ sdmc_ftruncate(struct _reent *r,
if(rc == 0) if(rc == 0)
return 0; return 0;
r->_errno = rc; r->_errno = sdmc_translate_error(rc);
return -1; return -1;
} }
@ -943,7 +1088,7 @@ sdmc_fsync(struct _reent *r,
if(rc == 0) if(rc == 0)
return 0; return 0;
r->_errno = rc; r->_errno = sdmc_translate_error(rc);
return -1; return -1;
} }
@ -982,3 +1127,91 @@ sdmc_fchmod(struct _reent *r,
r->_errno = ENOSYS; r->_errno = ENOSYS;
return -1; return -1;
} }
/*! Remove a directory
*
* @param[in,out] r newlib reentrancy struct
* @param[in] name Path of directory to remove
*
* @returns 0 for success
* @returns -1 for error
*/
static int
sdmc_rmdir(struct _reent *r,
const char *name)
{
Result rc;
FS_path fs_path;
fs_path = sdmc_utf16path(r, name);
if(fs_path.data == NULL)
return -1;
rc = FSUSER_DeleteDirectory(NULL, sdmcArchive, fs_path);
if(rc == 0)
return 0;
r->_errno = sdmc_translate_error(rc);
return -1;
}
/*! Error map */
typedef struct
{
Result fs_error; //!< Error from FS service
int error; //!< POSIX errno
} error_map_t;
/*! Error table */
static const error_map_t error_table[] =
{
/* keep this list sorted! */
{ 0x082044BE, EEXIST, },
{ 0x086044D2, ENOSPC, },
{ 0xC8804478, ENOENT, },
{ 0xC92044FA, ENOENT, },
{ 0xE0E046BE, EINVAL, },
{ 0xE0E046BF, ENAMETOOLONG, },
};
static const size_t num_errors = sizeof(error_table)/sizeof(error_table[0]);
/*! Comparison function for bsearch on error_table
*
* @param[in] p1 Left side of comparison
* @param[in] p2 Right side of comparison
*
* @returns <0 if lhs < rhs
* @returns >0 if lhs > rhs
* @returns 0 if lhs == rhs
*/
static int
error_cmp(const void *p1, const void *p2)
{
const error_map_t *lhs = (const error_map_t*)p1;
const error_map_t *rhs = (const error_map_t*)p2;
if((u32)lhs->fs_error < (u32)rhs->fs_error)
return -1;
else if((u32)lhs->fs_error > (u32)rhs->fs_error)
return 1;
return 0;
}
/*! Translate FS service error to errno
*
* @param[in] error FS service error
*
* @returns errno
*/
static int
sdmc_translate_error(Result error)
{
error_map_t key = { .fs_error = error };
const error_map_t *rc = bsearch(&key, error_table, num_errors,
sizeof(error_map_t), error_cmp);
if(rc != NULL)
return rc->error;
return (int)error;
}

View File

@ -0,0 +1,25 @@
#include "3ds/util/utf.h"
ssize_t
decode_utf16(uint32_t *out,
const uint16_t *in)
{
uint16_t code1, code2;
code1 = *in++;
if(code1 >= 0xD800 && code1 < 0xDC00)
{
/* surrogate pair */
code2 = *in++;
if(code2 >= 0xDC00 && code2 < 0xE000)
{
*out = (code1 << 10) + code2 - 0x35FDC00;
return 2;
}
return -1;
}
*out = code1;
return 1;
}

View File

@ -0,0 +1,88 @@
#include "3ds/util/utf.h"
ssize_t
decode_utf8(uint32_t *out,
const uint8_t *in)
{
uint8_t code1, code2, code3, code4;
code1 = *in++;
if(code1 < 0x80)
{
/* 1-byte sequence */
*out = code1;
return 1;
}
else if(code1 < 0xC2)
{
return -1;
}
else if(code1 < 0xE0)
{
/* 2-byte sequence */
code2 = *in++;
if((code2 & 0xC0) != 0x80)
{
return -1;
}
*out = (code1 << 6) + code2 - 0x3080;
return 2;
}
else if(code1 < 0xF0)
{
/* 3-byte sequence */
code2 = *in++;
if((code2 & 0xC0) != 0x80)
{
return -1;
}
if(code1 == 0xE0 && code2 < 0xA0)
{
return -1;
}
code3 = *in++;
if((code3 & 0xC0) != 0x80)
{
return -1;
}
*out = (code1 << 12) + (code2 << 6) + code3 - 0xE2080;
return 3;
}
else if(code1 < 0xF5)
{
/* 4-byte sequence */
code2 = *in++;
if((code2 & 0xC0) != 0x80)
{
return -1;
}
if(code1 == 0xF0 && code2 < 0x90)
{
return -1;
}
if(code1 == 0xF4 && code2 >= 0x90)
{
return -1;
}
code3 = *in++;
if((code3 & 0xC0) != 0x80)
{
return -1;
}
code4 = *in++;
if((code4 & 0xC0) != 0x80)
{
return -1;
}
*out = (code1 << 18) + (code2 << 12) + (code3 << 6) + code4 - 0x3C82080;
return 4;
}
return -1;
}

View File

@ -0,0 +1,24 @@
#include "3ds/util/utf.h"
ssize_t
encode_utf16(uint16_t *out,
uint32_t in)
{
if(in < 0x10000)
{
if(out != NULL)
*out++ = in;
return 1;
}
else if(in < 0x110000)
{
if(out != NULL)
{
*out++ = (in >> 10) + 0xD7C0;
*out++ = (in & 0x3FF) + 0xDC00;
}
return 2;
}
return -1;
}

View File

@ -0,0 +1,45 @@
#include "3ds/util/utf.h"
ssize_t
encode_utf8(uint8_t *out,
uint32_t in)
{
if(in < 0x80)
{
if(out != NULL)
*out++ = in;
return 1;
}
else if(in < 0x800)
{
if(out != NULL)
{
*out++ = (in >> 6) + 0xC0;
*out++ = (in & 0x3F) + 0x80;
}
return 2;
}
else if(in < 0x10000)
{
if(out != NULL)
{
*out++ = (in >> 12) + 0xE0;
*out++ = ((in >> 6) & 0x3F) + 0x80;
*out++ = (in & 0x3F) + 0x80;
}
return 3;
}
else if(in < 0x110000)
{
if(out != NULL)
{
*out++ = (in >> 18) + 0xF0;
*out++ = ((in >> 12) & 0x3F) + 0x80;
*out++ = ((in >> 6) & 0x3F) + 0x80;
*out++ = (in & 0x3F) + 0x80;
}
return 4;
}
return -1;
}

View File

@ -0,0 +1,35 @@
#include "3ds/util/utf.h"
size_t
utf16_to_utf32(uint32_t *out,
const uint16_t *in,
size_t len)
{
size_t rc = 0;
ssize_t units;
uint32_t code;
do
{
units = decode_utf16(&code, in);
if(units == -1)
return (size_t)-1;
if(code > 0)
{
in += units;
if(out != NULL)
{
if(rc < len)
*out++ = code;
else
return rc;
}
++rc;
}
} while(code > 0);
return rc;
}

View File

@ -0,0 +1,48 @@
#include "3ds/util/utf.h"
size_t
utf16_to_utf8(uint8_t *out,
const uint16_t *in,
size_t len)
{
size_t rc = 0;
ssize_t units;
uint32_t code;
uint8_t encoded[4];
do
{
units = decode_utf16(&code, in);
if(units == -1)
return (size_t)-1;
if(code > 0)
{
in += units;
units = encode_utf8(encoded, code);
if(units == -1)
return (size_t)-1;
if(out != NULL)
{
if(rc + units <= len)
{
*out++ = encoded[0];
if(units > 1)
*out++ = encoded[1];
if(units > 2)
*out++ = encoded[2];
if(units > 3)
*out++ = encoded[3];
}
else
return rc;
}
rc += units;
}
} while(code > 0);
return rc;
}

View File

@ -0,0 +1,34 @@
#include "3ds/util/utf.h"
size_t
utf32_to_utf16(uint16_t *out,
const uint32_t *in,
size_t len)
{
size_t rc = 0;
ssize_t units;
uint16_t encoded[2];
while(*in > 0)
{
units = encode_utf16(encoded, *in++);
if(units == -1)
return (size_t)-1;
if(out != NULL)
{
if(rc + units <= len)
{
*out++ = encoded[0];
if(units > 1)
*out++ = encoded[1];
}
else
return rc;
}
rc += units;
}
return rc;
}

View File

@ -0,0 +1,38 @@
#include "3ds/util/utf.h"
size_t
utf32_to_utf8(uint8_t *out,
const uint32_t *in,
size_t len)
{
size_t rc = 0;
ssize_t units;
uint8_t encoded[4];
while(*in > 0)
{
units = encode_utf8(encoded, *in++);
if(units == -1)
return (size_t)-1;
if(out != NULL)
{
if(rc + units <= len)
{
*out++ = encoded[0];
if(units > 1)
*out++ = encoded[1];
if(units > 2)
*out++ = encoded[2];
if(units > 3)
*out++ = encoded[3];
}
else
return rc;
}
rc += units;
}
return rc;
}

View File

@ -0,0 +1,44 @@
#include "3ds/util/utf.h"
size_t
utf8_to_utf16(uint16_t *out,
const uint8_t *in,
size_t len)
{
size_t rc = 0;
ssize_t units;
uint32_t code;
uint16_t encoded[2];
do
{
units = decode_utf8(&code, in);
if(units == -1)
return (size_t)-1;
if(code > 0)
{
in += units;
units = encode_utf16(encoded, code);
if(units == -1)
return (size_t)-1;
if(out != NULL)
{
if(rc + units <= len)
{
*out++ = encoded[0];
if(units > 1)
*out++ = encoded[1];
}
else
return rc;
}
rc += units;
}
} while(code > 0);
return rc;
}

View File

@ -0,0 +1,35 @@
#include "3ds/util/utf.h"
size_t
utf8_to_utf32(uint32_t *out,
const uint8_t *in,
size_t len)
{
size_t rc = 0;
ssize_t units;
uint32_t code;
do
{
units = decode_utf8(&code, in);
if(units == -1)
return (size_t)-1;
if(code > 0)
{
in += units;
if(out != NULL)
{
if(rc < len)
*out++ = code;
else
return rc;
}
++rc;
}
} while(code > 0);
return rc;
}