diff --git a/platform/libretro/libretro-common/compat/fopen_utf8.c b/platform/libretro/libretro-common/compat/fopen_utf8.c index 52b481e7..893afb83 100644 --- a/platform/libretro/libretro-common/compat/fopen_utf8.c +++ b/platform/libretro/libretro-common/compat/fopen_utf8.c @@ -36,9 +36,7 @@ void *fopen_utf8(const char * filename, const char * mode) { -#if defined(_XBOX) - return fopen(filename, mode); -#elif defined(LEGACY_WIN32) +#if defined(LEGACY_WIN32) FILE *ret = NULL; char * filename_local = utf8_to_local_string_alloc(filename); diff --git a/platform/libretro/libretro-common/encodings/encoding_utf.c b/platform/libretro/libretro-common/encodings/encoding_utf.c index f7c533f1..b6ad2f96 100644 --- a/platform/libretro/libretro-common/encodings/encoding_utf.c +++ b/platform/libretro/libretro-common/encodings/encoding_utf.c @@ -211,10 +211,7 @@ size_t utf8len(const char *string) return ret; } -static uint8_t utf8_walkbyte(const char **string) -{ - return *((*string)++); -} +#define utf8_walkbyte(string) (*((*(string))++)) /* Does not validate the input, returns garbage if it's not UTF-8. */ uint32_t utf8_walk(const char **string) @@ -227,14 +224,16 @@ uint32_t utf8_walk(const char **string) ret = (ret << 6) | (utf8_walkbyte(string) & 0x3F); if (first >= 0xE0) + { ret = (ret << 6) | (utf8_walkbyte(string) & 0x3F); - if (first >= 0xF0) - ret = (ret << 6) | (utf8_walkbyte(string) & 0x3F); - - if (first >= 0xF0) - return ret | (first & 7) << 18; - if (first >= 0xE0) + if (first >= 0xF0) + { + ret = (ret << 6) | (utf8_walkbyte(string) & 0x3F); + return ret | (first & 7) << 18; + } return ret | (first & 15) << 12; + } + return ret | (first & 31) << 6; } @@ -273,37 +272,25 @@ bool utf16_to_char_string(const uint16_t *in, char *s, size_t len) return ret; } +#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE) /* Returned pointer MUST be freed by the caller if non-NULL. */ -static char* mb_to_mb_string_alloc(const char *str, +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 = 0; + int path_buf_wide_len = MultiByteToWideChar(cp_in, 0, str, -1, NULL, 0); - if (!str || !*str) - return NULL; - - (void)path_buf; - (void)path_buf_wide; - (void)path_buf_len; - (void)path_buf_wide_len; - -#if !defined(_WIN32) || defined(_XBOX) - /* assume string needs no modification if not on Windows */ - return strdup(str); -#else -#ifdef UNICODE - /* TODO/FIXME: Not implemented. */ - return strdup(str); -#else - - /* Windows 95 will return 0 from these functions with a UTF8 codepage set without MSLU. From an unknown MSDN version (others omit this info): - * - CP_UTF8 Windows 98/Me, Windows NT 4.0 and later: Translate using UTF-8. When this is set, dwFlags must be zero. - * - Windows 95: Under the Microsoft Layer for Unicode, MultiByteToWideChar also supports CP_UTF7 and CP_UTF8. + /* Windows 95 will return 0 from these functions with + * a UTF8 codepage set without MSLU. + * + * From an unknown MSDN version (others omit this info): + * - CP_UTF8 Windows 98/Me, Windows NT 4.0 and later: + * Translate using UTF-8. When this is set, dwFlags must be zero. + * - Windows 95: Under the Microsoft Layer for Unicode, + * MultiByteToWideChar also supports CP_UTF7 and CP_UTF8. */ - path_buf_wide_len = MultiByteToWideChar(cp_in, 0, str, -1, NULL, 0); if (path_buf_wide_len) { @@ -355,20 +342,37 @@ static char* mb_to_mb_string_alloc(const char *str, free(path_buf_wide); return NULL; -#endif -#endif } +#endif /* Returned pointer MUST be freed by the caller if non-NULL. */ char* utf8_to_local_string_alloc(const char *str) { - return mb_to_mb_string_alloc(str, CODEPAGE_UTF8, CODEPAGE_LOCAL); + if (str && *str) + { +#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE) + return mb_to_mb_string_alloc(str, CODEPAGE_UTF8, CODEPAGE_LOCAL); +#else + /* assume string needs no modification if not on Windows */ + return strdup(str); +#endif + } + return NULL; } /* Returned pointer MUST be freed by the caller if non-NULL. */ char* local_to_utf8_string_alloc(const char *str) { - return mb_to_mb_string_alloc(str, CODEPAGE_LOCAL, CODEPAGE_UTF8); + if (str && *str) + { +#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE) + return mb_to_mb_string_alloc(str, CODEPAGE_LOCAL, CODEPAGE_UTF8); +#else + /* assume string needs no modification if not on Windows */ + return strdup(str); +#endif + } + return NULL; } /* Returned pointer MUST be freed by the caller if non-NULL. */ @@ -447,52 +451,44 @@ wchar_t* utf8_to_utf16_string_alloc(const char *str) char* utf16_to_utf8_string_alloc(const wchar_t *str) { #ifdef _WIN32 - int len = 0; - int out_len = 0; + int len = 0; #else - size_t len = 0; - size_t out_len = 0; + size_t len = 0; #endif - char *buf = NULL; + char *buf = NULL; if (!str || !*str) return NULL; #ifdef _WIN32 - len = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL); - - if (len) { + UINT code_page = CP_UTF8; + len = WideCharToMultiByte(code_page, + 0, str, -1, NULL, 0, NULL, NULL); + + /* fallback to ANSI codepage instead */ + if (!len) + { + code_page = CP_ACP; + len = WideCharToMultiByte(code_page, + 0, str, -1, NULL, 0, NULL, NULL); + } + buf = (char*)calloc(len, sizeof(char)); if (!buf) return NULL; - out_len = WideCharToMultiByte(CP_UTF8, 0, str, -1, buf, len, NULL, NULL); - } - else - { - /* fallback to ANSI codepage instead */ - len = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL); - - if (len) + if (WideCharToMultiByte(code_page, + 0, str, -1, buf, len, NULL, NULL) < 0) { - buf = (char*)calloc(len, sizeof(char)); - - if (!buf) - return NULL; - - out_len = WideCharToMultiByte(CP_ACP, 0, str, -1, buf, len, NULL, NULL); + free(buf); + return NULL; } } - - if (out_len < 0) - { - free(buf); - return NULL; - } #else - /* NOTE: For now, assume non-Windows platforms' locale is already UTF-8. */ + /* NOTE: For now, assume non-Windows platforms' + * locale is already UTF-8. */ len = wcstombs(NULL, str, 0) + 1; if (len) @@ -502,13 +498,11 @@ char* utf16_to_utf8_string_alloc(const wchar_t *str) if (!buf) return NULL; - out_len = wcstombs(buf, str, len); - } - - if (out_len == (size_t)-1) - { - free(buf); - return NULL; + if (wcstombs(buf, str, len) == (size_t)-1) + { + free(buf); + return NULL; + } } #endif diff --git a/platform/libretro/libretro-common/include/compat/getopt.h b/platform/libretro/libretro-common/include/compat/getopt.h index 74287b64..2e606a68 100644 --- a/platform/libretro/libretro-common/include/compat/getopt.h +++ b/platform/libretro/libretro-common/include/compat/getopt.h @@ -72,4 +72,3 @@ RETRO_END_DECLS /* pragma once */ #endif - diff --git a/platform/libretro/libretro-common/include/compat/intrinsics.h b/platform/libretro/libretro-common/include/compat/intrinsics.h index 3fc7d929..cb1f540d 100644 --- a/platform/libretro/libretro-common/include/compat/intrinsics.h +++ b/platform/libretro/libretro-common/include/compat/intrinsics.h @@ -41,7 +41,7 @@ RETRO_BEGIN_DECLS /* Count Leading Zero, unsigned 16bit input value */ static INLINE unsigned compat_clz_u16(uint16_t val) { -#ifdef __GNUC__ +#if defined(__GNUC__) && !defined(PS2) return __builtin_clz(val << 16 | 0x8000); #else unsigned ret = 0; @@ -61,7 +61,7 @@ static INLINE int compat_ctz(unsigned x) { #if defined(__GNUC__) && !defined(RARCH_CONSOLE) return __builtin_ctz(x); -#elif _MSC_VER >= 1400 && !defined(_XBOX) +#elif _MSC_VER >= 1400 && !defined(_XBOX) && !defined(__WINRT__) unsigned long r = 0; _BitScanReverse((unsigned long*)&r, x); return (int)r; diff --git a/platform/libretro/libretro-common/include/compat/msvc.h b/platform/libretro/libretro-common/include/compat/msvc.h index 822c9733..1c242630 100644 --- a/platform/libretro/libretro-common/include/compat/msvc.h +++ b/platform/libretro/libretro-common/include/compat/msvc.h @@ -31,6 +31,7 @@ extern "C" { /* Pre-MSVC 2015 compilers don't implement snprintf in a cross-platform manner. */ #if _MSC_VER < 1900 + #include #include #ifndef snprintf #define snprintf c99_snprintf_retro__ @@ -39,8 +40,9 @@ extern "C" { int c99_snprintf_retro__(char *outBuf, size_t size, const char *format, ...); #endif -/* Pre-MSVC 2010 compilers don't implement vsnprintf in a cross-platform manner? Not sure about this one. */ -#if _MSC_VER < 1600 +/* 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 vsnprintf @@ -56,6 +58,8 @@ extern "C" { #undef UNICODE /* Do not bother with UNICODE at this time. */ #include #include + +#define _USE_MATH_DEFINES #include /* Python headers defines ssize_t and sets HAVE_SSIZE_T. @@ -125,4 +129,3 @@ typedef int ssize_t; #endif #endif - diff --git a/platform/libretro/libretro-common/include/compat/msvc/stdint.h b/platform/libretro/libretro-common/include/compat/msvc/stdint.h index c791176c..7c91a685 100644 --- a/platform/libretro/libretro-common/include/compat/msvc/stdint.h +++ b/platform/libretro/libretro-common/include/compat/msvc/stdint.h @@ -67,7 +67,6 @@ extern "C" { # endif #endif - /* 7.18.1 Integer types. */ /* 7.18.1.1 Exact-width integer types. */ @@ -94,7 +93,6 @@ extern "C" { typedef signed __int64 int64_t; typedef unsigned __int64 uint64_t; - /* 7.18.1.2 Minimum-width integer types. */ typedef int8_t int_least8_t; typedef int16_t int_least16_t; @@ -255,4 +253,3 @@ typedef uint64_t uintmax_t; #endif #endif - diff --git a/platform/libretro/libretro-common/include/compat/posix_string.h b/platform/libretro/libretro-common/include/compat/posix_string.h index 9f56322a..f4380c3a 100644 --- a/platform/libretro/libretro-common/include/compat/posix_string.h +++ b/platform/libretro/libretro-common/include/compat/posix_string.h @@ -29,6 +29,10 @@ #include #endif +#if defined(PS2) +#include +#endif + RETRO_BEGIN_DECLS #ifdef _WIN32 @@ -55,7 +59,6 @@ int isblank(int c); #endif - RETRO_END_DECLS #endif diff --git a/platform/libretro/libretro-common/include/compat/strcasestr.h b/platform/libretro/libretro-common/include/compat/strcasestr.h index f849593b..c26de9e0 100644 --- a/platform/libretro/libretro-common/include/compat/strcasestr.h +++ b/platform/libretro/libretro-common/include/compat/strcasestr.h @@ -25,6 +25,10 @@ #include +#if defined(PS2) +#include +#endif + #if defined(RARCH_INTERNAL) && defined(HAVE_CONFIG_H) #include "../../../config.h" #endif @@ -46,4 +50,3 @@ RETRO_END_DECLS #endif #endif - diff --git a/platform/libretro/libretro-common/include/compat/strl.h b/platform/libretro/libretro-common/include/compat/strl.h index 290498d9..c70f1195 100644 --- a/platform/libretro/libretro-common/include/compat/strl.h +++ b/platform/libretro/libretro-common/include/compat/strl.h @@ -57,4 +57,3 @@ char *strldup(const char *s, size_t n); RETRO_END_DECLS #endif - diff --git a/platform/libretro/libretro-common/include/libretro.h b/platform/libretro/libretro-common/include/libretro.h index d0f1042c..2b7fc95a 100644 --- a/platform/libretro/libretro-common/include/libretro.h +++ b/platform/libretro/libretro-common/include/libretro.h @@ -1117,7 +1117,7 @@ enum retro_mod * This may be still be done regardless of the core options * interface version. * - * If version is >= 1 however, core options may instead be set by + * If version is 1 however, core options may instead be set by * passing an array of retro_core_option_definition structs to * RETRO_ENVIRONMENT_SET_CORE_OPTIONS, or a 2D array of * retro_core_option_definition structs to RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL. @@ -1132,8 +1132,8 @@ enum retro_mod * GET_VARIABLE. * This allows the frontend to present these variables to * a user dynamically. - * This should only be called if RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION - * returns an API version of >= 1. + * This should only be called if RETRO_ENVIRONMENT_GET_ENHANCED_CORE_OPTIONS + * returns an API version of 1. * This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES. * This should be called the first time as early as * possible (ideally in retro_set_environment). @@ -1169,6 +1169,8 @@ enum retro_mod * i.e. it should be feasible to cycle through options * without a keyboard. * + * First entry should be treated as a default. + * * Example entry: * { * "foo_option", @@ -1194,8 +1196,8 @@ enum retro_mod * GET_VARIABLE. * This allows the frontend to present these variables to * a user dynamically. - * This should only be called if RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION - * returns an API version of >= 1. + * This should only be called if RETRO_ENVIRONMENT_GET_ENHANCED_CORE_OPTIONS + * returns an API version of 1. * This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES. * This should be called the first time as early as * possible (ideally in retro_set_environment). @@ -1246,6 +1248,16 @@ enum retro_mod * default when calling SET_VARIABLES/SET_CORE_OPTIONS. */ +#define RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER 56 + /* unsigned * -- + * + * Allows an implementation to ask frontend preferred hardware + * context to use. Core should use this information to deal + * with what specific context to request with SET_HW_RENDER. + * + * 'data' points to an unsigned variable + */ + /* VFS functionality */ /* File paths: @@ -1922,6 +1934,10 @@ enum retro_sensor_action { RETRO_SENSOR_ACCELEROMETER_ENABLE = 0, RETRO_SENSOR_ACCELEROMETER_DISABLE, + RETRO_SENSOR_GYROSCOPE_ENABLE, + RETRO_SENSOR_GYROSCOPE_DISABLE, + RETRO_SENSOR_ILLUMINANCE_ENABLE, + RETRO_SENSOR_ILLUMINANCE_DISABLE, RETRO_SENSOR_DUMMY = INT_MAX }; @@ -1930,6 +1946,10 @@ enum retro_sensor_action #define RETRO_SENSOR_ACCELEROMETER_X 0 #define RETRO_SENSOR_ACCELEROMETER_Y 1 #define RETRO_SENSOR_ACCELEROMETER_Z 2 +#define RETRO_SENSOR_GYROSCOPE_X 3 +#define RETRO_SENSOR_GYROSCOPE_Y 4 +#define RETRO_SENSOR_GYROSCOPE_Z 5 +#define RETRO_SENSOR_ILLUMINANCE 6 typedef bool (RETRO_CALLCONV *retro_set_sensor_state_t)(unsigned port, enum retro_sensor_action action, unsigned rate); @@ -2502,20 +2522,8 @@ struct retro_core_option_display }; /* Maximum number of values permitted for a core option - * > Note: We have to set a maximum value due the limitations - * of the C language - i.e. it is not possible to create an - * array of structs each containing a variable sized array, - * so the retro_core_option_definition values array must - * have a fixed size. The size limit of 128 is a balancing - * act - it needs to be large enough to support all 'sane' - * core options, but setting it too large may impact low memory - * platforms. In practise, if a core option has more than - * 128 values then the implementation is likely flawed. - * To quote the above API reference: - * "The number of possible options should be very limited - * i.e. it should be feasible to cycle through options - * without a keyboard." - */ + * NOTE: This may be increased on a core-by-core basis + * if required (doing so has no effect on the frontend) */ #define RETRO_NUM_CORE_OPTION_VALUES_MAX 128 struct retro_core_option_value diff --git a/platform/libretro/libretro-common/include/memmap.h b/platform/libretro/libretro-common/include/memmap.h index 2bedd5c9..277f9cb3 100644 --- a/platform/libretro/libretro-common/include/memmap.h +++ b/platform/libretro/libretro-common/include/memmap.h @@ -26,7 +26,7 @@ #include #include -#if defined(__CELLOS_LV2__) || defined(PSP) || defined(GEKKO) || defined(VITA) || defined(_XBOX) || defined(_3DS) || defined(WIIU) || defined(SWITCH) +#if defined(__CELLOS_LV2__) || 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 diff --git a/platform/libretro/libretro-common/include/retro_common.h b/platform/libretro/libretro-common/include/retro_common.h index e4804fae..9a1fd5fd 100644 --- a/platform/libretro/libretro-common/include/retro_common.h +++ b/platform/libretro/libretro-common/include/retro_common.h @@ -34,4 +34,3 @@ in a public API, you may need this. #include #endif - diff --git a/platform/libretro/libretro-common/include/retro_common_api.h b/platform/libretro/libretro-common/include/retro_common_api.h index c3b3f492..d784842e 100644 --- a/platform/libretro/libretro-common/include/retro_common_api.h +++ b/platform/libretro/libretro-common/include/retro_common_api.h @@ -89,7 +89,9 @@ typedef int ssize_t; /* C++11 says this one isn't needed, but apparently (some versions of) mingw require it anyways */ /* https://stackoverflow.com/questions/8132399/how-to-printf-uint64-t-fails-with-spurious-trailing-in-format */ /* https://github.com/libretro/RetroArch/issues/6009 */ -#define __STDC_FORMAT_MACROS +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS 1 +#endif #include #endif #ifndef PRId64 @@ -113,6 +115,5 @@ Of course, another school of thought is that you should do as little damage as p in as few places as possible... */ - /* _LIBRETRO_COMMON_RETRO_COMMON_API_H */ #endif diff --git a/platform/libretro/libretro-common/include/retro_dirent.h b/platform/libretro/libretro-common/include/retro_dirent.h index c3450732..8a2591bd 100644 --- a/platform/libretro/libretro-common/include/retro_dirent.h +++ b/platform/libretro/libretro-common/include/retro_dirent.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2018 The RetroArch team +/* Copyright (C) 2010-2019 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (retro_dirent.h). @@ -23,6 +23,7 @@ #ifndef __RETRO_DIRENT_H #define __RETRO_DIRENT_H +#include #include #include @@ -30,6 +31,10 @@ RETRO_BEGIN_DECLS +#define DIRENT_REQUIRED_VFS_VERSION 3 + +void dirent_vfs_init(const struct retro_vfs_interface_info* vfs_info); + typedef struct RDIR RDIR; /** @@ -44,25 +49,27 @@ typedef struct RDIR RDIR; */ struct RDIR *retro_opendir(const char *name); +struct RDIR *retro_opendir_include_hidden(const char *name, bool include_hidden); + int retro_readdir(struct RDIR *rdir); +/* Deprecated, returns false, left for compatibility */ bool retro_dirent_error(struct RDIR *rdir); -void retro_dirent_include_hidden(struct RDIR *rdir, bool include_hidden); - const char *retro_dirent_get_name(struct RDIR *rdir); /** * * retro_dirent_is_dir: * @rdir : pointer to the directory entry. + * @unused : deprecated, included for compatibility reasons, pass NULL * * Is the directory listing entry a directory? * * Returns: true if directory listing entry is * a directory, false if not. */ -bool retro_dirent_is_dir(struct RDIR *rdir, const char *path); +bool retro_dirent_is_dir(struct RDIR *rdir, const char *unused); void retro_closedir(struct RDIR *rdir); diff --git a/platform/libretro/libretro-common/include/retro_environment.h b/platform/libretro/libretro-common/include/retro_environment.h index 1a18cd6f..4a68046b 100644 --- a/platform/libretro/libretro-common/include/retro_environment.h +++ b/platform/libretro/libretro-common/include/retro_environment.h @@ -73,6 +73,42 @@ printf("This is C++, version %d.\n", __cplusplus); /* This is not standard C. __STDC__ is not defined. */ #endif +#if defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) +/* Try to find out if we're compiling for WinRT or non-WinRT */ +#if defined(_MSC_VER) && defined(__has_include) +#if __has_include() +#define HAVE_WINAPIFAMILY_H 1 +#else +#define HAVE_WINAPIFAMILY_H 0 +#endif +/* If _USING_V110_SDK71_ is defined it means we are using the Windows XP toolset. */ +#elif defined(_MSC_VER) && (_MSC_VER >= 1700 && !_USING_V110_SDK71_) /* _MSC_VER == 1700 for Visual Studio 2012 */ +#define HAVE_WINAPIFAMILY_H 1 +#else +#define HAVE_WINAPIFAMILY_H 0 +#endif + +#if HAVE_WINAPIFAMILY_H +#include +#define WINAPI_FAMILY_WINRT (!WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)) +#else +#define WINAPI_FAMILY_WINRT 0 +#endif /* HAVE_WINAPIFAMILY_H */ + +#if WINAPI_FAMILY_WINRT +#undef __WINRT__ +#define __WINRT__ 1 +#endif + +/* MSVC obviously has to have some non-standard constants... */ +#if _M_IX86_FP == 1 +#define __SSE__ 1 +#elif _M_IX86_FP == 2 || (defined(_M_AMD64) || defined(_M_X64)) +#define __SSE__ 1 +#define __SSE2__ 1 +#endif + +#endif #endif diff --git a/platform/libretro/libretro-common/include/retro_miscellaneous.h b/platform/libretro/libretro-common/include/retro_miscellaneous.h index afcb885c..3893416e 100644 --- a/platform/libretro/libretro-common/include/retro_miscellaneous.h +++ b/platform/libretro/libretro-common/include/retro_miscellaneous.h @@ -77,7 +77,7 @@ static INLINE bool bits_any_set(uint32_t* ptr, uint32_t count) #ifndef PATH_MAX_LENGTH #if defined(__CELLOS_LV2__) #define PATH_MAX_LENGTH CELL_FS_MAX_FS_PATH_LENGTH -#elif defined(_XBOX1) || defined(_3DS) || defined(PSP) || defined(GEKKO)|| defined(WIIU) +#elif defined(_XBOX1) || defined(_3DS) || defined(PSP) || defined(PS2) || defined(GEKKO)|| defined(WIIU) || defined(ORBIS) #define PATH_MAX_LENGTH 512 #else #define PATH_MAX_LENGTH 4096 @@ -155,4 +155,28 @@ typedef struct uint32_t data[8]; } retro_bits_t; +#ifdef _WIN32 +# ifdef _WIN64 +# define PRI_SIZET PRIu64 +# else +# if _MSC_VER == 1800 +# define PRI_SIZET PRIu32 +# else +# define PRI_SIZET "u" +# endif +# endif +#elif PS2 +# define PRI_SIZET "u" +#else +# if (SIZE_MAX == 0xFFFF) +# define PRI_SIZET "hu" +# elif (SIZE_MAX == 0xFFFFFFFF) +# define PRI_SIZET "u" +# elif (SIZE_MAX == 0xFFFFFFFFFFFFFFFF) +# define PRI_SIZET "lu" +# else +# error PRI_SIZET: unknown SIZE_MAX +# endif +#endif + #endif diff --git a/platform/libretro/libretro-common/include/streams/file_stream.h b/platform/libretro/libretro-common/include/streams/file_stream.h index 546fd126..0cfadad8 100644 --- a/platform/libretro/libretro-common/include/streams/file_stream.h +++ b/platform/libretro/libretro-common/include/streams/file_stream.h @@ -36,19 +36,22 @@ #include #include +#include -#define FILESTREAM_REQUIRED_VFS_VERSION 1 +#define FILESTREAM_REQUIRED_VFS_VERSION 2 RETRO_BEGIN_DECLS typedef struct RFILE RFILE; -#define FILESTREAM_REQUIRED_VFS_VERSION 1 +#define FILESTREAM_REQUIRED_VFS_VERSION 2 void filestream_vfs_init(const struct retro_vfs_interface_info* vfs_info); int64_t filestream_get_size(RFILE *stream); +int64_t filestream_truncate(RFILE *stream, int64_t length); + /** * filestream_open: * @path : path to file @@ -58,7 +61,7 @@ int64_t filestream_get_size(RFILE *stream); * Opens a file for reading or writing, depending on the requested mode. * Returns a pointer to an RFILE if opened successfully, otherwise NULL. **/ -RFILE *filestream_open(const char *path, unsigned mode, unsigned hints); +RFILE* filestream_open(const char *path, unsigned mode, unsigned hints); int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position); @@ -74,10 +77,12 @@ int filestream_close(RFILE *stream); int64_t filestream_read_file(const char *path, void **buf, int64_t *len); -char *filestream_gets(RFILE *stream, char *s, size_t len); +char* filestream_gets(RFILE *stream, char *s, size_t len); int filestream_getc(RFILE *stream); +int filestream_scanf(RFILE *stream, const char* format, ...); + int filestream_eof(RFILE *stream); bool filestream_write_file(const char *path, const void *data, int64_t size); @@ -96,11 +101,14 @@ int filestream_delete(const char *path); int filestream_rename(const char *old_path, const char *new_path); -const char *filestream_get_path(RFILE *stream); +const char* filestream_get_path(RFILE *stream); bool filestream_exists(const char *path); -char *filestream_getline(RFILE *stream); +/* Returned pointer must be freed by the caller. */ +char* filestream_getline(RFILE *stream); + +libretro_vfs_implementation_file* filestream_get_vfs_handle(RFILE *stream); RETRO_END_DECLS diff --git a/platform/libretro/libretro-common/include/streams/file_stream_transforms.h b/platform/libretro/libretro-common/include/streams/file_stream_transforms.h index c1690fa6..9cf15c59 100644 --- a/platform/libretro/libretro-common/include/streams/file_stream_transforms.h +++ b/platform/libretro/libretro-common/include/streams/file_stream_transforms.h @@ -30,6 +30,8 @@ RETRO_BEGIN_DECLS +#ifndef SKIP_STDIO_REDEFINES + #define FILE RFILE #undef fopen @@ -41,9 +43,11 @@ RETRO_BEGIN_DECLS #undef fgetc #undef fwrite #undef fputc +#undef fflush #undef fprintf #undef ferror #undef feof +#undef fscanf #define fopen rfopen #define fclose rfclose @@ -54,9 +58,13 @@ RETRO_BEGIN_DECLS #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); @@ -78,12 +86,16 @@ int64_t rfwrite(void const* buffer, 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/platform/libretro/libretro-common/include/string/stdstring.h b/platform/libretro/libretro-common/include/string/stdstring.h index eb2954b8..d57256a7 100644 --- a/platform/libretro/libretro-common/include/string/stdstring.h +++ b/platform/libretro/libretro-common/include/string/stdstring.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2018 The RetroArch team +/* Copyright (C) 2010-2019 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (stdstring.h). @@ -37,43 +37,20 @@ RETRO_BEGIN_DECLS static INLINE bool string_is_empty(const char *data) { - return (data == NULL) || (*data == '\0'); + return !data || (*data == '\0'); } static INLINE bool string_is_equal(const char *a, const char *b) { - if (!a || !b) - return false; - while(*a && (*a == *b)) - a++, b++; - return (*(const unsigned char*)a - *(const unsigned char*)b) == 0; + return (a && b) ? !strcmp(a, b) : false; } -static INLINE bool string_is_not_equal(const char *a, const char *b) -{ - return !string_is_equal(a, b); -} +#define STRLEN_CONST(x) ((sizeof((x))-1)) -#define string_add_pair_open(s, size) strlcat((s), " (", (size)) -#define string_add_pair_close(s, size) strlcat((s), ")", (size)) -#define string_add_bracket_open(s, size) strlcat((s), "{", (size)) -#define string_add_bracket_close(s, size) strlcat((s), "}", (size)) -#define string_add_single_quote(s, size) strlcat((s), "'", (size)) -#define string_add_quote(s, size) strlcat((s), "\"", (size)) -#define string_add_colon(s, size) strlcat((s), ":", (size)) -#define string_add_glob_open(s, size) strlcat((s), "glob('*", (size)) -#define string_add_glob_close(s, size) strlcat((s), "*')", (size)) - -static INLINE void string_add_between_pairs(char *s, const char *str, - size_t size) -{ - string_add_pair_open(s, size); - strlcat(s, str, size); - string_add_pair_close(s, size); -} +#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 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) @@ -116,7 +93,7 @@ char *string_to_upper(char *s); char *string_to_lower(char *s); -char *string_ucwords(char* s); +char *string_ucwords(char *s); char *string_replace_substring(const char *in, const char *pattern, const char *by); @@ -130,7 +107,44 @@ char *string_trim_whitespace_right(char *const s); /* Remove leading and trailing whitespaces */ char *string_trim_whitespace(char *const s); -char *word_wrap(char* buffer, const char *string, int line_width, bool unicode); +/* max_lines == 0 means no limit */ +char *word_wrap(char *buffer, const char *string, + int line_width, bool unicode, unsigned max_lines); + +/* Splits string into tokens seperated by 'delim' + * > Returned token string must be free()'d + * > Returns NULL if token is not found + * > After each call, 'str' is set to the position after the + * last found token + * > Tokens *include* empty strings + * Usage example: + * char *str = "1,2,3,4,5,6,7,,,10,"; + * char **str_ptr = &str; + * char *token = NULL; + * while((token = string_tokenize(str_ptr, ","))) + * { + * printf("%s\n", token); + * free(token); + * token = NULL; + * } + */ +char* string_tokenize(char **str, const char *delim); + +/* Removes every instance of character 'c' from 'str' */ +void string_remove_all_chars(char *str, char c); + +/* Replaces every instance of character 'find' in 'str' + * with character 'replace' */ +void string_replace_all_chars(char *str, char find, char replace); + +/* Converts string to unsigned integer. + * Returns 0 if string is invalid */ +unsigned string_to_unsigned(const char *str); + +/* Converts hexadecimal string to unsigned integer. + * Handles optional leading '0x'. + * Returns 0 if string is invalid */ +unsigned string_hex_to_unsigned(const char *str); RETRO_END_DECLS diff --git a/platform/libretro/libretro-common/include/vfs/vfs_implementation.h b/platform/libretro/libretro-common/include/vfs/vfs_implementation.h index 047b1703..c981cf72 100644 --- a/platform/libretro/libretro-common/include/vfs/vfs_implementation.h +++ b/platform/libretro/libretro-common/include/vfs/vfs_implementation.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2018 The RetroArch team +/* Copyright (C) 2010-2019 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (vfs_implementation.h). @@ -23,20 +23,13 @@ #ifndef __LIBRETRO_SDK_VFS_IMPLEMENTATION_H #define __LIBRETRO_SDK_VFS_IMPLEMENTATION_H +#include #include #include +#include +#include -/* Replace the following symbol with something appropriate - * to signify the file is being compiled for a front end instead of a core. - * This allows the same code to act as reference implementation - * for VFS and as fallbacks for when the front end does not provide VFS functionality. - */ - -#ifdef VFS_FRONTEND -typedef struct retro_vfs_file_handle libretro_vfs_implementation_file; -#else -typedef struct libretro_vfs_implementation_file libretro_vfs_implementation_file; -#endif +RETRO_BEGIN_DECLS libretro_vfs_implementation_file *retro_vfs_file_open_impl(const char *path, unsigned mode, unsigned hints); @@ -46,6 +39,8 @@ int retro_vfs_file_error_impl(libretro_vfs_implementation_file *stream); int64_t retro_vfs_file_size_impl(libretro_vfs_implementation_file *stream); +int64_t retro_vfs_file_truncate_impl(libretro_vfs_implementation_file *stream, int64_t length); + int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream); int64_t retro_vfs_file_seek_impl(libretro_vfs_implementation_file *stream, int64_t offset, int seek_position); @@ -62,4 +57,20 @@ int retro_vfs_file_rename_impl(const char *old_path, const char *new_path); const char *retro_vfs_file_get_path_impl(libretro_vfs_implementation_file *stream); +int retro_vfs_stat_impl(const char *path, int32_t *size); + +int retro_vfs_mkdir_impl(const char *dir); + +libretro_vfs_implementation_dir *retro_vfs_opendir_impl(const char *dir, bool include_hidden); + +bool retro_vfs_readdir_impl(libretro_vfs_implementation_dir *dirstream); + +const char *retro_vfs_dirent_get_name_impl(libretro_vfs_implementation_dir *dirstream); + +bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *dirstream); + +int retro_vfs_closedir_impl(libretro_vfs_implementation_dir *dirstream); + +RETRO_END_DECLS + #endif diff --git a/platform/libretro/libretro-common/streams/file_stream.c b/platform/libretro/libretro-common/streams/file_stream.c index 5ccdbcfa..e3e45907 100644 --- a/platform/libretro/libretro-common/streams/file_stream.c +++ b/platform/libretro/libretro-common/streams/file_stream.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #ifdef HAVE_CONFIG_H @@ -40,6 +41,7 @@ static retro_vfs_get_path_t filestream_get_path_cb = NULL; static retro_vfs_open_t filestream_open_cb = NULL; static retro_vfs_close_t filestream_close_cb = NULL; static retro_vfs_size_t filestream_size_cb = NULL; +static retro_vfs_truncate_t filestream_truncate_cb = NULL; static retro_vfs_tell_t filestream_tell_cb = NULL; static retro_vfs_seek_t filestream_seek_cb = NULL; static retro_vfs_read_t filestream_read_cb = NULL; @@ -66,6 +68,7 @@ void filestream_vfs_init(const struct retro_vfs_interface_info* vfs_info) filestream_close_cb = NULL; filestream_tell_cb = NULL; filestream_size_cb = NULL; + filestream_truncate_cb = NULL; filestream_seek_cb = NULL; filestream_read_cb = NULL; filestream_write_cb = NULL; @@ -83,6 +86,7 @@ void filestream_vfs_init(const struct retro_vfs_interface_info* vfs_info) filestream_open_cb = vfs_iface->open; filestream_close_cb = vfs_iface->close; filestream_size_cb = vfs_iface->size; + filestream_truncate_cb = vfs_iface->truncate; filestream_tell_cb = vfs_iface->tell; filestream_seek_cb = vfs_iface->seek; filestream_read_cb = vfs_iface->read; @@ -126,6 +130,21 @@ int64_t filestream_get_size(RFILE *stream) return output; } +int64_t filestream_truncate(RFILE *stream, int64_t length) +{ + int64_t output; + + if (filestream_truncate_cb != NULL) + output = filestream_truncate_cb(stream->hfile, length); + else + output = retro_vfs_file_truncate_impl((libretro_vfs_implementation_file*)stream->hfile, length); + + if (output == vfs_error_return_value) + stream->error_flag = true; + + return output; +} + /** * filestream_open: * @path : path to file @@ -135,7 +154,7 @@ int64_t filestream_get_size(RFILE *stream) * Opens a file for reading or writing, depending on the requested mode. * Returns a pointer to an RFILE if opened successfully, otherwise NULL. **/ -RFILE *filestream_open(const char *path, unsigned mode, unsigned hints) +RFILE* filestream_open(const char *path, unsigned mode, unsigned hints) { struct retro_vfs_file_handle *fp = NULL; RFILE* output = NULL; @@ -157,7 +176,7 @@ RFILE *filestream_open(const char *path, unsigned mode, unsigned hints) return output; } -char *filestream_gets(RFILE *stream, char *s, size_t len) +char* filestream_gets(RFILE *stream, char *s, size_t len) { int c = 0; char *p = s; @@ -185,12 +204,107 @@ int filestream_getc(RFILE *stream) { char c = 0; if (!stream) - return 0; - if(filestream_read(stream, &c, 1) == 1) - return (int)c; + return EOF; + if (filestream_read(stream, &c, 1) == 1) + return (int)(unsigned char)c; return EOF; } +int filestream_scanf(RFILE *stream, const char* format, ...) +{ + char buf[4096]; + char subfmt[64]; + va_list args; + + const char * bufiter = buf; + int64_t startpos = filestream_tell(stream); + int ret = 0; + int64_t maxlen = filestream_read(stream, buf, sizeof(buf)-1); + + if (maxlen <= 0) + return EOF; + + buf[maxlen] = '\0'; + + va_start(args, format); + + while (*format) + { + if (*format == '%') + { + int sublen; + + char* subfmtiter = subfmt; + bool asterisk = false; + + *subfmtiter++ = *format++; /* '%' */ + + /* %[*][width][length]specifier */ + + if (*format == '*') + { + asterisk = true; + *subfmtiter++ = *format++; + } + + while (isdigit(*format)) *subfmtiter++ = *format++; /* width */ + + /* length */ + if (*format == 'h' || *format == 'l') + { + if (format[1] == format[0]) *subfmtiter++ = *format++; + *subfmtiter++ = *format++; + } + else if (*format == 'j' || *format == 'z' || *format == 't' || *format == 'L') + { + *subfmtiter++ = *format++; + } + + /* specifier - always a single character (except ]) */ + if (*format == '[') + { + while (*format != ']') *subfmtiter++ = *format++; + *subfmtiter++ = *format++; + } + else *subfmtiter++ = *format++; + + *subfmtiter++ = '%'; + *subfmtiter++ = 'n'; + *subfmtiter++ = '\0'; + + if (sizeof(void*) != sizeof(long*)) abort(); /* all pointers must have the same size */ + if (asterisk) + { + if (sscanf(bufiter, subfmt, &sublen) != 0) break; + } + else + { + if (sscanf(bufiter, subfmt, va_arg(args, void*), &sublen) != 1) break; + } + + ret++; + bufiter += sublen; + } + else if (isspace(*format)) + { + while (isspace(*bufiter)) bufiter++; + format++; + } + else + { + if (*bufiter != *format) + break; + bufiter++; + format++; + } + } + + va_end(args); + filestream_seek(stream, startpos+(bufiter-buf), RETRO_VFS_SEEK_POSITION_START); + + return ret; +} + int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position) { int64_t output; @@ -212,7 +326,6 @@ int filestream_eof(RFILE *stream) return stream->eof_flag; } - int64_t filestream_tell(RFILE *stream) { int64_t output; @@ -286,7 +399,7 @@ int filestream_rename(const char *old_path, const char *new_path) return retro_vfs_file_rename_impl(old_path, new_path); } -const char *filestream_get_path(RFILE *stream) +const char* filestream_get_path(RFILE *stream) { if (filestream_get_path_cb != NULL) return filestream_get_path_cb(stream->hfile); @@ -314,7 +427,7 @@ int filestream_putc(RFILE *stream, int c) char c_char = (char)c; if (!stream) return EOF; - return filestream_write(stream, &c_char, 1)==1 ? 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) @@ -459,13 +572,14 @@ bool filestream_write_file(const char *path, const void *data, int64_t size) return true; } -char *filestream_getline(RFILE *stream) +/* Returned pointer must be freed by the caller. */ +char* filestream_getline(RFILE *stream) { - char* newline_tmp = NULL; + char *newline_tmp = NULL; size_t cur_size = 8; size_t idx = 0; int in = 0; - char* newline = (char*)malloc(9); + char *newline = (char*)malloc(9); if (!stream || !newline) { @@ -499,3 +613,8 @@ char *filestream_getline(RFILE *stream) newline[idx] = '\0'; return newline; } + +libretro_vfs_implementation_file* filestream_get_vfs_handle(RFILE *stream) +{ + return (libretro_vfs_implementation_file*)stream->hfile; +} diff --git a/platform/libretro/libretro-common/streams/file_stream_transforms.c b/platform/libretro/libretro-common/streams/file_stream_transforms.c index efeb7edd..0a0cd540 100644 --- a/platform/libretro/libretro-common/streams/file_stream_transforms.c +++ b/platform/libretro/libretro-common/streams/file_stream_transforms.c @@ -37,7 +37,7 @@ RFILE* rfopen(const char *path, const char *mode) retro_mode = RETRO_VFS_FILE_ACCESS_READ; if (strstr(mode, "+")) { - retro_mode = RETRO_VFS_FILE_ACCESS_READ_WRITE | + retro_mode = RETRO_VFS_FILE_ACCESS_READ_WRITE | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING; } } @@ -49,12 +49,12 @@ RFILE* rfopen(const char *path, const char *mode) } else if (strstr(mode, "a")) { - retro_mode = RETRO_VFS_FILE_ACCESS_WRITE | + 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_mode = RETRO_VFS_FILE_ACCESS_READ_WRITE | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING; } } @@ -99,7 +99,7 @@ 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) { - return filestream_read(stream, buffer, elem_size * elem_count); + return (filestream_read(stream, buffer, elem_size * elem_count) / elem_size); } char *rfgets(char *buffer, int maxCount, RFILE* stream) @@ -109,7 +109,7 @@ char *rfgets(char *buffer, int maxCount, RFILE* stream) int rfgetc(RFILE* stream) { - return filestream_getc(stream); + return filestream_getc(stream); } int64_t rfwrite(void const* buffer, @@ -123,14 +123,19 @@ 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; + va_list vl; + va_start(vl, format); + result = filestream_vprintf(stream, format, vl); + va_end(vl); + return result; } int rferror(RFILE* stream) @@ -142,3 +147,13 @@ 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_scanf(stream, format, vl); + va_end(vl); + return result; +} diff --git a/platform/libretro/libretro-common/string/stdstring.c b/platform/libretro/libretro-common/string/stdstring.c index 6e1555db..e2afef14 100644 --- a/platform/libretro/libretro-common/string/stdstring.c +++ b/platform/libretro/libretro-common/string/stdstring.c @@ -82,6 +82,10 @@ char *string_replace_substring(const char *in, outlen = strlen(in) - pattern_len*numhits + replacement_len*numhits; out = (char *)malloc(outlen+1); + + if (!out) + return NULL; + outat = out; inat = in; inprev = in; @@ -105,15 +109,17 @@ char *string_trim_whitespace_left(char *const s) { if(s && *s) { - size_t len = strlen(s); - char *cur = s; + size_t len = strlen(s); + char *current = s; - while(*cur && isspace((unsigned char)*cur)) - ++cur, --len; - - if(s != cur) - memmove(s, cur, len + 1); + while(*current && isspace((unsigned char)*current)) + { + ++current; + --len; + } + if(s != current) + memmove(s, current, len + 1); } return s; @@ -124,13 +130,16 @@ char *string_trim_whitespace_right(char *const s) { if(s && *s) { - size_t len = strlen(s); - char *cur = s + len - 1; + size_t len = strlen(s); + char *current = s + len - 1; - while(cur != s && isspace((unsigned char)*cur)) - --cur, --len; + while(current != s && isspace((unsigned char)*current)) + { + --current; + --len; + } - cur[isspace((unsigned char)*cur) ? 0 : 1] = '\0'; + current[isspace((unsigned char)*current) ? 0 : 1] = '\0'; } return s; @@ -145,14 +154,16 @@ char *string_trim_whitespace(char *const s) return s; } -char *word_wrap(char* buffer, const char *string, int line_width, bool unicode) +char *word_wrap(char* buffer, const char *string, int line_width, bool unicode, unsigned max_lines) { - unsigned i = 0; - unsigned len = (unsigned)strlen(string); + unsigned i = 0; + unsigned len = (unsigned)strlen(string); + unsigned lines = 1; while (i < len) { unsigned counter; + int pos = (int)(&buffer[i] - buffer); /* copy string until the end of the line is reached */ for (counter = 1; counter <= (unsigned)line_width; counter++) @@ -184,14 +195,21 @@ char *word_wrap(char* buffer, const char *string, int line_width, bool unicode) /* 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] == ' ') { - buffer[i] = '\n'; - i++; + if ((max_lines == 0 || lines < max_lines)) + { + buffer[i] = '\n'; + i++; + lines++; + } } else { @@ -200,14 +218,18 @@ char *word_wrap(char* buffer, const char *string, int line_width, bool unicode) /* check for nearest whitespace back in string */ for (k = i; k > 0; k--) { - if (string[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; } } @@ -215,3 +237,148 @@ char *word_wrap(char* buffer, const char *string, int line_width, bool unicode) return buffer; } + +/* Splits string into tokens seperated by 'delim' + * > Returned token string must be free()'d + * > Returns NULL if token is not found + * > After each call, 'str' is set to the position after the + * last found token + * > Tokens *include* empty strings + * Usage example: + * char *str = "1,2,3,4,5,6,7,,,10,"; + * char **str_ptr = &str; + * char *token = NULL; + * while((token = string_tokenize(str_ptr, ","))) + * { + * printf("%s\n", token); + * free(token); + * token = NULL; + * } + */ +char* string_tokenize(char **str, const char *delim) +{ + /* Taken from https://codereview.stackexchange.com/questions/216956/strtok-function-thread-safe-supports-empty-tokens-doesnt-change-string# */ + char *str_ptr = NULL; + char *delim_ptr = NULL; + char *token = NULL; + size_t token_len = 0; + + /* Sanity checks */ + if (!str || string_is_empty(delim)) + return NULL; + + str_ptr = *str; + + /* Note: we don't check string_is_empty() here, + * empty strings are valid */ + if (!str_ptr) + return NULL; + + /* Search for delimiter */ + delim_ptr = strstr(str_ptr, delim); + + if (delim_ptr) + token_len = delim_ptr - str_ptr; + else + token_len = strlen(str_ptr); + + /* Allocate token string */ + token = (char *)malloc((token_len + 1) * sizeof(char)); + + if (!token) + return NULL; + + /* Copy token */ + strlcpy(token, str_ptr, (token_len + 1) * sizeof(char)); + token[token_len] = '\0'; + + /* Update input string pointer */ + *str = delim_ptr ? delim_ptr + strlen(delim) : NULL; + + return token; +} + +/* Removes every instance of character 'c' from 'str' */ +void string_remove_all_chars(char *str, char c) +{ + char *read_ptr = NULL; + char *write_ptr = NULL; + + if (string_is_empty(str)) + return; + + read_ptr = str; + write_ptr = str; + + while (*read_ptr != '\0') + { + *write_ptr = *read_ptr++; + write_ptr += (*write_ptr != c) ? 1 : 0; + } + + *write_ptr = '\0'; +} + +/* Replaces every instance of character 'find' in 'str' + * with character 'replace' */ +void string_replace_all_chars(char *str, char find, char replace) +{ + char *str_ptr = str; + + if (string_is_empty(str)) + return; + + while((str_ptr = strchr(str_ptr, find)) != NULL) + *str_ptr++ = replace; +} + +/* Converts string to unsigned integer. + * Returns 0 if string is invalid */ +unsigned string_to_unsigned(const char *str) +{ + const char *ptr = NULL; + + if (string_is_empty(str)) + return 0; + + for (ptr = str; *ptr != '\0'; ptr++) + { + if (!isdigit(*ptr)) + return 0; + } + + return (unsigned)strtoul(str, NULL, 10); +} + +/* Converts hexadecimal string to unsigned integer. + * Handles optional leading '0x'. + * Returns 0 if string is invalid */ +unsigned string_hex_to_unsigned(const char *str) +{ + const char *hex_str = str; + const char *ptr = NULL; + size_t len; + + if (string_is_empty(str)) + return 0; + + /* Remove leading '0x', if required */ + len = strlen(str); + + if (len >= 2) + if ((str[0] == '0') && + ((str[1] == 'x') || (str[1] == 'X'))) + hex_str = str + 2; + + if (string_is_empty(hex_str)) + return 0; + + /* Check for valid characters */ + for (ptr = hex_str; *ptr != '\0'; ptr++) + { + if (!isxdigit(*ptr)) + return 0; + } + + return (unsigned)strtoul(hex_str, NULL, 16); +} diff --git a/platform/libretro/libretro-common/vfs/vfs_implementation.c b/platform/libretro/libretro-common/vfs/vfs_implementation.c index ff8100f7..71529482 100644 --- a/platform/libretro/libretro-common/vfs/vfs_implementation.c +++ b/platform/libretro/libretro-common/vfs/vfs_implementation.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2010-2018 The RetroArch team +/* Copyright (C) 2010-2019 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (vfs_implementation.c). @@ -26,6 +26,8 @@ #include #include +#include + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -34,25 +36,36 @@ # ifdef _MSC_VER # define setmode _setmode # endif +#include # ifdef _XBOX # include # define INVALID_FILE_ATTRIBUTES -1 # else -# include + # include # include # include # endif +# include #else # if defined(PSP) # include # endif +# if defined(PS2) +# include +# include +# endif # include # include # if !defined(VITA) # include # endif # include +# if defined(ORBIS) +# include +# include +# include +# endif #endif #ifdef __CELLOS_LV2__ @@ -66,6 +79,90 @@ #include #endif +/* TODO: Some things are duplicated but I'm really afraid of breaking other platforms by touching this */ +#if defined(VITA) +# include +# include +# include +#elif defined(ORBIS) +# include +# include +# include +# include +#elif !defined(_WIN32) +# if defined(PSP) +# include +# endif +# if defined(PS2) +# include +# endif +# include +# include +# include +# include +#endif + +#if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(__QNX__) || defined(PSP) || defined(PS2) +#include /* stat() is defined here */ +#endif + +#ifdef __APPLE__ +#include +#endif +#ifdef __HAIKU__ +#include +#endif +#ifndef __MACH__ +#include +#include +#endif +#include +#include +#include + +#if defined(_WIN32) +#ifndef _XBOX +#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(ORBIS) +#include +#include +#include +#endif +#if defined(PSP) +#include +#endif + +#if defined(PS2) +#include +#include +#endif + +#if defined(__CELLOS_LV2__) +#include +#endif + +#if defined(VITA) +#define FIO_S_ISDIR SCE_S_ISDIR +#endif + +#if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(__QNX__) || defined(PSP) +#include /* stat() is defined here */ +#endif + /* Assume W-functions do not work below Win2K and Xbox platforms */ #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX) @@ -75,89 +172,89 @@ #endif -#if defined(_WIN32) && !defined(_XBOX) +#if defined(_WIN32) #if !defined(_MSC_VER) || (defined(_MSC_VER) && _MSC_VER >= 1400) #define ATLEAST_VC2005 #endif #endif -#ifdef RARCH_INTERNAL -#ifndef VFS_FRONTEND -#define VFS_FRONTEND -#endif -#endif - #include #include #include #include #include +#include + +#ifdef HAVE_CDROM +#include +#endif #define RFILE_HINT_UNBUFFERED (1 << 8) -#ifdef VFS_FRONTEND -struct retro_vfs_file_handle -#else -struct libretro_vfs_implementation_file -#endif -{ - int fd; - unsigned hints; - int64_t size; - char *buf; - FILE *fp; - char* orig_path; -#if defined(HAVE_MMAP) - uint64_t mappos; - uint64_t mapsize; - uint8_t *mapped; -#endif -}; - int64_t retro_vfs_file_seek_internal(libretro_vfs_implementation_file *stream, int64_t offset, int whence) { if (!stream) - goto error; + return -1; if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) + { +#ifdef HAVE_CDROM + if (stream->scheme == VFS_SCHEME_CDROM) + return retro_vfs_file_seek_cdrom(stream, offset, whence); +#endif /* VC2005 and up have a special 64-bit fseek */ #ifdef ATLEAST_VC2005 return _fseeki64(stream->fp, offset, whence); #elif defined(__CELLOS_LV2__) || defined(_MSC_VER) && _MSC_VER <= 1310 return fseek(stream->fp, (long)offset, whence); +#elif defined(PS2) + { + int64_t ret = fileXioLseek(fileno(stream->fp), (off_t)offset, whence); + /* fileXioLseek could return positive numbers */ + if (ret > 0) + return 0; + return ret; + } +#elif defined(ORBIS) + { + int ret = orbisLseek(stream->fd, offset, whence); + if (ret < 0) + return -1; + return 0; + } #else return fseeko(stream->fp, (off_t)offset, whence); #endif - + } #ifdef HAVE_MMAP /* Need to check stream->mapped because this function is * called in filestream_open() */ - if (stream->mapped && stream->hints & + if (stream->mapped && stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS) { - /* fseek() returns error on under/overflow but + /* fseek() returns error on under/overflow but * allows cursor > EOF for read-only file descriptors. */ switch (whence) { case SEEK_SET: if (offset < 0) - goto error; + return -1; stream->mappos = offset; break; case SEEK_CUR: - if ((offset < 0 && stream->mappos + offset > stream->mappos) || + if ( (offset < 0 && stream->mappos + offset > stream->mappos) || (offset > 0 && stream->mappos + offset < stream->mappos)) - goto error; + return -1; stream->mappos += offset; break; case SEEK_END: if (stream->mapsize + offset < stream->mapsize) - goto error; + return -1; stream->mappos = stream->mapsize + offset; break; @@ -167,12 +264,9 @@ int64_t retro_vfs_file_seek_internal(libretro_vfs_implementation_file *stream, i #endif if (lseek(stream->fd, offset, whence) < 0) - goto error; + return -1; return 0; - -error: - return -1; } /** @@ -185,17 +279,44 @@ error: * Returns a pointer to an RFILE if opened successfully, otherwise NULL. **/ -libretro_vfs_implementation_file *retro_vfs_file_open_impl(const char *path, unsigned mode, unsigned hints) +libretro_vfs_implementation_file *retro_vfs_file_open_impl( + const char *path, unsigned mode, unsigned hints) { int flags = 0; const char *mode_str = NULL; - libretro_vfs_implementation_file *stream = (libretro_vfs_implementation_file*)calloc(1, sizeof(*stream)); + libretro_vfs_implementation_file *stream = (libretro_vfs_implementation_file*) + calloc(1, sizeof(*stream)); +#if defined(VFS_FRONTEND) || defined(HAVE_CDROM) + int path_len = (int)strlen(path); +#endif #ifdef VFS_FRONTEND const char *dumb_prefix = "vfsonly://"; + size_t dumb_prefix_siz = strlen(dumb_prefix); + int dumb_prefix_len = (int)dumb_prefix_siz; - if (!memcmp(path, dumb_prefix, strlen(dumb_prefix))) - path += strlen(dumb_prefix); + if (path_len >= dumb_prefix_len) + { + if (!memcmp(path, dumb_prefix, dumb_prefix_len)) + path += dumb_prefix_siz; + } +#endif + +#ifdef HAVE_CDROM + { + const char *cdrom_prefix = "cdrom://"; + size_t cdrom_prefix_siz = strlen(cdrom_prefix); + int cdrom_prefix_len = (int)cdrom_prefix_siz; + + if (path_len > cdrom_prefix_len) + { + if (!memcmp(path, cdrom_prefix, cdrom_prefix_len)) + { + path += cdrom_prefix_siz; + stream->scheme = VFS_SCHEME_CDROM; + } + } + } #endif if (!stream) @@ -228,21 +349,28 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(const char *path, uns mode_str = "wb"; flags = O_WRONLY | O_CREAT | O_TRUNC; -#ifndef _WIN32 +#if !defined(ORBIS) +#if defined(PS2) + flags |= FIO_S_IRUSR | FIO_S_IWUSR; +#elif !defined(_WIN32) flags |= S_IRUSR | S_IWUSR; #else flags |= O_BINARY; +#endif #endif break; case RETRO_VFS_FILE_ACCESS_READ_WRITE: mode_str = "w+b"; - flags = O_RDWR | O_CREAT | O_TRUNC; -#ifndef _WIN32 +#if !defined(ORBIS) +#if defined(PS2) + flags |= FIO_S_IRUSR | FIO_S_IWUSR; +#elif !defined(_WIN32) flags |= S_IRUSR | S_IWUSR; #else flags |= O_BINARY; +#endif #endif break; @@ -251,24 +379,55 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(const char *path, uns mode_str = "r+b"; flags = O_RDWR; -#ifndef _WIN32 +#if !defined(ORBIS) +#if defined(PS2) + flags |= FIO_S_IRUSR | FIO_S_IWUSR; +#elif !defined(_WIN32) flags |= S_IRUSR | S_IWUSR; #else flags |= O_BINARY; +#endif #endif break; - + default: goto error; } if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) { - FILE *fp = (FILE*)fopen_utf8(path, mode_str); - - if (!fp) +#ifdef ORBIS + int fd = orbisOpen(path, flags, 0644); + if (fd < 0) + { + stream->fd = -1; goto error; + } + stream->fd = fd; +#else + FILE *fp; +#ifdef HAVE_CDROM + if (stream->scheme == VFS_SCHEME_CDROM) + { + retro_vfs_file_open_cdrom(stream, path, mode, hints); +#if defined(_WIN32) && !defined(_XBOX) + if (!stream->fh) + goto error; +#else + if (!stream->fp) + goto error; +#endif + } + else +#endif + { + fp = (FILE*)fopen_utf8(path, mode_str); + if (!fp) + goto error; + + stream->fp = fp; + } /* Regarding setvbuf: * * https://www.freebsd.org/cgi/man.cgi?query=setvbuf&apropos=0&sektion=0&manpath=FreeBSD+11.1-RELEASE&arch=default&format=html @@ -279,9 +438,15 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(const char *path, uns * Since C89 does not support specifying a null buffer with a non-zero size, we create and track our own buffer for it. */ /* TODO: this is only useful for a few platforms, find which and add ifdef */ - stream->fp = fp; - stream->buf = (char*)calloc(1, 0x4000); - setvbuf(stream->fp, stream->buf, _IOFBF, 0x4000); +#if !defined(PS2) && !defined(PSP) + if (stream->scheme != VFS_SCHEME_CDROM) + { + stream->buf = (char*)calloc(1, 0x4000); + if (stream->fp) + setvbuf(stream->fp, stream->buf, _IOFBF, 0x4000); + } +#endif +#endif } else { @@ -298,7 +463,7 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(const char *path, uns free(path_wide); #endif #else - stream->fd = open(path, flags, 0); + stream->fd = open(path, flags, 0); #endif if (stream->fd == -1) @@ -324,14 +489,31 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(const char *path, uns } #endif } +#ifdef ORBIS + stream->size = orbisLseek(stream->fd, 0, SEEK_END); + orbisLseek(stream->fd, 0, SEEK_SET); +#else +#ifdef HAVE_CDROM + if (stream->scheme == VFS_SCHEME_CDROM) + { + retro_vfs_file_seek_cdrom(stream, 0, SEEK_SET); + retro_vfs_file_seek_cdrom(stream, 0, SEEK_END); - retro_vfs_file_seek_internal(stream, 0, SEEK_SET); - retro_vfs_file_seek_internal(stream, 0, SEEK_END); + stream->size = retro_vfs_file_tell_impl(stream); - stream->size = retro_vfs_file_tell_impl(stream); + retro_vfs_file_seek_cdrom(stream, 0, SEEK_SET); + } + else +#endif + { + retro_vfs_file_seek_internal(stream, 0, SEEK_SET); + retro_vfs_file_seek_internal(stream, 0, SEEK_END); - retro_vfs_file_seek_internal(stream, 0, SEEK_SET); + stream->size = retro_vfs_file_tell_impl(stream); + retro_vfs_file_seek_internal(stream, 0, SEEK_SET); + } +#endif return stream; error: @@ -344,10 +526,20 @@ int retro_vfs_file_close_impl(libretro_vfs_implementation_file *stream) if (!stream) return -1; +#ifdef HAVE_CDROM + if (stream->scheme == VFS_SCHEME_CDROM) + { + retro_vfs_file_close_cdrom(stream); + goto end; + } +#endif + if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) { if (stream->fp) + { fclose(stream->fp); + } } else { @@ -358,11 +550,25 @@ int retro_vfs_file_close_impl(libretro_vfs_implementation_file *stream) } if (stream->fd > 0) + { +#ifdef ORBIS + orbisClose(stream->fd); + stream->fd = -1; +#else close(stream->fd); +#endif + } +#ifdef HAVE_CDROM +end: + if (stream->cdrom.cue_buf) + free(stream->cdrom.cue_buf); +#endif if (stream->buf) free(stream->buf); + if (stream->orig_path) free(stream->orig_path); + free(stream); return 0; @@ -370,14 +576,39 @@ int retro_vfs_file_close_impl(libretro_vfs_implementation_file *stream) int retro_vfs_file_error_impl(libretro_vfs_implementation_file *stream) { +#ifdef HAVE_CDROM + if (stream->scheme == VFS_SCHEME_CDROM) + return retro_vfs_file_error_cdrom(stream); +#endif +#ifdef ORBIS + /* TODO/FIXME - implement this? */ + return 0; +#else return ferror(stream->fp); +#endif } int64_t retro_vfs_file_size_impl(libretro_vfs_implementation_file *stream) +{ + if (stream) + return stream->size; + return 0; +} + +int64_t retro_vfs_file_truncate_impl(libretro_vfs_implementation_file *stream, int64_t length) { if (!stream) - return 0; - return stream->size; + return -1; + +#ifdef _WIN32 + if (_chsize(_fileno(stream->fp), length) != 0) + return -1; +#elif !defined(VITA) && !defined(PSP) && !defined(PS2) && !defined(ORBIS) && (!defined(SWITCH) || defined(HAVE_LIBNX)) + if (ftruncate(fileno(stream->fp), length) != 0) + return -1; +#endif + + return 0; } int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream) @@ -386,13 +617,27 @@ int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream) return -1; if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) -/* VC2005 and up have a special 64-bit ftell */ + { +#ifdef HAVE_CDROM + if (stream->scheme == VFS_SCHEME_CDROM) + return retro_vfs_file_tell_cdrom(stream); +#endif +#ifdef ORBIS + { + int64_t ret = orbisLseek(stream->fd, 0, SEEK_CUR); + if (ret < 0) + return -1; + return ret; + } +#else + /* VC2005 and up have a special 64-bit ftell */ #ifdef ATLEAST_VC2005 return _ftelli64(stream->fp); #else return ftell(stream->fp); #endif - +#endif + } #ifdef HAVE_MMAP /* Need to check stream->mapped because this function * is called in filestream_open() */ @@ -405,7 +650,8 @@ int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream) return 0; } -int64_t retro_vfs_file_seek_impl(libretro_vfs_implementation_file *stream, int64_t offset, int seek_position) +int64_t retro_vfs_file_seek_impl(libretro_vfs_implementation_file *stream, + int64_t offset, int seek_position) { int whence = -1; switch (seek_position) @@ -424,19 +670,31 @@ int64_t retro_vfs_file_seek_impl(libretro_vfs_implementation_file *stream, int64 return retro_vfs_file_seek_internal(stream, offset, whence); } -int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream, void *s, uint64_t len) +int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream, + void *s, uint64_t len) { if (!stream || !s) - goto error; + return -1; if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) + { +#ifdef HAVE_CDROM + if (stream->scheme == VFS_SCHEME_CDROM) + return retro_vfs_file_read_cdrom(stream, s, len); +#endif +#ifdef ORBIS + if (orbisRead(stream->fd, s, (size_t)len) < 0) + return -1; + return 0; +#else return fread(s, 1, (size_t)len, stream->fp); - +#endif + } #ifdef HAVE_MMAP if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS) { if (stream->mappos > stream->mapsize) - goto error; + return -1; if (stream->mappos + len > stream->mapsize) len = stream->mapsize - stream->mappos; @@ -449,48 +707,54 @@ int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream, void #endif return read(stream->fd, s, (size_t)len); - -error: - return -1; } int64_t retro_vfs_file_write_impl(libretro_vfs_implementation_file *stream, const void *s, uint64_t len) { if (!stream) - goto error; + return -1; if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) + { +#ifdef ORBIS + if (orbisWrite(stream->fd, s, (size_t)len) < 0) + return -1; + return 0; +#else return fwrite(s, 1, (size_t)len, stream->fp); +#endif + } #ifdef HAVE_MMAP if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS) - goto error; + return -1; #endif return write(stream->fd, s, (size_t)len); - -error: - return -1; } int retro_vfs_file_flush_impl(libretro_vfs_implementation_file *stream) { if (!stream) return -1; - return fflush(stream->fp)==0 ? 0 : -1; +#ifdef ORBIS + return 0; +#else + return fflush(stream->fp) == 0 ? 0 : -1; +#endif } int retro_vfs_file_remove_impl(const char *path) { - char *path_local = NULL; - wchar_t *path_wide = NULL; +#if defined(_WIN32) && !defined(_XBOX) + /* Win32 (no Xbox) */ +#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 + char *path_local = NULL; +#else + wchar_t *path_wide = NULL; +#endif if (!path || !*path) return -1; - - (void)path_local; - (void)path_wide; - -#if defined(_WIN32) && !defined(_XBOX) #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 path_local = utf8_to_local_string_alloc(path); @@ -514,78 +778,563 @@ int retro_vfs_file_remove_impl(const char *path) return 0; } #endif + return -1; +#elif defined(ORBIS) + /* Orbis + * TODO/FIXME - stub for now */ + return 0; #else if (remove(path) == 0) return 0; -#endif return -1; +#endif } int retro_vfs_file_rename_impl(const char *old_path, const char *new_path) { +#if defined(_WIN32) && !defined(_XBOX) + /* Win32 (no Xbox) */ + int ret = -1; +#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 char *old_path_local = NULL; - char *new_path_local = NULL; +#else wchar_t *old_path_wide = NULL; - wchar_t *new_path_wide = NULL; +#endif if (!old_path || !*old_path || !new_path || !*new_path) return -1; - (void)old_path_local; - (void)new_path_local; - (void)old_path_wide; - (void)new_path_wide; - -#if defined(_WIN32) && !defined(_XBOX) #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 old_path_local = utf8_to_local_string_alloc(old_path); - new_path_local = utf8_to_local_string_alloc(new_path); if (old_path_local) { + char *new_path_local = utf8_to_local_string_alloc(new_path); + if (new_path_local) { - int ret = rename(old_path_local, new_path_local); - free(old_path_local); + if (rename(old_path_local, new_path_local) == 0) + ret = 0; free(new_path_local); - return ret==0 ? 0 : -1; } free(old_path_local); } - - if (new_path_local) - free(new_path_local); #else old_path_wide = utf8_to_utf16_string_alloc(old_path); - new_path_wide = utf8_to_utf16_string_alloc(new_path); if (old_path_wide) { + wchar_t *new_path_wide = utf8_to_utf16_string_alloc(new_path); + if (new_path_wide) { - int ret = _wrename(old_path_wide, new_path_wide); - free(old_path_wide); + if (_wrename(old_path_wide, new_path_wide) == 0) + ret = 0; free(new_path_wide); - return ret==0 ? 0 : -1; } free(old_path_wide); } - - if (new_path_wide) - free(new_path_wide); #endif - return -1; + return ret; + +#elif defined(ORBIS) + /* Orbis */ + /* TODO/FIXME - Stub for now */ + if (!old_path || !*old_path || !new_path || !*new_path) + return -1; + return 0; + #else - return rename(old_path, new_path)==0 ? 0 : -1; + /* Every other platform */ + if (!old_path || !*old_path || !new_path || !*new_path) + return -1; + return rename(old_path, new_path) == 0 ? 0 : -1; #endif } -const char *retro_vfs_file_get_path_impl(libretro_vfs_implementation_file *stream) +const char *retro_vfs_file_get_path_impl( + libretro_vfs_implementation_file *stream) { /* should never happen, do something noisy so caller can be fixed */ if (!stream) abort(); return stream->orig_path; } + +int retro_vfs_stat_impl(const char *path, int32_t *size) +{ +#if defined(VITA) || defined(PSP) + /* Vita / PSP */ + SceIoStat buf; + int stat_ret; + bool is_dir = false; + bool is_character_special = false; + char *tmp = NULL; + size_t len = 0; + + if (!path || !*path) + return 0; + + tmp = strdup(path); + len = strlen(tmp); + if (tmp[len-1] == '/') + tmp[len-1] = '\0'; + + stat_ret = sceIoGetstat(tmp, &buf); + free(tmp); + if (stat_ret < 0) + return 0; + + if (size) + *size = (int32_t)buf.st_size; + + is_dir = FIO_S_ISDIR(buf.st_mode); + + return RETRO_VFS_STAT_IS_VALID | (is_dir ? RETRO_VFS_STAT_IS_DIRECTORY : 0) | (is_character_special ? RETRO_VFS_STAT_IS_CHARACTER_SPECIAL : 0); + +#elif defined(ORBIS) + /* Orbis */ + bool is_dir, is_character_special; + int dir_ret; + + if (!path || !*path) + return 0; + + if (size) + *size = (int32_t)buf.st_size; + + dir_ret = orbisDopen(path); + is_dir = dir_ret > 0; + orbisDclose(dir_ret); + + is_character_special = S_ISCHR(buf.st_mode); + + return RETRO_VFS_STAT_IS_VALID | (is_dir ? RETRO_VFS_STAT_IS_DIRECTORY : 0) | (is_character_special ? RETRO_VFS_STAT_IS_CHARACTER_SPECIAL : 0); + +#elif defined(PS2) + /* PS2 */ + iox_stat_t buf; + bool is_dir; + bool is_character_special = false; + char *tmp = NULL; + size_t len = 0; + + if (!path || !*path) + return 0; + + tmp = strdup(path); + len = strlen(tmp); + if (tmp[len-1] == '/') + tmp[len-1] = '\0'; + + fileXioGetStat(tmp, &buf); + free(tmp); + + if (size) + *size = (int32_t)buf.size; + + if (!buf.mode) + { + /* if fileXioGetStat fails */ + int dir_ret = fileXioDopen(path); + is_dir = dir_ret > 0; + if (is_dir) { + fileXioDclose(dir_ret); + } + } + else + is_dir = FIO_S_ISDIR(buf.mode); + + return RETRO_VFS_STAT_IS_VALID | (is_dir ? RETRO_VFS_STAT_IS_DIRECTORY : 0) | (is_character_special ? RETRO_VFS_STAT_IS_CHARACTER_SPECIAL : 0); + +#elif defined(__CELLOS_LV2__) + /* CellOS Lv2 */ + bool is_dir; + bool is_character_special = false; + CellFsStat buf; + + if (!path || !*path) + return 0; + if (cellFsStat(path, &buf) < 0) + return 0; + + if (size) + *size = (int32_t)buf.st_size; + + is_dir = ((buf.st_mode & S_IFMT) == S_IFDIR); + + return RETRO_VFS_STAT_IS_VALID | (is_dir ? RETRO_VFS_STAT_IS_DIRECTORY : 0) | (is_character_special ? RETRO_VFS_STAT_IS_CHARACTER_SPECIAL : 0); + +#elif defined(_WIN32) + /* Windows */ + bool is_dir; + DWORD file_info; + struct _stat buf; + bool is_character_special = false; +#if defined(LEGACY_WIN32) + char *path_local = NULL; +#else + wchar_t *path_wide = NULL; +#endif + + if (!path || !*path) + return 0; + +#if defined(LEGACY_WIN32) + path_local = utf8_to_local_string_alloc(path); + file_info = GetFileAttributes(path_local); + + if (!string_is_empty(path_local)) + _stat(path_local, &buf); + + if (path_local) + free(path_local); +#else + path_wide = utf8_to_utf16_string_alloc(path); + file_info = GetFileAttributesW(path_wide); + + _wstat(path_wide, &buf); + + if (path_wide) + free(path_wide); +#endif + + if (file_info == INVALID_FILE_ATTRIBUTES) + return 0; + + if (size) + *size = (int32_t)buf.st_size; + + is_dir = (file_info & FILE_ATTRIBUTE_DIRECTORY); + + return RETRO_VFS_STAT_IS_VALID | (is_dir ? RETRO_VFS_STAT_IS_DIRECTORY : 0) | (is_character_special ? RETRO_VFS_STAT_IS_CHARACTER_SPECIAL : 0); + +#else + /* Every other platform */ + bool is_dir, is_character_special; + struct stat buf; + + if (!path || !*path) + return 0; + if (stat(path, &buf) < 0) + return 0; + + if (size) + *size = (int32_t)buf.st_size; + + is_dir = S_ISDIR(buf.st_mode); + is_character_special = S_ISCHR(buf.st_mode); + + return RETRO_VFS_STAT_IS_VALID | (is_dir ? RETRO_VFS_STAT_IS_DIRECTORY : 0) | (is_character_special ? RETRO_VFS_STAT_IS_CHARACTER_SPECIAL : 0); +#endif +} + +#if defined(VITA) +#define path_mkdir_error(ret) (((ret) == SCE_ERROR_ERRNO_EEXIST)) +#elif defined(PSP) || defined(PS2) || defined(_3DS) || defined(WIIU) || defined(SWITCH) || defined(ORBIS) +#define path_mkdir_error(ret) ((ret) == -1) +#else +#define path_mkdir_error(ret) ((ret) < 0 && errno == EEXIST) +#endif + +int retro_vfs_mkdir_impl(const char *dir) +{ +#if defined(_WIN32) +#ifdef LEGACY_WIN32 + int ret = _mkdir(dir); +#else + wchar_t *dirW = utf8_to_utf16_string_alloc(dir); + int ret = -1; + + if (dirW) + { + ret = _wmkdir(dirW); + free(dirW); + } +#endif +#elif defined(IOS) + int ret = mkdir(dir, 0755); +#elif defined(VITA) || defined(PSP) + int ret = sceIoMkdir(dir, 0777); +#elif defined(PS2) + int ret = fileXioMkdir(dir, 0777); +#elif defined(ORBIS) + int ret = orbisMkdir(dir, 0755); +#elif defined(__QNX__) + int ret = mkdir(dir, 0777); +#else + int ret = mkdir(dir, 0750); +#endif + + if (path_mkdir_error(ret)) + return -2; + return ret < 0 ? -1 : 0; +} + +#ifdef VFS_FRONTEND +struct retro_vfs_dir_handle +#else +struct libretro_vfs_implementation_dir +#endif +{ + char* orig_path; +#if defined(_WIN32) +#if defined(LEGACY_WIN32) + WIN32_FIND_DATA entry; +#else + WIN32_FIND_DATAW entry; +#endif + HANDLE directory; + bool next; + char path[PATH_MAX_LENGTH]; +#elif defined(VITA) || defined(PSP) + SceUID directory; + SceIoDirent entry; +#elif defined(PS2) + int directory; + iox_dirent_t entry; +#elif defined(__CELLOS_LV2__) + CellFsErrno error; + int directory; + CellFsDirent entry; +#elif defined(ORBIS) + int directory; + struct dirent entry; +#else + DIR *directory; + const struct dirent *entry; +#endif +}; + +static bool dirent_check_error(libretro_vfs_implementation_dir *rdir) +{ +#if defined(_WIN32) + return (rdir->directory == INVALID_HANDLE_VALUE); +#elif defined(VITA) || defined(PSP) || defined(PS2) || defined(ORBIS) + return (rdir->directory < 0); +#elif defined(__CELLOS_LV2__) + return (rdir->error != CELL_FS_SUCCEEDED); +#else + return !(rdir->directory); +#endif +} + +libretro_vfs_implementation_dir *retro_vfs_opendir_impl( + const char *name, bool include_hidden) +{ +#if defined(_WIN32) + unsigned path_len; + char path_buf[1024]; + size_t copied = 0; +#if defined(LEGACY_WIN32) + char *path_local = NULL; +#else + wchar_t *path_wide = NULL; +#endif +#endif + libretro_vfs_implementation_dir *rdir; + + /*Reject null or empty string paths*/ + if (!name || (*name == 0)) + return NULL; + + /*Allocate RDIR struct. Tidied later with retro_closedir*/ + rdir = (libretro_vfs_implementation_dir*)calloc(1, sizeof(*rdir)); + if (!rdir) + return NULL; + + rdir->orig_path = strdup(name); + +#if defined(_WIN32) + path_buf[0] = '\0'; + path_len = strlen(name); + + copied = strlcpy(path_buf, name, sizeof(path_buf)); + + /* Non-NT platforms don't like extra slashes in the path */ + if (name[path_len - 1] != '\\') + path_buf[copied++] = '\\'; + + path_buf[copied] = '*'; + path_buf[copied+1] = '\0'; + +#if defined(LEGACY_WIN32) + path_local = utf8_to_local_string_alloc(path_buf); + rdir->directory = FindFirstFile(path_local, &rdir->entry); + + if (path_local) + free(path_local); +#else + path_wide = utf8_to_utf16_string_alloc(path_buf); + rdir->directory = FindFirstFileW(path_wide, &rdir->entry); + + if (path_wide) + free(path_wide); +#endif + +#elif defined(VITA) || defined(PSP) + rdir->directory = sceIoDopen(name); +#elif defined(PS2) + rdir->directory = ps2fileXioDopen(name); +#elif defined(_3DS) + rdir->directory = !string_is_empty(name) ? opendir(name) : NULL; + rdir->entry = NULL; +#elif defined(__CELLOS_LV2__) + rdir->error = cellFsOpendir(name, &rdir->directory); +#elif defined(ORBIS) + rdir->directory = orbisDopen(name); +#else + rdir->directory = opendir(name); + rdir->entry = NULL; +#endif + +#ifdef _WIN32 + if (include_hidden) + rdir->entry.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN; + else + rdir->entry.dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN; +#endif + + if (rdir->directory && !dirent_check_error(rdir)) + return rdir; + + retro_vfs_closedir_impl(rdir); + return NULL; +} + +bool retro_vfs_readdir_impl(libretro_vfs_implementation_dir *rdir) +{ +#if defined(_WIN32) + if (rdir->next) +#if defined(LEGACY_WIN32) + return (FindNextFile(rdir->directory, &rdir->entry) != 0); +#else + return (FindNextFileW(rdir->directory, &rdir->entry) != 0); +#endif + + rdir->next = true; + return (rdir->directory != INVALID_HANDLE_VALUE); +#elif defined(VITA) || defined(PSP) + return (sceIoDread(rdir->directory, &rdir->entry) > 0); +#elif defined(PS2) + iox_dirent_t record; + int ret = ps2fileXioDread(rdir->directory, &record); + rdir->entry = record; + return ( ret > 0); +#elif defined(__CELLOS_LV2__) + uint64_t nread; + rdir->error = cellFsReaddir(rdir->directory, &rdir->entry, &nread); + return (nread != 0); +#elif defined(ORBIS) + return (orbisDread(rdir->directory, &rdir->entry) > 0); +#else + return ((rdir->entry = readdir(rdir->directory)) != NULL); +#endif +} + +const char *retro_vfs_dirent_get_name_impl(libretro_vfs_implementation_dir *rdir) +{ +#if defined(_WIN32) +#if defined(LEGACY_WIN32) + { + char *name_local = local_to_utf8_string_alloc(rdir->entry.cFileName); + memset(rdir->entry.cFileName, 0, sizeof(rdir->entry.cFileName)); + strlcpy(rdir->entry.cFileName, name_local, sizeof(rdir->entry.cFileName)); + + if (name_local) + free(name_local); + } +#else + { + char *name = utf16_to_utf8_string_alloc(rdir->entry.cFileName); + memset(rdir->entry.cFileName, 0, sizeof(rdir->entry.cFileName)); + strlcpy((char*)rdir->entry.cFileName, name, sizeof(rdir->entry.cFileName)); + + if (name) + free(name); + } +#endif + return (char*)rdir->entry.cFileName; +#elif defined(VITA) || defined(PSP) || defined(__CELLOS_LV2__) || defined(ORBIS) + return rdir->entry.d_name; +#elif defined(PS2) + return rdir->entry.name; +#else + if (!rdir || !rdir->entry) + return NULL; + return rdir->entry->d_name; +#endif +} + +bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *rdir) +{ +#if defined(_WIN32) + const WIN32_FIND_DATA *entry = (const WIN32_FIND_DATA*)&rdir->entry; + return entry->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; +#elif defined(PSP) || defined(VITA) + const SceIoDirent *entry = (const SceIoDirent*)&rdir->entry; +#if defined(PSP) + return (entry->d_stat.st_attr & FIO_SO_IFDIR) == FIO_SO_IFDIR; +#elif defined(VITA) + return SCE_S_ISDIR(entry->d_stat.st_mode); +#endif +#elif defined(PS2) + const iox_dirent_t *entry = (const iox_dirent_t*)&rdir->entry; + return FIO_S_ISDIR(entry->stat.mode); +#elif defined(__CELLOS_LV2__) + CellFsDirent *entry = (CellFsDirent*)&rdir->entry; + return (entry->d_type == CELL_FS_TYPE_DIRECTORY); +#elif defined(ORBIS) + const struct dirent *entry = &rdir->entry; + if (entry->d_type == DT_DIR) + return true; + if (!(entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK)) + return false; +#else + struct stat buf; + char path[PATH_MAX_LENGTH]; +#if defined(DT_DIR) + const struct dirent *entry = (const struct dirent*)rdir->entry; + if (entry->d_type == DT_DIR) + return true; + /* This can happen on certain file systems. */ + if (!(entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK)) + return false; +#endif + /* dirent struct doesn't have d_type, do it the slow way ... */ + path[0] = '\0'; + fill_pathname_join(path, rdir->orig_path, retro_vfs_dirent_get_name_impl(rdir), sizeof(path)); + if (stat(path, &buf) < 0) + return false; + return S_ISDIR(buf.st_mode); +#endif +} + +int retro_vfs_closedir_impl(libretro_vfs_implementation_dir *rdir) +{ + if (!rdir) + return -1; + +#if defined(_WIN32) + if (rdir->directory != INVALID_HANDLE_VALUE) + FindClose(rdir->directory); +#elif defined(VITA) || defined(PSP) + sceIoDclose(rdir->directory); +#elif defined(PS2) + ps2fileXioDclose(rdir->directory); +#elif defined(__CELLOS_LV2__) + rdir->error = cellFsClosedir(rdir->directory); +#elif defined(ORBIS) + orbisDclose(rdir->directory); +#else + if (rdir->directory) + closedir(rdir->directory); +#endif + + if (rdir->orig_path) + free(rdir->orig_path); + free(rdir); + return 0; +} diff --git a/platform/libretro/libretro.c b/platform/libretro/libretro.c index ae91156c..b836185b 100644 --- a/platform/libretro/libretro.c +++ b/platform/libretro/libretro.c @@ -1038,6 +1038,26 @@ static const char *find_bios(int *region, const char *cd_fname) return NULL; } +static void set_memory_maps(void) +{ + if (PicoIn.AHW & PAHW_MCD) + { + const size_t SCD_BIT = 1ULL << 31ULL; + const uint64_t mem = RETRO_MEMDESC_SYSTEM_RAM; + struct retro_memory_map mmaps; + struct retro_memory_descriptor descs[] = { + { mem, PicoMem.ram, 0, 0xFF0000, 0, 0, 0x10000, "68KRAM" }, + /* virtual address using SCD_BIT so all 512M of prg_ram can be accessed */ + /* at address $80020000 */ + { mem, Pico_mcd->prg_ram, 0, SCD_BIT | 0x020000, 0, 0, 0x80000, "PRGRAM" }, + }; + + mmaps.descriptors = descs; + mmaps.num_descriptors = sizeof(descs) / sizeof(descs[0]); + environ_cb(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &mmaps); + } +} + bool retro_load_game(const struct retro_game_info *info) { enum media_type_e media_type; @@ -1156,6 +1176,9 @@ bool retro_load_game(const struct retro_game_info *info) PicoIn.sndOut = sndBuffer; PsndRerate(0); + /* Setup retro memory maps */ + set_memory_maps(); + return true; }