Add UTF-8 <-> UTF-16 support.
This commit is contained in:
parent
cecae02b1d
commit
b7ec5341b9
@ -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
|
||||||
@ -38,7 +39,7 @@ INCLUDES := include
|
|||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# options for code generation
|
# 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 \
|
CFLAGS := -g -Wall -O2 -mword-relocations \
|
||||||
-fomit-frame-pointer -ffast-math \
|
-fomit-frame-pointer -ffast-math \
|
||||||
|
108
libctru/include/3ds/util/utf.h
Normal file
108
libctru/include/3ds/util/utf.h
Normal 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);
|
@ -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,48 +659,32 @@ 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;
|
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;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
r->_errno = EBADF;
|
r->_errno = sdmc_translate_error(rc);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -602,7 +718,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -618,32 +745,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
|
||||||
@ -660,7 +779,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -679,23 +821,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -713,23 +851,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;
|
||||||
@ -737,7 +872,7 @@ sdmc_diropen(struct _reent *r,
|
|||||||
return dirState;
|
return dirState;
|
||||||
}
|
}
|
||||||
|
|
||||||
r->_errno = rc;
|
r->_errno = sdmc_translate_error(rc);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -773,14 +908,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)
|
||||||
{
|
{
|
||||||
@ -798,16 +934,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -833,7 +978,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -882,7 +1027,7 @@ sdmc_statvfs(struct _reent *r,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
r->_errno = rc;
|
r->_errno = sdmc_translate_error(rc);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -917,7 +1062,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -942,7 +1087,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -981,3 +1126,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;
|
||||||
|
}
|
||||||
|
25
libctru/source/util/utf/decode_utf16.c
Normal file
25
libctru/source/util/utf/decode_utf16.c
Normal 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;
|
||||||
|
}
|
88
libctru/source/util/utf/decode_utf8.c
Normal file
88
libctru/source/util/utf/decode_utf8.c
Normal 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;
|
||||||
|
}
|
24
libctru/source/util/utf/encode_utf16.c
Normal file
24
libctru/source/util/utf/encode_utf16.c
Normal 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;
|
||||||
|
}
|
45
libctru/source/util/utf/encode_utf8.c
Normal file
45
libctru/source/util/utf/encode_utf8.c
Normal 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;
|
||||||
|
}
|
35
libctru/source/util/utf/utf16_to_utf32.c
Normal file
35
libctru/source/util/utf/utf16_to_utf32.c
Normal 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;
|
||||||
|
}
|
48
libctru/source/util/utf/utf16_to_utf8.c
Normal file
48
libctru/source/util/utf/utf16_to_utf8.c
Normal 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;
|
||||||
|
}
|
34
libctru/source/util/utf/utf32_to_utf16.c
Normal file
34
libctru/source/util/utf/utf32_to_utf16.c
Normal 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;
|
||||||
|
}
|
38
libctru/source/util/utf/utf32_to_utf8.c
Normal file
38
libctru/source/util/utf/utf32_to_utf8.c
Normal 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;
|
||||||
|
}
|
44
libctru/source/util/utf/utf8_to_utf16.c
Normal file
44
libctru/source/util/utf/utf8_to_utf16.c
Normal 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;
|
||||||
|
}
|
35
libctru/source/util/utf/utf8_to_utf32.c
Normal file
35
libctru/source/util/utf/utf8_to_utf32.c
Normal 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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user