From 422ea3bb8430e71436344d7ad3cb61305da1f2dd Mon Sep 17 00:00:00 2001 From: jdgleaver Date: Mon, 4 Oct 2021 13:42:38 +0100 Subject: [PATCH] Replace direct file access with VFS routines + clean-ups --- Makefile.common | 106 +++--- .../libretro-common/compat/compat_snprintf.c | 28 +- .../libretro-common/compat/fopen_utf8.c | 11 +- .../libretro-common/encodings/encoding_utf.c | 87 ++--- libgambatte/libretro-common/file/file_path.c | 277 +++++++++----- .../libretro-common/file/file_path_io.c | 151 ++++++++ .../libretro-common/include/compat/msvc.h | 19 +- .../libretro-common/include/encodings/utf.h | 2 +- .../libretro-common/include/file/file_path.h | 7 + libgambatte/libretro-common/include/memmap.h | 52 --- .../libretro-common/include/retro_assert.h | 4 +- .../include/retro_miscellaneous.h | 37 +- .../include/streams/file_stream.h | 2 + .../include/streams/file_stream_transforms.h | 101 +++++ .../include/string/stdstring.h | 124 ++++-- libgambatte/libretro-common/include/vfs/vfs.h | 24 +- .../libretro-common/streams/file_stream.c | 131 ++++--- .../streams/file_stream_transforms.c | 159 ++++++++ .../libretro-common/string/stdstring.c | 280 ++++++++++---- libgambatte/libretro-common/time/rtime.c | 1 + .../libretro-common/vfs/vfs_implementation.c | 125 +++++- libgambatte/libretro/blipper.c | 9 +- libgambatte/libretro/gambatte_log.c | 32 ++ libgambatte/libretro/gambatte_log.h | 17 + libgambatte/libretro/libretro.cpp | 355 +++++++++++------- libgambatte/libretro/net_serial.cpp | 37 +- libgambatte/src/mem/cartridge.cpp | 75 ++-- libgambatte/src/mem/cartridge_libretro.cpp | 2 - libgambatte/src/mem/huc3.cpp | 3 +- 29 files changed, 1611 insertions(+), 647 deletions(-) create mode 100644 libgambatte/libretro-common/file/file_path_io.c delete mode 100644 libgambatte/libretro-common/include/memmap.h create mode 100644 libgambatte/libretro-common/include/streams/file_stream_transforms.h create mode 100644 libgambatte/libretro-common/streams/file_stream_transforms.c create mode 100644 libgambatte/libretro/gambatte_log.c create mode 100644 libgambatte/libretro/gambatte_log.h diff --git a/Makefile.common b/Makefile.common index 54f9664..ddc46ec 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1,58 +1,70 @@ LIBRETRO_COMM_DIR := $(CORE_DIR)/../libretro-common -INCFLAGS := -I$(CORE_DIR) -I$(CORE_DIR)/../include -I$(CORE_DIR)/../../common -I$(CORE_DIR)/../../common/resample -I$(CORE_DIR)/../libretro -I$(LIBRETRO_COMM_DIR)/include +INCFLAGS := \ + -I$(CORE_DIR) \ + -I$(CORE_DIR)/../include \ + -I$(CORE_DIR)/../../common \ + -I$(CORE_DIR)/../../common/resample \ + -I$(CORE_DIR)/../libretro \ + -I$(LIBRETRO_COMM_DIR)/include ifneq (,$(findstring msvc2003,$(platform))) INCFLAGS += -I$(LIBRETRO_COMM_DIR)/include/compat/msvc endif -SOURCES_C := $(CORE_DIR)/../libretro/blipper.c -SOURCES_CXX := $(CORE_DIR)/bootloader.cpp \ - $(CORE_DIR)/cpu.cpp \ - $(CORE_DIR)/gambatte.cpp \ - $(CORE_DIR)/initstate.cpp \ - $(CORE_DIR)/interrupter.cpp \ - $(CORE_DIR)/interruptrequester.cpp \ - $(CORE_DIR)/gambatte-memory.cpp \ - $(CORE_DIR)/sound.cpp \ - $(CORE_DIR)/statesaver.cpp \ - $(CORE_DIR)/tima.cpp \ - $(CORE_DIR)/video.cpp \ - $(CORE_DIR)/video_libretro.cpp \ - $(CORE_DIR)/mem/cartridge.cpp \ - $(CORE_DIR)/mem/cartridge_libretro.cpp \ - $(CORE_DIR)/mem/huc3.cpp \ - $(CORE_DIR)/mem/memptrs.cpp \ - $(CORE_DIR)/mem/rtc.cpp \ - $(CORE_DIR)/sound/channel1.cpp \ - $(CORE_DIR)/sound/channel2.cpp \ - $(CORE_DIR)/sound/channel3.cpp \ - $(CORE_DIR)/sound/channel4.cpp \ - $(CORE_DIR)/sound/duty_unit.cpp \ - $(CORE_DIR)/sound/envelope_unit.cpp \ - $(CORE_DIR)/sound/length_counter.cpp \ - $(CORE_DIR)/video/ly_counter.cpp \ - $(CORE_DIR)/video/lyc_irq.cpp \ - $(CORE_DIR)/video/next_m0_time.cpp \ - $(CORE_DIR)/video/ppu.cpp \ - $(CORE_DIR)/video/sprite_mapper.cpp \ - $(CORE_DIR)/../libretro/libretro.cpp +SOURCES_C := \ + $(CORE_DIR)/../libretro/gambatte_log.c \ + $(CORE_DIR)/../libretro/blipper.c + +SOURCES_CXX := \ + $(CORE_DIR)/bootloader.cpp \ + $(CORE_DIR)/cpu.cpp \ + $(CORE_DIR)/gambatte.cpp \ + $(CORE_DIR)/initstate.cpp \ + $(CORE_DIR)/interrupter.cpp \ + $(CORE_DIR)/interruptrequester.cpp \ + $(CORE_DIR)/gambatte-memory.cpp \ + $(CORE_DIR)/sound.cpp \ + $(CORE_DIR)/statesaver.cpp \ + $(CORE_DIR)/tima.cpp \ + $(CORE_DIR)/video.cpp \ + $(CORE_DIR)/video_libretro.cpp \ + $(CORE_DIR)/mem/cartridge.cpp \ + $(CORE_DIR)/mem/cartridge_libretro.cpp \ + $(CORE_DIR)/mem/huc3.cpp \ + $(CORE_DIR)/mem/memptrs.cpp \ + $(CORE_DIR)/mem/rtc.cpp \ + $(CORE_DIR)/sound/channel1.cpp \ + $(CORE_DIR)/sound/channel2.cpp \ + $(CORE_DIR)/sound/channel3.cpp \ + $(CORE_DIR)/sound/channel4.cpp \ + $(CORE_DIR)/sound/duty_unit.cpp \ + $(CORE_DIR)/sound/envelope_unit.cpp \ + $(CORE_DIR)/sound/length_counter.cpp \ + $(CORE_DIR)/video/ly_counter.cpp \ + $(CORE_DIR)/video/lyc_irq.cpp \ + $(CORE_DIR)/video/next_m0_time.cpp \ + $(CORE_DIR)/video/ppu.cpp \ + $(CORE_DIR)/video/sprite_mapper.cpp \ + $(CORE_DIR)/../libretro/libretro.cpp ifeq ($(HAVE_NETWORK),1) -SOURCES_CXX += $(CORE_DIR)/../libretro/net_serial.cpp + SOURCES_CXX += \ + $(CORE_DIR)/../libretro/net_serial.cpp endif -ifeq ($(STATIC_LINKING),1) -else -SOURCES_C += $(LIBRETRO_COMM_DIR)/streams/file_stream.c \ - $(LIBRETRO_COMM_DIR)/vfs/vfs_implementation.c \ - $(LIBRETRO_COMM_DIR)/compat/fopen_utf8.c \ - $(LIBRETRO_COMM_DIR)/compat/compat_strl.c \ - $(LIBRETRO_COMM_DIR)/compat/compat_snprintf.c \ - $(LIBRETRO_COMM_DIR)/compat/compat_posix_string.c \ - $(LIBRETRO_COMM_DIR)/compat/compat_strcasestr.c \ - $(LIBRETRO_COMM_DIR)/file/file_path.c \ - $(LIBRETRO_COMM_DIR)/time/rtime.c \ - $(LIBRETRO_COMM_DIR)/string/stdstring.c \ - $(LIBRETRO_COMM_DIR)/encodings/encoding_utf.c +ifneq ($(STATIC_LINKING), 1) + SOURCES_C += \ + $(LIBRETRO_COMM_DIR)/compat/compat_posix_string.c \ + $(LIBRETRO_COMM_DIR)/compat/compat_snprintf.c \ + $(LIBRETRO_COMM_DIR)/compat/compat_strcasestr.c \ + $(LIBRETRO_COMM_DIR)/compat/compat_strl.c \ + $(LIBRETRO_COMM_DIR)/compat/fopen_utf8.c \ + $(LIBRETRO_COMM_DIR)/encodings/encoding_utf.c \ + $(LIBRETRO_COMM_DIR)/file/file_path.c \ + $(LIBRETRO_COMM_DIR)/file/file_path_io.c \ + $(LIBRETRO_COMM_DIR)/streams/file_stream.c \ + $(LIBRETRO_COMM_DIR)/streams/file_stream_transforms.c \ + $(LIBRETRO_COMM_DIR)/string/stdstring.c \ + $(LIBRETRO_COMM_DIR)/time/rtime.c \ + $(LIBRETRO_COMM_DIR)/vfs/vfs_implementation.c endif - diff --git a/libgambatte/libretro-common/compat/compat_snprintf.c b/libgambatte/libretro-common/compat/compat_snprintf.c index 5b0a34e..d7320cc 100644 --- a/libgambatte/libretro-common/compat/compat_snprintf.c +++ b/libgambatte/libretro-common/compat/compat_snprintf.c @@ -33,12 +33,12 @@ #if _MSC_VER < 1300 #define _vscprintf c89_vscprintf_retro__ -static int c89_vscprintf_retro__(const char *format, va_list pargs) +static int c89_vscprintf_retro__(const char *fmt, va_list pargs) { int retval; va_list argcopy; va_copy(argcopy, pargs); - retval = vsnprintf(NULL, 0, format, argcopy); + retval = vsnprintf(NULL, 0, fmt, argcopy); va_end(argcopy); return retval; } @@ -46,38 +46,36 @@ static int c89_vscprintf_retro__(const char *format, va_list pargs) /* http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 */ -int c99_vsnprintf_retro__(char *outBuf, size_t size, const char *format, va_list ap) +int c99_vsnprintf_retro__(char *s, size_t len, const char *fmt, va_list ap) { int count = -1; - if (size != 0) + if (len != 0) { #if (_MSC_VER <= 1310) - count = _vsnprintf(outBuf, size - 1, format, ap); + count = _vsnprintf(s, len - 1, fmt, ap); #else - count = _vsnprintf_s(outBuf, size, size - 1, format, ap); + count = _vsnprintf_s(s, len, len - 1, fmt, ap); #endif } if (count == -1) - count = _vscprintf(format, ap); + count = _vscprintf(fmt, ap); - if (count == size) - { - /* there was no room for a NULL, so truncate the last character */ - outBuf[size - 1] = '\0'; - } + /* there was no room for a NULL, so truncate the last character */ + if (count == len && len) + s[len - 1] = '\0'; return count; } -int c99_snprintf_retro__(char *outBuf, size_t size, const char *format, ...) +int c99_snprintf_retro__(char *s, size_t len, const char *fmt, ...) { int count; va_list ap; - va_start(ap, format); - count = c99_vsnprintf_retro__(outBuf, size, format, ap); + va_start(ap, fmt); + count = c99_vsnprintf_retro__(s, len, fmt, ap); va_end(ap); return count; diff --git a/libgambatte/libretro-common/compat/fopen_utf8.c b/libgambatte/libretro-common/compat/fopen_utf8.c index ea16961..85abb59 100644 --- a/libgambatte/libretro-common/compat/fopen_utf8.c +++ b/libgambatte/libretro-common/compat/fopen_utf8.c @@ -49,9 +49,14 @@ void *fopen_utf8(const char * filename, const char * mode) #else wchar_t * filename_w = utf8_to_utf16_string_alloc(filename); wchar_t * mode_w = utf8_to_utf16_string_alloc(mode); - FILE* ret = _wfopen(filename_w, mode_w); - free(filename_w); - free(mode_w); + FILE* ret = NULL; + + if (filename_w && mode_w) + ret = _wfopen(filename_w, mode_w); + if (filename_w) + free(filename_w); + if (mode_w) + free(mode_w); return ret; #endif } diff --git a/libgambatte/libretro-common/encodings/encoding_utf.c b/libgambatte/libretro-common/encodings/encoding_utf.c index 190264a..2760824 100644 --- a/libgambatte/libretro-common/encodings/encoding_utf.c +++ b/libgambatte/libretro-common/encodings/encoding_utf.c @@ -284,9 +284,7 @@ bool utf16_to_char_string(const uint16_t *in, char *s, size_t len) static char *mb_to_mb_string_alloc(const char *str, enum CodePage cp_in, enum CodePage cp_out) { - char *path_buf = NULL; wchar_t *path_buf_wide = NULL; - int path_buf_len = 0; int path_buf_wide_len = MultiByteToWideChar(cp_in, 0, str, -1, NULL, 0); /* Windows 95 will return 0 from these functions with @@ -299,54 +297,51 @@ static char *mb_to_mb_string_alloc(const char *str, * MultiByteToWideChar also supports CP_UTF7 and CP_UTF8. */ - if (path_buf_wide_len) - { - path_buf_wide = (wchar_t*) - calloc(path_buf_wide_len + sizeof(wchar_t), sizeof(wchar_t)); - - if (path_buf_wide) - { - MultiByteToWideChar(cp_in, 0, - str, -1, path_buf_wide, path_buf_wide_len); - - if (*path_buf_wide) - { - path_buf_len = WideCharToMultiByte(cp_out, 0, - path_buf_wide, -1, NULL, 0, NULL, NULL); - - if (path_buf_len) - { - path_buf = (char*) - calloc(path_buf_len + sizeof(char), sizeof(char)); - - if (path_buf) - { - WideCharToMultiByte(cp_out, 0, - path_buf_wide, -1, path_buf, - path_buf_len, NULL, NULL); - - free(path_buf_wide); - - if (*path_buf) - return path_buf; - - free(path_buf); - return NULL; - } - } - else - { - free(path_buf_wide); - return strdup(str); - } - } - } - } - else + if (!path_buf_wide_len) return strdup(str); + path_buf_wide = (wchar_t*) + calloc(path_buf_wide_len + sizeof(wchar_t), sizeof(wchar_t)); + if (path_buf_wide) + { + MultiByteToWideChar(cp_in, 0, + str, -1, path_buf_wide, path_buf_wide_len); + + if (*path_buf_wide) + { + int path_buf_len = WideCharToMultiByte(cp_out, 0, + path_buf_wide, -1, NULL, 0, NULL, NULL); + + if (path_buf_len) + { + char *path_buf = (char*) + calloc(path_buf_len + sizeof(char), sizeof(char)); + + if (path_buf) + { + WideCharToMultiByte(cp_out, 0, + path_buf_wide, -1, path_buf, + path_buf_len, NULL, NULL); + + free(path_buf_wide); + + if (*path_buf) + return path_buf; + + free(path_buf); + return NULL; + } + } + else + { + free(path_buf_wide); + return strdup(str); + } + } + free(path_buf_wide); + } return NULL; } diff --git a/libgambatte/libretro-common/file/file_path.c b/libgambatte/libretro-common/file/file_path.c index dd5bffd..fc11270 100644 --- a/libgambatte/libretro-common/file/file_path.c +++ b/libgambatte/libretro-common/file/file_path.c @@ -48,43 +48,9 @@ #include #include -#if defined(_WIN32) -#ifdef _MSC_VER -#define setmode _setmode -#endif -#include -#ifdef _XBOX -#include -#define INVALID_FILE_ATTRIBUTES -1 -#else -#include -#include +#ifdef _WIN32 #include -#include -#if defined(_MSC_VER) && _MSC_VER <= 1200 -#define INVALID_FILE_ATTRIBUTES ((DWORD)-1) -#endif -#endif -#elif defined(VITA) -#define SCE_ERROR_ERRNO_EEXIST 0x80010011 -#include -#include -#include #else -#include -#include -#include -#endif - -#if defined(PSP) -#include -#endif - -#if defined(VITA) -#define FIO_S_ISDIR SCE_S_ISDIR -#endif - -#if defined(__QNX__) || defined(PSP) #include /* stat() is defined here */ #endif @@ -121,41 +87,53 @@ const char *path_get_archive_delim(const char *path) buf[0] = '\0'; + /* We search for delimiters after the last slash + * in the file path to avoid capturing delimiter + * characters in any parent directory names. + * If there are no slashes in the file name, then + * the path is just the file basename - in this + * case we search the path in its entirety */ if (!last_slash) - return NULL; + last_slash = path; - /* Find delimiter position */ - delim = strrchr(last_slash, '#'); + /* Find delimiter position + * > Since filenames may contain '#' characters, + * must loop until we find the first '#' that + * is directly *after* a compression extension */ + delim = strchr(last_slash, '#'); - if (!delim) - return NULL; - - /* Check whether this is a known archive type - * > Note: The code duplication here is - * deliberate, to maximise performance */ - if (delim - last_slash > 4) + while (delim) { - strlcpy(buf, delim - 4, sizeof(buf)); - buf[4] = '\0'; + /* Check whether this is a known archive type + * > Note: The code duplication here is + * deliberate, to maximise performance */ + if (delim - last_slash > 4) + { + strlcpy(buf, delim - 4, sizeof(buf)); + buf[4] = '\0'; - string_to_lower(buf); + string_to_lower(buf); - /* Check if this is a '.zip', '.apk' or '.7z' file */ - if (string_is_equal(buf, ".zip") || - string_is_equal(buf, ".apk") || - string_is_equal(buf + 1, ".7z")) - return delim; - } - else if (delim - last_slash > 3) - { - strlcpy(buf, delim - 3, sizeof(buf)); - buf[3] = '\0'; + /* Check if this is a '.zip', '.apk' or '.7z' file */ + if (string_is_equal(buf, ".zip") || + string_is_equal(buf, ".apk") || + string_is_equal(buf + 1, ".7z")) + return delim; + } + else if (delim - last_slash > 3) + { + strlcpy(buf, delim - 3, sizeof(buf)); + buf[3] = '\0'; - string_to_lower(buf); + string_to_lower(buf); - /* Check if this is a '.7z' file */ - if (string_is_equal(buf, ".7z")) - return delim; + /* Check if this is a '.7z' file */ + if (string_is_equal(buf, ".7z")) + return delim; + } + + delim++; + delim = strchr(delim, '#'); } return NULL; @@ -214,15 +192,11 @@ char *path_remove_extension(char *path) bool path_is_compressed_file(const char* path) { const char *ext = path_get_extension(path); - - if (string_is_empty(ext)) - return false; - - if (string_is_equal_noncase(ext, "zip") || - string_is_equal_noncase(ext, "apk") || - string_is_equal_noncase(ext, "7z")) - return true; - + if (!string_is_empty(ext)) + if ( string_is_equal_noncase(ext, "zip") || + string_is_equal_noncase(ext, "apk") || + string_is_equal_noncase(ext, "7z")) + return true; return false; } @@ -547,7 +521,7 @@ void path_basedir(char *path) if (last) last[1] = '\0'; else - snprintf(path, 3, "." PATH_DEFAULT_SLASH()); + strlcpy(path, "." PATH_DEFAULT_SLASH(), 3); } /** @@ -612,6 +586,16 @@ const char *path_basename(const char *path) return path; } +/* Specialized version */ +const char *path_basename_nocompression(const char *path) +{ + /* We cut at the last slash */ + const char *last = find_last_slash(path); + if (last) + return last + 1; + return path; +} + /** * path_is_absolute: * @path : path @@ -666,16 +650,31 @@ bool path_is_absolute(const char *path) char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks) { #if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL) - char tmp[PATH_MAX_LENGTH]; #ifdef _WIN32 - strlcpy(tmp, buf, sizeof(tmp)); - if (!_fullpath(buf, tmp, size)) + char *ret = NULL; + wchar_t abs_path[PATH_MAX_LENGTH]; + wchar_t *rel_path = utf8_to_utf16_string_alloc(buf); + + if (rel_path) { - strlcpy(buf, tmp, size); - return NULL; + if (_wfullpath(abs_path, rel_path, PATH_MAX_LENGTH)) + { + char *tmp = utf16_to_utf8_string_alloc(abs_path); + + if (tmp) + { + strlcpy(buf, tmp, size); + free(tmp); + ret = buf; + } + } + + free(rel_path); } - return buf; + + return ret; #else + char tmp[PATH_MAX_LENGTH]; size_t t; char *p; const char *next; @@ -1006,7 +1005,7 @@ void fill_pathname_expand_special(char *out_path, retro_assert(src_size < size); out_path += src_size; - size -= src_size; + size -= src_size; } in_path += 2; @@ -1096,8 +1095,7 @@ void fill_pathname_abbreviate_special(char *out_path, if (!PATH_CHAR_IS_SLASH(*in_path)) { - retro_assert(strlcpy(out_path, - PATH_DEFAULT_SLASH(), size) < size); + strcpy_literal(out_path, PATH_DEFAULT_SLASH()); out_path++; size--; } @@ -1111,6 +1109,99 @@ void fill_pathname_abbreviate_special(char *out_path, retro_assert(strlcpy(out_path, in_path, size) < size); } +/* Changes the slashes to the correct kind for the os + * So forward slash on linux and backslash on Windows */ +void pathname_conform_slashes_to_os(char *path) +{ + /* Conform slashes to os standard so we get proper matching */ + char* p; + for (p = path; *p; p++) + if (*p == '/' || *p == '\\') + *p = PATH_DEFAULT_SLASH_C(); +} + +/* Change all shashes to forward so they are more portable between windows and linux */ +void pathname_make_slashes_portable(char *path) +{ + /* Conform slashes to os standard so we get proper matching */ + char* p; + for (p = path; *p; p++) + if (*p == '/' || *p == '\\') + *p = '/'; +} + +/* Get the number of slashes in a path, returns an integer */ +int get_pathname_num_slashes(const char *in_path) +{ + int num_slashes = 0; + int i = 0; + + for (i = 0; i < PATH_MAX_LENGTH; i++) + { + if (PATH_CHAR_IS_SLASH(in_path[i])) + num_slashes++; + if (in_path[i] == '\0') + break; + } + + return num_slashes; +} + +/* Fills the supplied path with either the abbreviated path or the relative path, which ever + * one is has less depth / number of slashes + * If lengths of abbreviated and relative paths are the same the relative path will be used + * in_path can be an absolute, relative or abbreviated path */ +void fill_pathname_abbreviated_or_relative(char *out_path, const char *in_refpath, const char *in_path, size_t size) +{ + char in_path_conformed[PATH_MAX_LENGTH]; + char in_refpath_conformed[PATH_MAX_LENGTH]; + char expanded_path[PATH_MAX_LENGTH]; + char absolute_path[PATH_MAX_LENGTH]; + char relative_path[PATH_MAX_LENGTH]; + char abbreviated_path[PATH_MAX_LENGTH]; + + in_path_conformed[0] = '\0'; + in_refpath_conformed[0] = '\0'; + expanded_path[0] = '\0'; + absolute_path[0] = '\0'; + relative_path[0] = '\0'; + abbreviated_path[0] = '\0'; + + strcpy_literal(in_path_conformed, in_path); + strcpy_literal(in_refpath_conformed, in_refpath); + + pathname_conform_slashes_to_os(in_path_conformed); + pathname_conform_slashes_to_os(in_refpath_conformed); + + /* Expand paths which start with :\ to an absolute path */ + fill_pathname_expand_special(expanded_path, + in_path_conformed, sizeof(expanded_path)); + + /* Get the absolute path if it is not already */ + if (path_is_absolute(expanded_path)) + strlcpy(absolute_path, expanded_path, PATH_MAX_LENGTH); + else + fill_pathname_resolve_relative(absolute_path, + in_refpath_conformed, in_path_conformed, PATH_MAX_LENGTH); + + pathname_conform_slashes_to_os(absolute_path); + + /* Get the relative path and see how many directories long it is */ + path_relative_to(relative_path, absolute_path, + in_refpath_conformed, sizeof(relative_path)); + + /* Get the abbreviated path and see how many directories long it is */ + fill_pathname_abbreviate_special(abbreviated_path, + absolute_path, sizeof(abbreviated_path)); + + /* Use the shortest path, preferring the relative path*/ + if ( get_pathname_num_slashes(relative_path) <= + get_pathname_num_slashes(abbreviated_path)) + retro_assert(strlcpy(out_path, relative_path, size) < size); + else + retro_assert(strlcpy(out_path, abbreviated_path, size) < size); +} + /** * path_basedir: * @path : path @@ -1136,7 +1227,7 @@ void path_basedir_wrapper(char *path) if (last) last[1] = '\0'; else - snprintf(path, 3, "." PATH_DEFAULT_SLASH()); + strlcpy(path, "." PATH_DEFAULT_SLASH(), 3); } #if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL) @@ -1263,30 +1354,28 @@ void fill_pathname_application_dir(char *s, size_t len) void fill_pathname_home_dir(char *s, size_t len) { #ifdef __WINRT__ - strlcpy(s, uwp_dir_data, len); + const char *home = uwp_dir_data; #else const char *home = getenv("HOME"); +#endif if (home) strlcpy(s, home, len); else *s = 0; -#endif } #endif bool is_path_accessible_using_standard_io(const char *path) { - bool result = true; #ifdef __WINRT__ - size_t path_sizeof = PATH_MAX_LENGTH * sizeof(char); - char *relative_path_abbrev = (char*)malloc(path_sizeof); - fill_pathname_abbreviate_special(relative_path_abbrev, path, path_sizeof); - - result = (strlen(relative_path_abbrev) >= 2 ) - && (relative_path_abbrev[0] == ':' || relative_path_abbrev[0] == '~') + char relative_path_abbrev[PATH_MAX_LENGTH]; + fill_pathname_abbreviate_special(relative_path_abbrev, + path, sizeof(relative_path_abbrev)); + return (strlen(relative_path_abbrev) >= 2 ) + && ( relative_path_abbrev[0] == ':' + || relative_path_abbrev[0] == '~') && PATH_CHAR_IS_SLASH(relative_path_abbrev[1]); - - free(relative_path_abbrev); +#else + return true; #endif - return result; } diff --git a/libgambatte/libretro-common/file/file_path_io.c b/libgambatte/libretro-common/file/file_path_io.c new file mode 100644 index 0000000..63587e3 --- /dev/null +++ b/libgambatte/libretro-common/file/file_path_io.c @@ -0,0 +1,151 @@ +/* Copyright (C) 2010-2020 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (file_path_io.c). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#define VFS_FRONTEND +#include + +#ifdef _WIN32 +#include +#else +#include /* stat() is defined here */ +#endif + +/* TODO/FIXME - globals */ +static retro_vfs_stat_t path_stat_cb = retro_vfs_stat_impl; +static retro_vfs_mkdir_t path_mkdir_cb = retro_vfs_mkdir_impl; + +void path_vfs_init(const struct retro_vfs_interface_info* vfs_info) +{ + const struct retro_vfs_interface* + vfs_iface = vfs_info->iface; + + path_stat_cb = retro_vfs_stat_impl; + path_mkdir_cb = retro_vfs_mkdir_impl; + + if (vfs_info->required_interface_version < PATH_REQUIRED_VFS_VERSION || !vfs_iface) + return; + + path_stat_cb = vfs_iface->stat; + path_mkdir_cb = vfs_iface->mkdir; +} + +int path_stat(const char *path) +{ + return path_stat_cb(path, NULL); +} + +/** + * path_is_directory: + * @path : path + * + * Checks if path is a directory. + * + * Returns: true (1) if path is a directory, otherwise false (0). + */ +bool path_is_directory(const char *path) +{ + return (path_stat_cb(path, NULL) & RETRO_VFS_STAT_IS_DIRECTORY) != 0; +} + +bool path_is_character_special(const char *path) +{ + return (path_stat_cb(path, NULL) & RETRO_VFS_STAT_IS_CHARACTER_SPECIAL) != 0; +} + +bool path_is_valid(const char *path) +{ + return (path_stat_cb(path, NULL) & RETRO_VFS_STAT_IS_VALID) != 0; +} + +int32_t path_get_size(const char *path) +{ + int32_t filesize = 0; + if (path_stat_cb(path, &filesize) != 0) + return filesize; + + return -1; +} + +/** + * path_mkdir: + * @dir : directory + * + * Create directory on filesystem. + * + * Returns: true (1) if directory could be created, otherwise false (0). + **/ +bool path_mkdir(const char *dir) +{ + bool norecurse = false; + char *basedir = NULL; + + if (!(dir && *dir)) + return false; + + /* Use heap. Real chance of stack + * overflow if we recurse too hard. */ + basedir = strdup(dir); + + if (!basedir) + return false; + + path_parent_dir(basedir); + + if (!*basedir || !strcmp(basedir, dir)) + { + free(basedir); + return false; + } + + if ( path_is_directory(basedir) + || path_mkdir(basedir)) + norecurse = true; + + free(basedir); + + if (norecurse) + { + int ret = path_mkdir_cb(dir); + + /* Don't treat this as an error. */ + if (ret == -2 && path_is_directory(dir)) + return true; + else if (ret == 0) + return true; + } + return false; +} diff --git a/libgambatte/libretro-common/include/compat/msvc.h b/libgambatte/libretro-common/include/compat/msvc.h index c71bf06..a4c93a5 100644 --- a/libgambatte/libretro-common/include/compat/msvc.h +++ b/libgambatte/libretro-common/include/compat/msvc.h @@ -29,22 +29,17 @@ extern "C" { #endif -/* Pre-MSVC 2015 compilers don't implement snprintf in a cross-platform manner. */ +/* Pre-MSVC 2015 compilers don't implement snprintf, vsnprintf in a cross-platform manner. */ #if _MSC_VER < 1900 - #include - #include - #ifndef snprintf - #define snprintf c99_snprintf_retro__ - #endif - - int c99_snprintf_retro__(char *outBuf, size_t size, const char *format, ...); -#endif - -/* Pre-MSVC 2008 compilers don't implement vsnprintf in a cross-platform manner? Not sure about this one. */ -#if _MSC_VER < 1500 #include #include #include + + #ifndef snprintf + #define snprintf c99_snprintf_retro__ + #endif + int c99_snprintf_retro__(char *outBuf, size_t size, const char *format, ...); + #ifndef vsnprintf #define vsnprintf c99_vsnprintf_retro__ #endif diff --git a/libgambatte/libretro-common/include/encodings/utf.h b/libgambatte/libretro-common/include/encodings/utf.h index d260cde..bea4e14 100644 --- a/libgambatte/libretro-common/include/encodings/utf.h +++ b/libgambatte/libretro-common/include/encodings/utf.h @@ -35,7 +35,7 @@ RETRO_BEGIN_DECLS enum CodePage { CODEPAGE_LOCAL = 0, /* CP_ACP */ - CODEPAGE_UTF8 = 65001 /* CP_UTF8 */ + CODEPAGE_UTF8 = 65001 /* CP_UTF8 */ }; size_t utf8_conv_utf32(uint32_t *out, size_t out_chars, diff --git a/libgambatte/libretro-common/include/file/file_path.h b/libgambatte/libretro-common/include/file/file_path.h index 6f1918d..452763f 100644 --- a/libgambatte/libretro-common/include/file/file_path.h +++ b/libgambatte/libretro-common/include/file/file_path.h @@ -125,6 +125,7 @@ char *path_remove_extension(char *path); * Returns: basename from path. **/ const char *path_basename(const char *path); +const char *path_basename_nocompression(const char *path); /** * path_basedir: @@ -442,6 +443,12 @@ void fill_pathname_expand_special(char *out_path, void fill_pathname_abbreviate_special(char *out_path, const char *in_path, size_t size); +void fill_pathname_abbreviated_or_relative(char *out_path, const char *in_refpath, const char *in_path, size_t size); + +void pathname_conform_slashes_to_os(char *path); + +void pathname_make_slashes_portable(char *path); + /** * path_basedir: * @path : path diff --git a/libgambatte/libretro-common/include/memmap.h b/libgambatte/libretro-common/include/memmap.h deleted file mode 100644 index ebbc307..0000000 --- a/libgambatte/libretro-common/include/memmap.h +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright (C) 2010-2020 The RetroArch team - * - * --------------------------------------------------------------------------------------- - * The following license statement only applies to this file (memmap.h). - * --------------------------------------------------------------------------------------- - * - * Permission is hereby granted, free of charge, - * to any person obtaining a copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef _LIBRETRO_MEMMAP_H -#define _LIBRETRO_MEMMAP_H - -#include -#include - -#if defined(PSP) || defined(PS2) || defined(GEKKO) || defined(VITA) || defined(_XBOX) || defined(_3DS) || defined(WIIU) || defined(SWITCH) || defined(HAVE_LIBNX) -/* No mman available */ -#elif defined(_WIN32) && !defined(_XBOX) -#include -#include -#include -#else -#define HAVE_MMAN -#include -#endif - -#if !defined(HAVE_MMAN) || defined(_WIN32) -void* mmap(void *addr, size_t len, int mmap_prot, int mmap_flags, int fildes, size_t off); - -int munmap(void *addr, size_t len); - -int mprotect(void *addr, size_t len, int prot); -#endif - -int memsync(void *start, void *end); - -int memprotect(void *addr, size_t len); - -#endif diff --git a/libgambatte/libretro-common/include/retro_assert.h b/libgambatte/libretro-common/include/retro_assert.h index 090e9f1..79dbb32 100644 --- a/libgambatte/libretro-common/include/retro_assert.h +++ b/libgambatte/libretro-common/include/retro_assert.h @@ -27,9 +27,7 @@ #ifdef RARCH_INTERNAL #include -#define retro_assert(cond) do { \ - if (!(cond)) { printf("Assertion failed at %s:%d.\n", __FILE__, __LINE__); abort(); } \ -} while(0) +#define retro_assert(cond) ((void)( (cond) || (printf("Assertion failed at %s:%d.\n", __FILE__, __LINE__), abort(), 0) )) #else #define retro_assert(cond) assert(cond) #endif diff --git a/libgambatte/libretro-common/include/retro_miscellaneous.h b/libgambatte/libretro-common/include/retro_miscellaneous.h index 31213af..f0dfb5c 100644 --- a/libgambatte/libretro-common/include/retro_miscellaneous.h +++ b/libgambatte/libretro-common/include/retro_miscellaneous.h @@ -30,13 +30,17 @@ #include #include -#if defined(_WIN32) && !defined(_XBOX) +#if defined(_WIN32) + +#if defined(_XBOX) +#include +#else #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include -#elif defined(_WIN32) && defined(_XBOX) -#include +#endif + #endif #include @@ -71,7 +75,7 @@ static INLINE bool bits_any_set(uint32_t* ptr, uint32_t count) } #ifndef PATH_MAX_LENGTH -#if defined(_XBOX1) || defined(_3DS) || defined(PSP) || defined(PS2) || defined(GEKKO)|| defined(WIIU) || defined(ORBIS) +#if defined(_XBOX1) || defined(_3DS) || defined(PSP) || defined(PS2) || defined(GEKKO)|| defined(WIIU) || defined(ORBIS) || defined(__PSL1GHT__) || defined(__PS3__) #define PATH_MAX_LENGTH 512 #else #define PATH_MAX_LENGTH 4096 @@ -130,6 +134,16 @@ static INLINE bool bits_any_set(uint32_t* ptr, uint32_t count) #define BIT256_GET_PTR(a, bit) BIT256_GET(*a, bit) #define BIT256_CLEAR_ALL_PTR(a) BIT256_CLEAR_ALL(*a) +#define BIT512_SET(a, bit) BIT256_SET(a, bit) +#define BIT512_CLEAR(a, bit) BIT256_CLEAR(a, bit) +#define BIT512_GET(a, bit) BIT256_GET(a, bit) +#define BIT512_CLEAR_ALL(a) BIT256_CLEAR_ALL(a) + +#define BIT512_SET_PTR(a, bit) BIT512_SET(*a, bit) +#define BIT512_CLEAR_PTR(a, bit) BIT512_CLEAR(*a, bit) +#define BIT512_GET_PTR(a, bit) BIT512_GET(*a, bit) +#define BIT512_CLEAR_ALL_PTR(a) BIT512_CLEAR_ALL(*a) + #define BITS_COPY16_PTR(a,bits) \ { \ BIT128_CLEAR_ALL_PTR(a); \ @@ -142,6 +156,13 @@ static INLINE bool bits_any_set(uint32_t* ptr, uint32_t count) BITS_GET_ELEM_PTR(a, 0) = (bits); \ } +#define BITS_COPY64_PTR(a,bits) \ +{ \ + BIT128_CLEAR_ALL_PTR(a); \ + BITS_GET_ELEM_PTR(a, 0) = (bits); \ + BITS_GET_ELEM_PTR(a, 1) = (bits >> 32); \ +} + /* Helper macros and struct to keep track of many booleans. */ /* This struct has 256 bits. */ typedef struct @@ -149,6 +170,12 @@ typedef struct uint32_t data[8]; } retro_bits_t; +/* This struct has 512 bits. */ +typedef struct +{ + uint32_t data[16]; +} retro_bits_512_t; + #ifdef _WIN32 # ifdef _WIN64 # define PRI_SIZET PRIu64 @@ -159,7 +186,7 @@ typedef struct # define PRI_SIZET "u" # endif # endif -#elif PS2 +#elif defined(PS2) # define PRI_SIZET "u" #else # if (SIZE_MAX == 0xFFFF) diff --git a/libgambatte/libretro-common/include/streams/file_stream.h b/libgambatte/libretro-common/include/streams/file_stream.h index ab198b2..5276f87 100644 --- a/libgambatte/libretro-common/include/streams/file_stream.h +++ b/libgambatte/libretro-common/include/streams/file_stream.h @@ -81,6 +81,8 @@ char* filestream_gets(RFILE *stream, char *s, size_t len); int filestream_getc(RFILE *stream); +int filestream_vscanf(RFILE *stream, const char* format, va_list *args); + int filestream_scanf(RFILE *stream, const char* format, ...); int filestream_eof(RFILE *stream); diff --git a/libgambatte/libretro-common/include/streams/file_stream_transforms.h b/libgambatte/libretro-common/include/streams/file_stream_transforms.h new file mode 100644 index 0000000..327e218 --- /dev/null +++ b/libgambatte/libretro-common/include/streams/file_stream_transforms.h @@ -0,0 +1,101 @@ +/* Copyright (C) 2010-2020 The RetroArch team +* +* --------------------------------------------------------------------------------------- +* The following license statement only applies to this file (file_stream_transforms.h). +* --------------------------------------------------------------------------------------- +* +* Permission is hereby granted, free of charge, +* to any person obtaining a copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation the rights to +* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +* and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __LIBRETRO_SDK_FILE_STREAM_TRANSFORMS_H +#define __LIBRETRO_SDK_FILE_STREAM_TRANSFORMS_H + +#include +#include +#include +#include + +RETRO_BEGIN_DECLS + +#ifndef SKIP_STDIO_REDEFINES + +#define FILE RFILE + +#undef fopen +#undef fclose +#undef ftell +#undef fseek +#undef fread +#undef fgets +#undef fgetc +#undef fwrite +#undef fputc +#undef fflush +#undef fprintf +#undef ferror +#undef feof +#undef fscanf + +#define fopen rfopen +#define fclose rfclose +#define ftell rftell +#define fseek rfseek +#define fread rfread +#define fgets rfgets +#define fgetc rfgetc +#define fwrite rfwrite +#define fputc rfputc +#define fflush rfflush +#define fprintf rfprintf +#define ferror rferror +#define feof rfeof +#define fscanf rfscanf + +#endif + +RFILE* rfopen(const char *path, const char *mode); + +int rfclose(RFILE* stream); + +int64_t rftell(RFILE* stream); + +int64_t rfseek(RFILE* stream, int64_t offset, int origin); + +int64_t rfread(void* buffer, + size_t elem_size, size_t elem_count, RFILE* stream); + +char *rfgets(char *buffer, int maxCount, RFILE* stream); + +int rfgetc(RFILE* stream); + +int64_t rfwrite(void const* buffer, + size_t elem_size, size_t elem_count, RFILE* stream); + +int rfputc(int character, RFILE * stream); + +int64_t rfflush(RFILE * stream); + +int rfprintf(RFILE * stream, const char * format, ...); + +int rferror(RFILE* stream); + +int rfeof(RFILE* stream); + +int rfscanf(RFILE * stream, const char * format, ...); + +RETRO_END_DECLS + +#endif diff --git a/libgambatte/libretro-common/include/string/stdstring.h b/libgambatte/libretro-common/include/string/stdstring.h index 07446bf..e7087d6 100644 --- a/libgambatte/libretro-common/include/string/stdstring.h +++ b/libgambatte/libretro-common/include/string/stdstring.h @@ -35,6 +35,33 @@ RETRO_BEGIN_DECLS +#define STRLEN_CONST(x) ((sizeof((x))-1)) + +#define strcpy_literal(a, b) strcpy(a, b) + +#define string_is_not_equal(a, b) !string_is_equal((a), (b)) + +#define string_is_not_equal_fast(a, b, size) (memcmp(a, b, size) != 0) +#define string_is_equal_fast(a, b, size) (memcmp(a, b, size) == 0) + +#define TOLOWER(c) ((c) | (lr_char_props[(unsigned char)(c)] & 0x20)) +#define TOUPPER(c) ((c) & ~(lr_char_props[(unsigned char)(c)] & 0x20)) + +/* C standard says \f \v are space, but this one disagrees */ +#define ISSPACE(c) (lr_char_props[(unsigned char)(c)] & 0x80) + +#define ISDIGIT(c) (lr_char_props[(unsigned char)(c)] & 0x40) +#define ISALPHA(c) (lr_char_props[(unsigned char)(c)] & 0x20) +#define ISLOWER(c) (lr_char_props[(unsigned char)(c)] & 0x04) +#define ISUPPER(c) (lr_char_props[(unsigned char)(c)] & 0x02) +#define ISALNUM(c) (lr_char_props[(unsigned char)(c)] & 0x60) +#define ISUALPHA(c) (lr_char_props[(unsigned char)(c)] & 0x28) +#define ISUALNUM(c) (lr_char_props[(unsigned char)(c)] & 0x68) +#define IS_XDIGIT(c) (lr_char_props[(unsigned char)(c)] & 0x01) + +/* Deprecated alias, all callers should use string_is_equal_case_insensitive instead */ +#define string_is_equal_noncase string_is_equal_case_insensitive + static INLINE bool string_is_empty(const char *data) { return !data || (*data == '\0'); @@ -70,15 +97,20 @@ static INLINE bool string_ends_with(const char *str, const char *suffix) return string_ends_with_size(str, suffix, strlen(str), strlen(suffix)); } +/* Returns the length of 'str' (c.f. strlen()), but only + * checks the first 'size' characters + * - If 'str' is NULL, returns 0 + * - If 'str' is not NULL and no '\0' character is found + * in the first 'size' characters, returns 'size' */ +static INLINE size_t strlen_size(const char *str, size_t size) +{ + size_t i = 0; + if (str) + while (i < size && str[i]) i++; + return i; +} -#define STRLEN_CONST(x) ((sizeof((x))-1)) - -#define string_is_not_equal(a, b) !string_is_equal((a), (b)) - -#define string_is_not_equal_fast(a, b, size) (memcmp(a, b, size) != 0) -#define string_is_equal_fast(a, b, size) (memcmp(a, b, size) == 0) - static INLINE bool string_is_equal_case_insensitive(const char *a, const char *b) { @@ -98,24 +130,6 @@ static INLINE bool string_is_equal_case_insensitive(const char *a, return (result == 0); } -static INLINE bool string_is_equal_noncase(const char *a, const char *b) -{ - int result = 0; - const unsigned char *p1 = (const unsigned char*)a; - const unsigned char *p2 = (const unsigned char*)b; - - if (!a || !b) - return false; - if (p1 == p2) - return false; - - while ((result = tolower (*p1) - tolower (*p2++)) == 0) - if (*p1++ == '\0') - break; - - return (result == 0); -} - char *string_to_upper(char *s); char *string_to_lower(char *s); @@ -134,9 +148,61 @@ char *string_trim_whitespace_right(char *const s); /* Remove leading and trailing whitespaces */ char *string_trim_whitespace(char *const s); -/* max_lines == 0 means no limit */ -char *word_wrap(char *buffer, const char *string, - int line_width, bool unicode, unsigned max_lines); +/* + * Wraps string specified by 'src' to destination buffer + * specified by 'dst' and 'dst_size'. + * This function assumes that all glyphs in the string + * have an on-screen pixel width similar to that of + * regular Latin characters - i.e. it will not wrap + * correctly any text containing so-called 'wide' Unicode + * characters (e.g. CJK languages, emojis, etc.). + * + * @param dst pointer to destination buffer. + * @param dst_size size of destination buffer. + * @param src pointer to input string. + * @param line_width max number of characters per line. + * @param wideglyph_width not used, but is necessary to keep + * compatibility with word_wrap_wideglyph(). + * @param max_lines max lines of destination string. + * 0 means no limit. + */ +void word_wrap(char *dst, size_t dst_size, const char *src, + int line_width, int wideglyph_width, unsigned max_lines); + +/* + * Wraps string specified by 'src' to destination buffer + * specified by 'dst' and 'dst_size'. + * This function assumes that all glyphs in the string + * are: + * - EITHER 'non-wide' Unicode glyphs, with an on-screen + * pixel width similar to that of regular Latin characters + * - OR 'wide' Unicode glyphs (e.g. CJK languages, emojis, etc.) + * with an on-screen pixel width defined by 'wideglyph_width' + * Note that wrapping may occur in inappropriate locations + * if 'src' string contains 'wide' Unicode characters whose + * on-screen pixel width deviates greatly from the set + * 'wideglyph_width' value. + * + * @param dst pointer to destination buffer. + * @param dst_size size of destination buffer. + * @param src pointer to input string. + * @param line_width max number of characters per line. + * @param wideglyph_width effective width of 'wide' Unicode glyphs. + * the value here is normalised relative to the + * typical on-screen pixel width of a regular + * Latin character: + * - a regular Latin character is defined to + * have an effective width of 100 + * - wideglyph_width = 100 * (wide_character_pixel_width / latin_character_pixel_width) + * - e.g. if 'wide' Unicode characters in 'src' + * have an on-screen pixel width twice that of + * regular Latin characters, wideglyph_width + * would be 200 + * @param max_lines max lines of destination string. + * 0 means no limit. + */ +void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src, + int line_width, int wideglyph_width, unsigned max_lines); /* Splits string into tokens seperated by 'delim' * > Returned token string must be free()'d @@ -177,6 +243,8 @@ char *string_init(const char *src); void string_set(char **string, const char *src); +extern const unsigned char lr_char_props[256]; + RETRO_END_DECLS #endif diff --git a/libgambatte/libretro-common/include/vfs/vfs.h b/libgambatte/libretro-common/include/vfs/vfs.h index 2f42c60..5366f33 100644 --- a/libgambatte/libretro-common/include/vfs/vfs.h +++ b/libgambatte/libretro-common/include/vfs/vfs.h @@ -41,17 +41,17 @@ typedef void* HANDLE; #ifdef HAVE_CDROM typedef struct { + int64_t byte_pos; char *cue_buf; size_t cue_len; - int64_t byte_pos; - char drive; + unsigned cur_lba; + unsigned last_frame_lba; unsigned char cur_min; unsigned char cur_sec; unsigned char cur_frame; unsigned char cur_track; - unsigned cur_lba; - unsigned last_frame_lba; unsigned char last_frame[2352]; + char drive; bool last_frame_valid; } vfs_cdrom_t; #endif @@ -69,22 +69,22 @@ struct retro_vfs_file_handle struct libretro_vfs_implementation_file #endif { - int fd; - unsigned hints; +#ifdef HAVE_CDROM + vfs_cdrom_t cdrom; /* int64_t alignment */ +#endif int64_t size; - char *buf; + uint64_t mappos; + uint64_t mapsize; FILE *fp; #ifdef _WIN32 HANDLE fh; #endif + char *buf; char* orig_path; - uint64_t mappos; - uint64_t mapsize; uint8_t *mapped; + int fd; + unsigned hints; enum vfs_scheme scheme; -#ifdef HAVE_CDROM - vfs_cdrom_t cdrom; -#endif }; #endif diff --git a/libgambatte/libretro-common/streams/file_stream.c b/libgambatte/libretro-common/streams/file_stream.c index d5dc40d..3543dcc 100644 --- a/libgambatte/libretro-common/streams/file_stream.c +++ b/libgambatte/libretro-common/streams/file_stream.c @@ -35,11 +35,19 @@ #include #endif +#include #include #define VFS_FRONTEND #include -static const int64_t vfs_error_return_value = -1; +#define VFS_ERROR_RETURN_VALUE -1 + +struct RFILE +{ + struct retro_vfs_file_handle *hfile; + bool error_flag; + bool eof_flag; +}; static retro_vfs_get_path_t filestream_get_path_cb = NULL; static retro_vfs_open_t filestream_open_cb = NULL; @@ -54,18 +62,12 @@ static retro_vfs_flush_t filestream_flush_cb = NULL; static retro_vfs_remove_t filestream_remove_cb = NULL; static retro_vfs_rename_t filestream_rename_cb = NULL; -struct RFILE -{ - struct retro_vfs_file_handle *hfile; - bool error_flag; - bool eof_flag; -}; - /* VFS Initialization */ void filestream_vfs_init(const struct retro_vfs_interface_info* vfs_info) { - const struct retro_vfs_interface* vfs_iface; + const struct retro_vfs_interface * + vfs_iface = vfs_info->iface; filestream_get_path_cb = NULL; filestream_open_cb = NULL; @@ -80,9 +82,9 @@ void filestream_vfs_init(const struct retro_vfs_interface_info* vfs_info) filestream_remove_cb = NULL; filestream_rename_cb = NULL; - vfs_iface = vfs_info->iface; - - if (vfs_info->required_interface_version < FILESTREAM_REQUIRED_VFS_VERSION + if ( + (vfs_info->required_interface_version < + FILESTREAM_REQUIRED_VFS_VERSION) || !vfs_iface) return; @@ -103,12 +105,13 @@ void filestream_vfs_init(const struct retro_vfs_interface_info* vfs_info) /* Callback wrappers */ bool filestream_exists(const char *path) { - RFILE *dummy = NULL; + RFILE *dummy = NULL; if (!path || !*path) return false; - dummy = filestream_open(path, + dummy = filestream_open( + path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); @@ -130,9 +133,10 @@ int64_t filestream_get_size(RFILE *stream) if (filestream_size_cb) output = filestream_size_cb(stream->hfile); else - output = retro_vfs_file_size_impl((libretro_vfs_implementation_file*)stream->hfile); + output = retro_vfs_file_size_impl( + (libretro_vfs_implementation_file*)stream->hfile); - if (output == vfs_error_return_value) + if (output == VFS_ERROR_RETURN_VALUE) stream->error_flag = true; return output; @@ -145,9 +149,10 @@ int64_t filestream_truncate(RFILE *stream, int64_t length) if (filestream_truncate_cb) output = filestream_truncate_cb(stream->hfile, length); else - output = retro_vfs_file_truncate_impl((libretro_vfs_implementation_file*)stream->hfile, length); + output = retro_vfs_file_truncate_impl( + (libretro_vfs_implementation_file*)stream->hfile, length); - if (output == vfs_error_return_value) + if (output == VFS_ERROR_RETURN_VALUE) stream->error_flag = true; return output; @@ -216,14 +221,14 @@ int filestream_getc(RFILE *stream) return EOF; } -int filestream_scanf(RFILE *stream, const char* format, ...) +int filestream_vscanf(RFILE *stream, const char* format, va_list *args) { char buf[4096]; char subfmt[64]; - va_list args; + va_list args_copy; const char * bufiter = buf; - int64_t startpos = filestream_tell(stream); int ret = 0; + int64_t startpos = filestream_tell(stream); int64_t maxlen = filestream_read(stream, buf, sizeof(buf)-1); if (maxlen <= 0) @@ -231,7 +236,12 @@ int filestream_scanf(RFILE *stream, const char* format, ...) buf[maxlen] = '\0'; - va_start(args, format); + /* Have to copy the input va_list here + * > Calling va_arg() on 'args' directly would + * cause the va_list to have an indeterminate value + * in the function calling filestream_vscanf(), + * leading to unexpected behaviour */ + va_copy(args_copy, *args); while (*format) { @@ -251,7 +261,7 @@ int filestream_scanf(RFILE *stream, const char* format, ...) *subfmtiter++ = *format++; } - while (isdigit(*format)) + while (ISDIGIT((unsigned char)*format)) *subfmtiter++ = *format++; /* width */ /* length */ @@ -284,28 +294,33 @@ int filestream_scanf(RFILE *stream, const char* format, ...) *subfmtiter++ = 'n'; *subfmtiter++ = '\0'; - if (sizeof(void*) != sizeof(long*)) abort(); /* all pointers must have the same size */ + if (sizeof(void*) != sizeof(long*)) + abort(); /* all pointers must have the same size */ + if (asterisk) { int v = sscanf(bufiter, subfmt, &sublen); if (v == EOF) return EOF; - if (v != 0) break; + if (v != 0) + break; } else { - int v = sscanf(bufiter, subfmt, va_arg(args, void*), &sublen); + int v = sscanf(bufiter, subfmt, va_arg(args_copy, void*), &sublen); if (v == EOF) return EOF; - if (v != 1) break; + if (v != 1) + break; } ret++; bufiter += sublen; } - else if (isspace(*format)) + else if (isspace((unsigned char)*format)) { - while (isspace(*bufiter)) bufiter++; + while (isspace((unsigned char)*bufiter)) + bufiter++; format++; } else @@ -317,12 +332,23 @@ int filestream_scanf(RFILE *stream, const char* format, ...) } } - va_end(args); - filestream_seek(stream, startpos+(bufiter-buf), RETRO_VFS_SEEK_POSITION_START); + va_end(args_copy); + filestream_seek(stream, startpos+(bufiter-buf), + RETRO_VFS_SEEK_POSITION_START); return ret; } +int filestream_scanf(RFILE *stream, const char* format, ...) +{ + int result; + va_list vl; + va_start(vl, format); + result = filestream_vscanf(stream, format, &vl); + va_end(vl); + return result; +} + int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position) { int64_t output; @@ -330,11 +356,14 @@ int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position) if (filestream_seek_cb) output = filestream_seek_cb(stream->hfile, offset, seek_position); else - output = retro_vfs_file_seek_impl((libretro_vfs_implementation_file*)stream->hfile, offset, seek_position); + output = retro_vfs_file_seek_impl( + (libretro_vfs_implementation_file*)stream->hfile, + offset, seek_position); - if (output == vfs_error_return_value) + if (output == VFS_ERROR_RETURN_VALUE) stream->error_flag = true; - stream->eof_flag = false; + + stream->eof_flag = false; return output; } @@ -351,9 +380,10 @@ int64_t filestream_tell(RFILE *stream) if (filestream_size_cb) output = filestream_tell_cb(stream->hfile); else - output = retro_vfs_file_tell_impl((libretro_vfs_implementation_file*)stream->hfile); + output = retro_vfs_file_tell_impl( + (libretro_vfs_implementation_file*)stream->hfile); - if (output == vfs_error_return_value) + if (output == VFS_ERROR_RETURN_VALUE) stream->error_flag = true; return output; @@ -365,7 +395,7 @@ void filestream_rewind(RFILE *stream) return; filestream_seek(stream, 0L, RETRO_VFS_SEEK_POSITION_START); stream->error_flag = false; - stream->eof_flag = false; + stream->eof_flag = false; } int64_t filestream_read(RFILE *stream, void *s, int64_t len) @@ -378,10 +408,10 @@ int64_t filestream_read(RFILE *stream, void *s, int64_t len) output = retro_vfs_file_read_impl( (libretro_vfs_implementation_file*)stream->hfile, s, len); - if (output == vfs_error_return_value) + if (output == VFS_ERROR_RETURN_VALUE) stream->error_flag = true; if (output < len) - stream->eof_flag = true; + stream->eof_flag = true; return output; } @@ -393,9 +423,10 @@ int filestream_flush(RFILE *stream) if (filestream_flush_cb) output = filestream_flush_cb(stream->hfile); else - output = retro_vfs_file_flush_impl((libretro_vfs_implementation_file*)stream->hfile); + output = retro_vfs_file_flush_impl( + (libretro_vfs_implementation_file*)stream->hfile); - if (output == vfs_error_return_value) + if (output == VFS_ERROR_RETURN_VALUE) stream->error_flag = true; return output; @@ -422,7 +453,8 @@ const char* filestream_get_path(RFILE *stream) if (filestream_get_path_cb) return filestream_get_path_cb(stream->hfile); - return retro_vfs_file_get_path_impl((libretro_vfs_implementation_file*)stream->hfile); + return retro_vfs_file_get_path_impl( + (libretro_vfs_implementation_file*)stream->hfile); } int64_t filestream_write(RFILE *stream, const void *s, int64_t len) @@ -432,9 +464,10 @@ int64_t filestream_write(RFILE *stream, const void *s, int64_t len) if (filestream_write_cb) output = filestream_write_cb(stream->hfile, s, len); else - output = retro_vfs_file_write_impl((libretro_vfs_implementation_file*)stream->hfile, s, len); + output = retro_vfs_file_write_impl( + (libretro_vfs_implementation_file*)stream->hfile, s, len); - if (output == vfs_error_return_value) + if (output == VFS_ERROR_RETURN_VALUE) stream->error_flag = true; return output; @@ -445,7 +478,9 @@ int filestream_putc(RFILE *stream, int c) char c_char = (char)c; if (!stream) return EOF; - return filestream_write(stream, &c_char, 1)==1 ? (int)(unsigned char)c : EOF; + return filestream_write(stream, &c_char, 1) == 1 + ? (int)(unsigned char)c + : EOF; } int filestream_vprintf(RFILE *stream, const char* format, va_list args) @@ -487,7 +522,8 @@ int filestream_close(RFILE *stream) if (filestream_close_cb) output = filestream_close_cb(fp); else - output = retro_vfs_file_close_impl((libretro_vfs_implementation_file*)fp); + output = retro_vfs_file_close_impl( + (libretro_vfs_implementation_file*)fp); if (output == 0) free(stream); @@ -500,10 +536,11 @@ int filestream_close(RFILE *stream) * @path : path to file. * @buf : buffer to allocate and read the contents of the * file into. Needs to be freed manually. + * @len : optional output integer containing bytes read. * * Read the contents of a file into @buf. * - * Returns: number of items read, -1 on error. + * Returns: non zero on success. */ int64_t filestream_read_file(const char *path, void **buf, int64_t *len) { diff --git a/libgambatte/libretro-common/streams/file_stream_transforms.c b/libgambatte/libretro-common/streams/file_stream_transforms.c new file mode 100644 index 0000000..cc6965f --- /dev/null +++ b/libgambatte/libretro-common/streams/file_stream_transforms.c @@ -0,0 +1,159 @@ +/* Copyright (C) 2010-2020 The RetroArch team +* +* --------------------------------------------------------------------------------------- +* The following license statement only applies to this file (file_stream_transforms.c). +* --------------------------------------------------------------------------------------- +* +* Permission is hereby granted, free of charge, +* to any person obtaining a copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation the rights to +* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +* and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include + +#include +#include + +RFILE* rfopen(const char *path, const char *mode) +{ + RFILE *output = NULL; + unsigned int retro_mode = RETRO_VFS_FILE_ACCESS_READ; + bool position_to_end = false; + + if (strstr(mode, "r")) + { + retro_mode = RETRO_VFS_FILE_ACCESS_READ; + if (strstr(mode, "+")) + { + retro_mode = RETRO_VFS_FILE_ACCESS_READ_WRITE | + RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING; + } + } + else if (strstr(mode, "w")) + { + retro_mode = RETRO_VFS_FILE_ACCESS_WRITE; + if (strstr(mode, "+")) + retro_mode = RETRO_VFS_FILE_ACCESS_READ_WRITE; + } + else if (strstr(mode, "a")) + { + retro_mode = RETRO_VFS_FILE_ACCESS_WRITE | + RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING; + position_to_end = true; + if (strstr(mode, "+")) + { + retro_mode = RETRO_VFS_FILE_ACCESS_READ_WRITE | + RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING; + } + } + + output = filestream_open(path, retro_mode, + RETRO_VFS_FILE_ACCESS_HINT_NONE); + if (output && position_to_end) + filestream_seek(output, 0, RETRO_VFS_SEEK_POSITION_END); + + return output; +} + +int rfclose(RFILE* stream) +{ + return filestream_close(stream); +} + +int64_t rftell(RFILE* stream) +{ + return filestream_tell(stream); +} + +int64_t rfseek(RFILE* stream, int64_t offset, int origin) +{ + int seek_position = -1; + switch (origin) + { + case SEEK_SET: + seek_position = RETRO_VFS_SEEK_POSITION_START; + break; + case SEEK_CUR: + seek_position = RETRO_VFS_SEEK_POSITION_CURRENT; + break; + case SEEK_END: + seek_position = RETRO_VFS_SEEK_POSITION_END; + break; + } + + return filestream_seek(stream, offset, seek_position); +} + +int64_t rfread(void* buffer, + size_t elem_size, size_t elem_count, RFILE* stream) +{ + return (filestream_read(stream, buffer, elem_size * elem_count) / elem_size); +} + +char *rfgets(char *buffer, int maxCount, RFILE* stream) +{ + return filestream_gets(stream, buffer, maxCount); +} + +int rfgetc(RFILE* stream) +{ + return filestream_getc(stream); +} + +int64_t rfwrite(void const* buffer, + size_t elem_size, size_t elem_count, RFILE* stream) +{ + return filestream_write(stream, buffer, elem_size * elem_count); +} + +int rfputc(int character, RFILE * stream) +{ + return filestream_putc(stream, character); +} + +int64_t rfflush(RFILE * stream) +{ + return filestream_flush(stream); +} + +int rfprintf(RFILE * stream, const char * format, ...) +{ + int result; + va_list vl; + va_start(vl, format); + result = filestream_vprintf(stream, format, vl); + va_end(vl); + return result; +} + +int rferror(RFILE* stream) +{ + return filestream_error(stream); +} + +int rfeof(RFILE* stream) +{ + return filestream_eof(stream); +} + +int rfscanf(RFILE * stream, const char * format, ...) +{ + int result; + va_list vl; + va_start(vl, format); + result = filestream_vscanf(stream, format, &vl); + va_end(vl); + return result; +} diff --git a/libgambatte/libretro-common/string/stdstring.c b/libgambatte/libretro-common/string/stdstring.c index ab8ade2..d637988 100644 --- a/libgambatte/libretro-common/string/stdstring.c +++ b/libgambatte/libretro-common/string/stdstring.c @@ -22,10 +22,31 @@ #include #include +#include #include #include +const uint8_t lr_char_props[256] = { + /*x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x80,0x00,0x00, /* 0x */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 1x */ + 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 2x !"#$%&'()*+,-./ */ + 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x00,0x00,0x00,0x00,0x00,0x00, /* 3x 0123456789:;<=>? */ + 0x00,0x23,0x23,0x23,0x23,0x23,0x23,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, /* 4x @ABCDEFGHIJKLMNO */ + 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x00,0x00,0x00,0x00,0x08, /* 5x PQRSTUVWXYZ[\]^_ */ + 0x00,0x25,0x25,0x25,0x25,0x25,0x25,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, /* 6x `abcdefghijklmno */ + 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x00,0x00,0x00,0x00,0x00, /* 7x pqrstuvwxyz{|}~ */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 8x */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 9x */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* Ax */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* Bx */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* Cx */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* Dx */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* Ex */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* Fx */ +}; + char *string_init(const char *src) { return src ? strdup(src) : NULL; @@ -34,7 +55,7 @@ char *string_init(const char *src) void string_set(char **string, const char *src) { free(*string); - *string = src ? strdup(src) : NULL; + *string = string_init(src); } @@ -124,7 +145,7 @@ char *string_trim_whitespace_left(char *const s) size_t len = strlen(s); char *current = s; - while (*current && isspace((unsigned char)*current)) + while (*current && ISSPACE((unsigned char)*current)) { ++current; --len; @@ -145,13 +166,13 @@ char *string_trim_whitespace_right(char *const s) size_t len = strlen(s); char *current = s + len - 1; - while (current != s && isspace((unsigned char)*current)) + while (current != s && ISSPACE((unsigned char)*current)) { --current; --len; } - current[isspace((unsigned char)*current) ? 0 : 1] = '\0'; + current[ISSPACE((unsigned char)*current) ? 0 : 1] = '\0'; } return s; @@ -166,88 +187,207 @@ char *string_trim_whitespace(char *const s) return s; } -char *word_wrap(char* buffer, const char *string, int line_width, bool unicode, unsigned max_lines) +void word_wrap(char *dst, size_t dst_size, const char *src, int line_width, int wideglyph_width, unsigned max_lines) { - unsigned i = 0; - unsigned len = (unsigned)strlen(string); - unsigned lines = 1; + char *lastspace = NULL; + unsigned counter = 0; + unsigned lines = 1; + size_t src_len = strlen(src); + const char *src_end = src + src_len; - while (i < len) + /* Prevent buffer overflow */ + if (dst_size < src_len + 1) + return; + + /* Early return if src string length is less + * than line width */ + if (src_len < line_width) { - unsigned counter; - int pos = (int)(&buffer[i] - buffer); + strcpy(dst, src); + return; + } - /* copy string until the end of the line is reached */ - for (counter = 1; counter <= (unsigned)line_width; counter++) + while (*src != '\0') + { + unsigned char_len; + + char_len = (unsigned)(utf8skip(src, 1) - src); + counter++; + + if (*src == ' ') + lastspace = dst; /* Remember the location of the whitespace */ + else if (*src == '\n') { - const char *character; - unsigned char_len; - unsigned j = i; + /* If newlines embedded in the input, + * reset the index */ + lines++; + counter = 0; - /* check if end of string reached */ - if (i == len) + /* Early return if remaining src string + * length is less than line width */ + if (src_end - src <= line_width) { - buffer[i] = 0; - return buffer; + strcpy(dst, src); + return; } + } - character = utf8skip(&string[i], 1); - char_len = (unsigned)(character - &string[i]); + while (char_len--) + *dst++ = *src++; - if (!unicode) - counter += char_len - 1; - - do - { - buffer[i] = string[i]; - char_len--; - i++; - } while (char_len); - - /* check for newlines embedded in the original input - * and reset the index */ - if (buffer[j] == '\n') - { - lines++; - counter = 1; - } - } - - /* check for whitespace */ - if (string[i] == ' ') + if (counter >= (unsigned)line_width) { - if ((max_lines == 0 || lines < max_lines)) + counter = 0; + + if (lastspace && (max_lines == 0 || lines < max_lines)) { - buffer[i] = '\n'; - i++; + /* Replace nearest (previous) whitespace + * with newline character */ + *lastspace = '\n'; lines++; + + src -= dst - lastspace - 1; + dst = lastspace + 1; + lastspace = NULL; + + /* Early return if remaining src string + * length is less than line width */ + if (src_end - src < line_width) + { + strcpy(dst, src); + return; + } } } - else - { - int k; - - /* check for nearest whitespace back in string */ - for (k = i; k > 0; k--) - { - if (string[k] != ' ' || (max_lines != 0 && lines >= max_lines)) - continue; - - buffer[k] = '\n'; - /* set string index back to character after this one */ - i = k + 1; - lines++; - break; - } - - if (&buffer[i] - buffer == pos) - return buffer; - } } - buffer[i] = 0; + *dst = '\0'; +} - return buffer; +void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src, int line_width, int wideglyph_width, unsigned max_lines) +{ + char *lastspace = NULL; + char *lastwideglyph = NULL; + const char *src_end = src + strlen(src); + unsigned lines = 1; + /* 'line_width' means max numbers of characters per line, + * but this metric is only meaningful when dealing with + * 'regular' glyphs that have an on-screen pixel width + * similar to that of regular Latin characters. + * When handing so-called 'wide' Unicode glyphs, it is + * necessary to consider the actual on-screen pixel width + * of each character. + * In order to do this, we create a distinction between + * regular Latin 'non-wide' glyphs and 'wide' glyphs, and + * normalise all values relative to the on-screen pixel + * width of regular Latin characters: + * - Regular 'non-wide' glyphs have a normalised width of 100 + * - 'line_width' is therefore normalised to 100 * (width_in_characters) + * - 'wide' glyphs have a normalised width of + * 100 * (wide_character_pixel_width / latin_character_pixel_width) + * - When a character is detected, the position in the current + * line is incremented by the regular normalised width of 100 + * - If that character is then determined to be a 'wide' + * glyph, the position in the current line is further incremented + * by the difference between the normalised 'wide' and 'non-wide' + * width values */ + unsigned counter_normalized = 0; + int line_width_normalized = line_width * 100; + int additional_counter_normalized = wideglyph_width - 100; + + /* Early return if src string length is less + * than line width */ + if (src_end - src < line_width) + { + strlcpy(dst, src, dst_size); + return; + } + + while (*src != '\0') + { + unsigned char_len; + + char_len = (unsigned)(utf8skip(src, 1) - src); + counter_normalized += 100; + + /* Prevent buffer overflow */ + if (char_len >= dst_size) + break; + + if (*src == ' ') + lastspace = dst; /* Remember the location of the whitespace */ + else if (*src == '\n') + { + /* If newlines embedded in the input, + * reset the index */ + lines++; + counter_normalized = 0; + + /* Early return if remaining src string + * length is less than line width */ + if (src_end - src <= line_width) + { + strlcpy(dst, src, dst_size); + return; + } + } + else if (char_len >= 3) + { + /* Remember the location of the first byte + * whose length as UTF-8 >= 3*/ + lastwideglyph = dst; + counter_normalized += additional_counter_normalized; + } + + dst_size -= char_len; + while (char_len--) + *dst++ = *src++; + + if (counter_normalized >= (unsigned)line_width_normalized) + { + counter_normalized = 0; + + if (max_lines != 0 && lines >= max_lines) + continue; + else if (lastwideglyph && (!lastspace || lastwideglyph > lastspace)) + { + /* Insert newline character */ + *lastwideglyph = '\n'; + lines++; + src -= dst - lastwideglyph; + dst = lastwideglyph + 1; + lastwideglyph = NULL; + + /* Early return if remaining src string + * length is less than line width */ + if (src_end - src <= line_width) + { + strlcpy(dst, src, dst_size); + return; + } + } + else if (lastspace) + { + /* Replace nearest (previous) whitespace + * with newline character */ + *lastspace = '\n'; + lines++; + src -= dst - lastspace - 1; + dst = lastspace + 1; + lastspace = NULL; + + /* Early return if remaining src string + * length is less than line width */ + if (src_end - src < line_width) + { + strlcpy(dst, src, dst_size); + return; + } + } + } + } + + *dst = '\0'; } /* Splits string into tokens seperated by 'delim' @@ -355,7 +495,7 @@ unsigned string_to_unsigned(const char *str) for (ptr = str; *ptr != '\0'; ptr++) { - if (!isdigit(*ptr)) + if (!ISDIGIT((unsigned char)*ptr)) return 0; } @@ -388,7 +528,7 @@ unsigned string_hex_to_unsigned(const char *str) /* Check for valid characters */ for (ptr = hex_str; *ptr != '\0'; ptr++) { - if (!isxdigit(*ptr)) + if (!isxdigit((unsigned char)*ptr)) return 0; } diff --git a/libgambatte/libretro-common/time/rtime.c b/libgambatte/libretro-common/time/rtime.c index 1195da2..d66c228 100644 --- a/libgambatte/libretro-common/time/rtime.c +++ b/libgambatte/libretro-common/time/rtime.c @@ -30,6 +30,7 @@ #include