From b7ec5341b9063cdd0e41600442c6f3706f2b9b6d Mon Sep 17 00:00:00 2001 From: mtheall Date: Wed, 7 Jan 2015 23:44:30 -0600 Subject: [PATCH 1/2] Add UTF-8 <-> UTF-16 support. --- libctru/Makefile | 5 +- libctru/include/3ds/util/utf.h | 108 ++++ libctru/source/sdmc_dev.c | 547 +++++++++++++----- .../source/util/{ => rbtree}/rbtree_clear.c | 0 .../source/util/{ => rbtree}/rbtree_empty.c | 0 .../source/util/{ => rbtree}/rbtree_find.c | 0 .../source/util/{ => rbtree}/rbtree_init.c | 0 .../source/util/{ => rbtree}/rbtree_insert.c | 0 .../util/{ => rbtree}/rbtree_internal.h | 0 .../util/{ => rbtree}/rbtree_iterator.c | 0 .../source/util/{ => rbtree}/rbtree_minmax.c | 0 .../source/util/{ => rbtree}/rbtree_remove.c | 0 .../source/util/{ => rbtree}/rbtree_rotate.c | 0 .../source/util/{ => rbtree}/rbtree_size.c | 0 libctru/source/util/utf/decode_utf16.c | 25 + libctru/source/util/utf/decode_utf8.c | 88 +++ libctru/source/util/utf/encode_utf16.c | 24 + libctru/source/util/utf/encode_utf8.c | 45 ++ libctru/source/util/utf/utf16_to_utf32.c | 35 ++ libctru/source/util/utf/utf16_to_utf8.c | 48 ++ libctru/source/util/utf/utf32_to_utf16.c | 34 ++ libctru/source/util/utf/utf32_to_utf8.c | 38 ++ libctru/source/util/utf/utf8_to_utf16.c | 44 ++ libctru/source/util/utf/utf8_to_utf32.c | 35 ++ 24 files changed, 917 insertions(+), 159 deletions(-) create mode 100644 libctru/include/3ds/util/utf.h rename libctru/source/util/{ => rbtree}/rbtree_clear.c (100%) rename libctru/source/util/{ => rbtree}/rbtree_empty.c (100%) rename libctru/source/util/{ => rbtree}/rbtree_find.c (100%) rename libctru/source/util/{ => rbtree}/rbtree_init.c (100%) rename libctru/source/util/{ => rbtree}/rbtree_insert.c (100%) rename libctru/source/util/{ => rbtree}/rbtree_internal.h (100%) rename libctru/source/util/{ => rbtree}/rbtree_iterator.c (100%) rename libctru/source/util/{ => rbtree}/rbtree_minmax.c (100%) rename libctru/source/util/{ => rbtree}/rbtree_remove.c (100%) rename libctru/source/util/{ => rbtree}/rbtree_rotate.c (100%) rename libctru/source/util/{ => rbtree}/rbtree_size.c (100%) create mode 100644 libctru/source/util/utf/decode_utf16.c create mode 100644 libctru/source/util/utf/decode_utf8.c create mode 100644 libctru/source/util/utf/encode_utf16.c create mode 100644 libctru/source/util/utf/encode_utf8.c create mode 100644 libctru/source/util/utf/utf16_to_utf32.c create mode 100644 libctru/source/util/utf/utf16_to_utf8.c create mode 100644 libctru/source/util/utf/utf32_to_utf16.c create mode 100644 libctru/source/util/utf/utf32_to_utf8.c create mode 100644 libctru/source/util/utf/utf8_to_utf16.c create mode 100644 libctru/source/util/utf/utf8_to_utf32.c diff --git a/libctru/Makefile b/libctru/Makefile index 279231c..aedc1ba 100644 --- a/libctru/Makefile +++ b/libctru/Makefile @@ -29,7 +29,8 @@ SOURCES := source \ source/gpu \ source/services \ source/services/soc \ - source/util \ + source/util/rbtree \ + source/util/utf \ source/system DATA := data @@ -38,7 +39,7 @@ INCLUDES := include #--------------------------------------------------------------------------------- # options for code generation #--------------------------------------------------------------------------------- -ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=softfp +ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard CFLAGS := -g -Wall -O2 -mword-relocations \ -fomit-frame-pointer -ffast-math \ diff --git a/libctru/include/3ds/util/utf.h b/libctru/include/3ds/util/utf.h new file mode 100644 index 0000000..eaaae25 --- /dev/null +++ b/libctru/include/3ds/util/utf.h @@ -0,0 +1,108 @@ +#pragma once + +#include +#include + +/*! 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); diff --git a/libctru/source/sdmc_dev.c b/libctru/source/sdmc_dev.c index d02c962..3d8b4c0 100644 --- a/libctru/source/sdmc_dev.c +++ b/libctru/source/sdmc_dev.c @@ -1,15 +1,17 @@ -#include #include -#include +#include #include +#include +#include +#include #include #include +#include -#include #include <3ds/types.h> #include <3ds/sdmc.h> #include <3ds/services/fs.h> - +#include <3ds/util/utf.h> /*! @internal @@ -19,6 +21,8 @@ * 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_close(struct _reent *r, int fd); 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_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_rmdir(struct _reent *r, const char *name); /*! @cond INTERNAL */ @@ -87,6 +92,7 @@ sdmc_devoptab = .deviceData = NULL, .chmod_r = sdmc_chmod, .fchmod_r = sdmc_fchmod, + .rmdir_r = sdmc_rmdir, }; /*! SDMC archive handle */ @@ -103,28 +109,106 @@ static FS_archive sdmcArchive = /*! @endcond */ -static char __cwd[PATH_MAX+1] = "/"; -static char __fixedpath[PATH_MAX+1]; +static char __cwd[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 - 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 (path[0]=='/') return path; - - strncpy(__fixedpath,__cwd,PATH_MAX); - strncat(__fixedpath,path,PATH_MAX); - __fixedpath[PATH_MAX] = 0; + if(__fixedpath[PATH_MAX] != 0) + { + __fixedpath[PATH_MAX] = 0; + r->_errno = ENAMETOOLONG; + return NULL; + } 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; @@ -135,29 +219,56 @@ static bool sdmcInitialised = false; /*! Initialize SDMC device */ 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); - - if(rc == 0) { int dev = AddDevice(&sdmc_devoptab); - if (dev != -1) { + if(dev != -1) + { 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); - char *last_slash = strrchr(__fixedpath,'/'); - if (last_slash != NULL) { - last_slash[0] = 0; - chdir(__fixedpath); + if(__fixedpath[PATH_MAX] != 0) + { + __fixedpath[PATH_MAX] = 0; + } + 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; - if (!sdmcInitialised) return rc; + if(!sdmcInitialised) return rc; rc = FSUSER_CloseArchive(NULL, &sdmcArchive); if(rc == 0) + { RemoveDevice("sdmc"); - - sdmcInitialised = false; + sdmcInitialised = false; + } return rc; } @@ -207,15 +319,11 @@ sdmc_open(struct _reent *r, Result rc; u32 sdmc_flags = 0; u32 attributes = FS_ATTRIBUTE_NONE; - const char *pathptr = NULL; + FS_path fs_path; - pathptr = sdmc_fixpath(path); - - if(pathptr==NULL) - { - r->_errno=EINVAL; + fs_path = sdmc_utf16path(r, path); + if(fs_path.data == NULL) return -1; - } /* get pointer to our data */ sdmc_file_t *file = (sdmc_file_t*)fileStruct; @@ -256,14 +364,10 @@ sdmc_open(struct _reent *r, /* Test 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) { - r->_errno = rc; - if(rc == 0x82044BE) - r->_errno = EEXIST; - if(rc == 0x86044D2) - r->_errno = ENOSPC; + r->_errno = sdmc_translate_error(rc); return -1; } } @@ -273,7 +377,7 @@ sdmc_open(struct _reent *r, attributes |= FS_ATTRIBUTE_READONLY;*/ /* 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); if(rc == 0) { @@ -283,17 +387,18 @@ sdmc_open(struct _reent *r, if(rc != 0) { FSFILE_Close(fd); - r->_errno = rc; + r->_errno = sdmc_translate_error(rc); return -1; } } + file->fd = fd; file->flags = (flags & (O_ACCMODE|O_APPEND|O_SYNC)); file->offset = 0; return 0; } - r->_errno = rc; + r->_errno = sdmc_translate_error(rc); return -1; } @@ -318,7 +423,7 @@ sdmc_close(struct _reent *r, if(rc == 0) return 0; - r->_errno = rc; + r->_errno = sdmc_translate_error(rc); return -1; } @@ -339,9 +444,8 @@ sdmc_write(struct _reent *r, size_t len) { Result rc; - u32 bytes; + u32 bytes, bytesWritten = 0; u32 sync = 0; - u64 offset; /* get pointer to our data */ sdmc_file_t *file = (sdmc_file_t*)fd; @@ -355,38 +459,52 @@ sdmc_write(struct _reent *r, /* check if this is synchronous or not */ if(file->flags & O_SYNC) - sync = 0x10001; + sync = FS_WRITE_FLUSH; - /* initialize offset */ - offset = file->offset; if(file->flags & O_APPEND) { /* 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) { - r->_errno = rc; + r->_errno = sdmc_translate_error(rc); return -1; } } - /* TODO: Copy to internal buffer and write in chunks. - * You cannot write from read-only memory. + /* Copy to internal buffer and write in chunks. + * You cannot write from read-only memory. */ - - /* write the data */ - rc = FSFILE_Write(file->fd, &bytes, offset, (u32*)ptr, (u32)len, sync); - if(rc == 0) + static char tmp_buffer[8192]; + while(len > 0) { - /* update current file offset; if O_APPEND, this moves it to the - * new end-of-file - */ - file->offset = offset + bytes; - return (ssize_t)bytes; + size_t toWrite = len; + if(toWrite > sizeof(tmp_buffer)) + toWrite = sizeof(tmp_buffer); + + /* 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 -1; + return bytesWritten; } /*! Read from an open file @@ -427,7 +545,7 @@ sdmc_read(struct _reent *r, return (ssize_t)bytes; } - r->_errno = rc; + r->_errno = sdmc_translate_error(rc); return -1; } @@ -471,7 +589,7 @@ sdmc_seek(struct _reent *r, rc = FSFILE_GetSize(file->fd, &offset); if(rc != 0) { - r->_errno = rc; + r->_errno = sdmc_translate_error(rc); return -1; } break; @@ -509,7 +627,21 @@ sdmc_fstat(struct _reent *r, int fd, 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; } @@ -527,48 +659,32 @@ sdmc_stat(struct _reent *r, const char *file, struct stat *st) { - Handle fd; - Result rc; - const char *pathptr = NULL; + Handle fd; + Result rc; + FS_path fs_path; - pathptr = sdmc_fixpath(file); - - if(pathptr==NULL) - { - r->_errno=EINVAL; + fs_path = sdmc_utf16path(r, file); + if(fs_path.data == NULL) return -1; - } - if( (rc = FSUSER_OpenFile(NULL, &fd, sdmcArchive, FS_makePath(PATH_CHAR, pathptr), - FS_OPEN_READ, FS_ATTRIBUTE_NONE))==0) + if((rc = FSUSER_OpenFile(NULL, &fd, sdmcArchive, fs_path, + FS_OPEN_READ, FS_ATTRIBUTE_NONE)) == 0) { - u64 tmpsize = 0; - rc = FSFILE_GetSize(fd, &tmpsize); - + sdmc_file_t tmpfd = { .fd = fd }; + rc = sdmc_fstat(r, (int)&tmpfd, st); FSFILE_Close(fd); - if(rc==0) - { - memset(st, 0, sizeof(struct stat)); - st->st_size = (off_t)tmpsize; - st->st_nlink = 1; - st->st_uid = 1; - st->st_gid = 2; - st->st_mode = S_IFREG | S_IWUSR | S_IWGRP | S_IWOTH | S_IRUSR | S_IRGRP | S_IROTH; - return 0; - } + return rc; + } + else if((rc = FSUSER_OpenDirectory(NULL, &fd, sdmcArchive, fs_path)) == 0) + { + memset(st, 0, sizeof(struct stat)); + st->st_nlink = 1; + st->st_mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO; + 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; - return 0; - } - r->_errno = EBADF; + r->_errno = sdmc_translate_error(rc); return -1; } @@ -602,7 +718,18 @@ static int sdmc_unlink(struct _reent *r, 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; } @@ -618,32 +745,24 @@ static int sdmc_chdir(struct _reent *r, const char *name) { - Handle fd; - Result rc; - const char *pathptr = NULL; + Handle fd; + Result rc; + FS_path fs_path; - pathptr = sdmc_fixpath(name); - - if(pathptr==NULL) - { - r->_errno=EINVAL; + fs_path = sdmc_utf16path(r, name); + if(fs_path.data == NULL) return -1; - } - rc = FSUSER_OpenDirectory(NULL, &fd, sdmcArchive, FS_makePath(PATH_CHAR, pathptr)); + rc = FSUSER_OpenDirectory(NULL, &fd, sdmcArchive, fs_path); if(rc == 0) { FSDIR_Close(fd); - strncpy(__cwd,pathptr,PATH_MAX); - } - else - { - r->_errno=EINVAL; - return -1; + strncpy(__cwd, __fixedpath, PATH_MAX); + return 0; } - return 0; - + r->_errno = sdmc_translate_error(rc); + return -1; } /*! Rename a file @@ -660,7 +779,30 @@ sdmc_rename(struct _reent *r, const char *oldName, 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; } @@ -679,23 +821,19 @@ sdmc_mkdir(struct _reent *r, int mode) { Result rc; - const char *pathptr = NULL; + FS_path fs_path; - pathptr = sdmc_fixpath(path); - - if(pathptr==NULL) - { - r->_errno=EINVAL; + fs_path = sdmc_utf16path(r, path); + if(fs_path.data == NULL) return -1; - } /* 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) return 0; - r->_errno = ENOSYS; + r->_errno = sdmc_translate_error(rc); return -1; } @@ -713,23 +851,20 @@ sdmc_diropen(struct _reent *r, DIR_ITER *dirState, const char *path) { - Handle fd; - Result rc; - const char *pathptr = NULL; + Handle fd; + Result rc; + FS_path fs_path; - pathptr = sdmc_fixpath(path); + fs_path = sdmc_utf16path(r, path); - if(pathptr==NULL) - { - r->_errno=EINVAL; + if(fs_path.data == NULL) return NULL; - } /* get pointer to our data */ sdmc_dir_t *dir = (sdmc_dir_t*)(dirState->dirStruct); /* 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) { dir->fd = fd; @@ -737,7 +872,7 @@ sdmc_diropen(struct _reent *r, return dirState; } - r->_errno = rc; + r->_errno = sdmc_translate_error(rc); return NULL; } @@ -773,14 +908,15 @@ sdmc_dirnext(struct _reent *r, char *filename, struct stat *filestat) { - Result rc; - u32 entries; - u16 *name; + Result rc; + u32 entries; + size_t units; /* get pointer to our data */ sdmc_dir_t *dir = (sdmc_dir_t*)(dirState->dirStruct); /* fetch the next entry */ + memset(&dir->entry_data, 0, sizeof(dir->entry_data)); rc = FSDIR_Read(dir->fd, &entries, 1, &dir->entry_data); if(rc == 0) { @@ -798,16 +934,25 @@ sdmc_dirnext(struct _reent *r, else filestat->st_mode = S_IFREG; - /* copy the name */ - name = dir->entry_data.name; - while(*name) - *filename++ = *name++; - *filename = 0; + /* convert name from UTF-16 to UTF-8 */ + memset(filename, 0, NAME_MAX); + units = utf16_to_utf8((uint8_t*)filename, dir->entry_data.name, NAME_MAX); + if(units == (size_t)-1) + { + r->_errno = EILSEQ; + return -1; + } + + if(filename[NAME_MAX-1] != 0) + { + r->_errno = ENAMETOOLONG; + return -1; + } return 0; } - r->_errno = rc; + r->_errno = sdmc_translate_error(rc); return -1; } @@ -833,7 +978,7 @@ sdmc_dirclose(struct _reent *r, if(rc == 0) return 0; - r->_errno = rc; + r->_errno = sdmc_translate_error(rc); return -1; } @@ -882,7 +1027,7 @@ sdmc_statvfs(struct _reent *r, return 0; } - r->_errno = rc; + r->_errno = sdmc_translate_error(rc); return -1; } @@ -917,7 +1062,7 @@ sdmc_ftruncate(struct _reent *r, if(rc == 0) return 0; - r->_errno = rc; + r->_errno = sdmc_translate_error(rc); return -1; } @@ -942,7 +1087,7 @@ sdmc_fsync(struct _reent *r, if(rc == 0) return 0; - r->_errno = rc; + r->_errno = sdmc_translate_error(rc); return -1; } @@ -981,3 +1126,91 @@ sdmc_fchmod(struct _reent *r, r->_errno = ENOSYS; 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; +} diff --git a/libctru/source/util/rbtree_clear.c b/libctru/source/util/rbtree/rbtree_clear.c similarity index 100% rename from libctru/source/util/rbtree_clear.c rename to libctru/source/util/rbtree/rbtree_clear.c diff --git a/libctru/source/util/rbtree_empty.c b/libctru/source/util/rbtree/rbtree_empty.c similarity index 100% rename from libctru/source/util/rbtree_empty.c rename to libctru/source/util/rbtree/rbtree_empty.c diff --git a/libctru/source/util/rbtree_find.c b/libctru/source/util/rbtree/rbtree_find.c similarity index 100% rename from libctru/source/util/rbtree_find.c rename to libctru/source/util/rbtree/rbtree_find.c diff --git a/libctru/source/util/rbtree_init.c b/libctru/source/util/rbtree/rbtree_init.c similarity index 100% rename from libctru/source/util/rbtree_init.c rename to libctru/source/util/rbtree/rbtree_init.c diff --git a/libctru/source/util/rbtree_insert.c b/libctru/source/util/rbtree/rbtree_insert.c similarity index 100% rename from libctru/source/util/rbtree_insert.c rename to libctru/source/util/rbtree/rbtree_insert.c diff --git a/libctru/source/util/rbtree_internal.h b/libctru/source/util/rbtree/rbtree_internal.h similarity index 100% rename from libctru/source/util/rbtree_internal.h rename to libctru/source/util/rbtree/rbtree_internal.h diff --git a/libctru/source/util/rbtree_iterator.c b/libctru/source/util/rbtree/rbtree_iterator.c similarity index 100% rename from libctru/source/util/rbtree_iterator.c rename to libctru/source/util/rbtree/rbtree_iterator.c diff --git a/libctru/source/util/rbtree_minmax.c b/libctru/source/util/rbtree/rbtree_minmax.c similarity index 100% rename from libctru/source/util/rbtree_minmax.c rename to libctru/source/util/rbtree/rbtree_minmax.c diff --git a/libctru/source/util/rbtree_remove.c b/libctru/source/util/rbtree/rbtree_remove.c similarity index 100% rename from libctru/source/util/rbtree_remove.c rename to libctru/source/util/rbtree/rbtree_remove.c diff --git a/libctru/source/util/rbtree_rotate.c b/libctru/source/util/rbtree/rbtree_rotate.c similarity index 100% rename from libctru/source/util/rbtree_rotate.c rename to libctru/source/util/rbtree/rbtree_rotate.c diff --git a/libctru/source/util/rbtree_size.c b/libctru/source/util/rbtree/rbtree_size.c similarity index 100% rename from libctru/source/util/rbtree_size.c rename to libctru/source/util/rbtree/rbtree_size.c diff --git a/libctru/source/util/utf/decode_utf16.c b/libctru/source/util/utf/decode_utf16.c new file mode 100644 index 0000000..dc82588 --- /dev/null +++ b/libctru/source/util/utf/decode_utf16.c @@ -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; +} diff --git a/libctru/source/util/utf/decode_utf8.c b/libctru/source/util/utf/decode_utf8.c new file mode 100644 index 0000000..d2c86d4 --- /dev/null +++ b/libctru/source/util/utf/decode_utf8.c @@ -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; +} diff --git a/libctru/source/util/utf/encode_utf16.c b/libctru/source/util/utf/encode_utf16.c new file mode 100644 index 0000000..e7f2ba1 --- /dev/null +++ b/libctru/source/util/utf/encode_utf16.c @@ -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; +} diff --git a/libctru/source/util/utf/encode_utf8.c b/libctru/source/util/utf/encode_utf8.c new file mode 100644 index 0000000..18d028c --- /dev/null +++ b/libctru/source/util/utf/encode_utf8.c @@ -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; +} diff --git a/libctru/source/util/utf/utf16_to_utf32.c b/libctru/source/util/utf/utf16_to_utf32.c new file mode 100644 index 0000000..9ed21ac --- /dev/null +++ b/libctru/source/util/utf/utf16_to_utf32.c @@ -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; +} diff --git a/libctru/source/util/utf/utf16_to_utf8.c b/libctru/source/util/utf/utf16_to_utf8.c new file mode 100644 index 0000000..3567e63 --- /dev/null +++ b/libctru/source/util/utf/utf16_to_utf8.c @@ -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; +} diff --git a/libctru/source/util/utf/utf32_to_utf16.c b/libctru/source/util/utf/utf32_to_utf16.c new file mode 100644 index 0000000..81ce203 --- /dev/null +++ b/libctru/source/util/utf/utf32_to_utf16.c @@ -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; +} diff --git a/libctru/source/util/utf/utf32_to_utf8.c b/libctru/source/util/utf/utf32_to_utf8.c new file mode 100644 index 0000000..80fc7c5 --- /dev/null +++ b/libctru/source/util/utf/utf32_to_utf8.c @@ -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; +} diff --git a/libctru/source/util/utf/utf8_to_utf16.c b/libctru/source/util/utf/utf8_to_utf16.c new file mode 100644 index 0000000..6991b3f --- /dev/null +++ b/libctru/source/util/utf/utf8_to_utf16.c @@ -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; +} diff --git a/libctru/source/util/utf/utf8_to_utf32.c b/libctru/source/util/utf/utf8_to_utf32.c new file mode 100644 index 0000000..fac6acd --- /dev/null +++ b/libctru/source/util/utf/utf8_to_utf32.c @@ -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; +} From 45732121765ca0cf1feb53ed2bc009dd7c775452 Mon Sep 17 00:00:00 2001 From: mtheall Date: Tue, 27 Jan 2015 23:28:06 -0600 Subject: [PATCH 2/2] Fix handle leak in sdmc_stat. --- libctru/source/sdmc_dev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libctru/source/sdmc_dev.c b/libctru/source/sdmc_dev.c index 3d8b4c0..36d75fa 100644 --- a/libctru/source/sdmc_dev.c +++ b/libctru/source/sdmc_dev.c @@ -681,6 +681,7 @@ sdmc_stat(struct _reent *r, memset(st, 0, sizeof(struct stat)); st->st_nlink = 1; st->st_mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO; + FSDIR_Close(fd); return 0; }