Add UTF-8 <-> UTF-16 support.
This commit is contained in:
parent
cecae02b1d
commit
b7ec5341b9
@ -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 \
|
||||
|
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 <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/dirent.h>
|
||||
#include <sys/iosupport.h>
|
||||
#include <sys/param.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string.h>
|
||||
#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 */
|
||||
@ -105,26 +111,104 @@ static FS_archive sdmcArchive =
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (strchr (path, ':') != NULL) return NULL;
|
||||
p += units;
|
||||
} while(code != ':' && code != 0);
|
||||
|
||||
// We found a colon; p points to the actual path
|
||||
if(code == ':')
|
||||
path = (const char*)p;
|
||||
|
||||
if (path[0]=='/') return path;
|
||||
// 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;
|
||||
}
|
||||
|
||||
strncpy(__fixedpath,__cwd,PATH_MAX);
|
||||
strncat(__fixedpath,path,PATH_MAX);
|
||||
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(__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,27 +219,53 @@ static bool sdmcInitialised = false;
|
||||
/*! Initialize SDMC device */
|
||||
Result sdmcInit(void)
|
||||
{
|
||||
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) {
|
||||
setDefaultDevice(dev);
|
||||
if (__system_argc != 0 && __system_argv[0] != NULL)
|
||||
if(dev != -1)
|
||||
{
|
||||
if (FindDevice(__system_argv[0]) == dev)
|
||||
setDefaultDevice(dev);
|
||||
if(__system_argc != 0 && __system_argv[0] != NULL)
|
||||
{
|
||||
if(FindDevice(__system_argv[0]) == dev)
|
||||
{
|
||||
strncpy(__fixedpath,__system_argv[0],PATH_MAX);
|
||||
char *last_slash = strrchr(__fixedpath,'/');
|
||||
if (last_slash != NULL) {
|
||||
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);
|
||||
}
|
||||
@ -163,6 +273,7 @@ Result sdmcInit(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sdmcInitialised = true;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
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.
|
||||
/* Copy to internal buffer and write in chunks.
|
||||
* You cannot write from read-only memory.
|
||||
*/
|
||||
static char tmp_buffer[8192];
|
||||
while(len > 0)
|
||||
{
|
||||
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, offset, (u32*)ptr, (u32)len, sync);
|
||||
if(rc == 0)
|
||||
rc = FSFILE_Write(file->fd, &bytes, file->offset,
|
||||
(u32*)tmp_buffer, (u32)toWrite, sync);
|
||||
if(rc != 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;
|
||||
/* return partial transfer */
|
||||
if(bytesWritten > 0)
|
||||
return bytesWritten;
|
||||
|
||||
r->_errno = sdmc_translate_error(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
r->_errno = rc;
|
||||
return -1;
|
||||
file->offset += bytes;
|
||||
bytesWritten += bytes;
|
||||
ptr += bytes;
|
||||
len -= bytes;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -529,46 +661,30 @@ sdmc_stat(struct _reent *r,
|
||||
{
|
||||
Handle fd;
|
||||
Result rc;
|
||||
const char *pathptr = NULL;
|
||||
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;
|
||||
}
|
||||
}
|
||||
if( (rc = FSUSER_OpenDirectory(NULL, &fd, sdmcArchive, FS_makePath(PATH_CHAR, pathptr))) == 0 )
|
||||
else if((rc = FSUSER_OpenDirectory(NULL, &fd, sdmcArchive, fs_path)) == 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;
|
||||
st->st_mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -620,30 +747,22 @@ sdmc_chdir(struct _reent *r,
|
||||
{
|
||||
Handle fd;
|
||||
Result rc;
|
||||
const char *pathptr = NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -715,21 +853,18 @@ sdmc_diropen(struct _reent *r,
|
||||
{
|
||||
Handle fd;
|
||||
Result rc;
|
||||
const char *pathptr = NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -775,12 +910,13 @@ sdmc_dirnext(struct _reent *r,
|
||||
{
|
||||
Result rc;
|
||||
u32 entries;
|
||||
u16 *name;
|
||||
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;
|
||||
}
|
||||
|
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