diff --git a/Makefile.common b/Makefile.common index 1d7a7b6..91305d2 100644 --- a/Makefile.common +++ b/Makefile.common @@ -46,7 +46,10 @@ SOURCES_C += \ ifeq ($(STATIC_LINKING),1) else SOURCES_C += $(CORE_DIR)/drivers/libretro/libretro-common/streams/memory_stream.c \ - $(CORE_DIR)/drivers/libretro/libretro-common/compat/compat_snprintf.c + $(CORE_DIR)/drivers/libretro/libretro-common/compat/compat_snprintf.c \ + $(CORE_DIR)/drivers/libretro/libretro-common/string/stdstring.c \ + $(CORE_DIR)/drivers/libretro/libretro-common/encodings/encoding_utf.c \ + $(CORE_DIR)/drivers/libretro/libretro-common/compat/compat_strl.c endif ifeq ($(DEBUG),1) diff --git a/src/drivers/libretro/griffin.c b/src/drivers/libretro/griffin.c index 591c460..0647180 100644 --- a/src/drivers/libretro/griffin.c +++ b/src/drivers/libretro/griffin.c @@ -4,6 +4,9 @@ #ifndef STATIC_LINKING #include "drivers/libretro/libretro-common/streams/memory_stream.c" +#include "drivers/libretro/libretro-common/string/stdstring.c" +#include "drivers/libretro/libretro-common/encodings/encoding_utf.c" +#include "drivers/libretro/libretro-common/compat/compat_strl.c" #endif #include "cart.c" diff --git a/src/drivers/libretro/libretro-common/compat/compat_strl.c b/src/drivers/libretro/libretro-common/compat/compat_strl.c new file mode 100644 index 0000000..94cb39b --- /dev/null +++ b/src/drivers/libretro/libretro-common/compat/compat_strl.c @@ -0,0 +1,69 @@ +/* Copyright (C) 2010-2018 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (compat_strl.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 + +/* Implementation of strlcpy()/strlcat() based on OpenBSD. */ + +#ifndef __MACH__ + +size_t strlcpy(char *dest, const char *source, size_t size) +{ + size_t src_size = 0; + size_t n = size; + + if (n) + while (--n && (*dest++ = *source++)) src_size++; + + if (!n) + { + if (size) *dest = '\0'; + while (*source++) src_size++; + } + + return src_size; +} + +size_t strlcat(char *dest, const char *source, size_t size) +{ + size_t len = strlen(dest); + + dest += len; + + if (len > size) + size = 0; + else + size -= len; + + return len + strlcpy(dest, source, size); +} +#endif + +char *strldup(const char *s, size_t n) +{ + char *dst = (char*)malloc(sizeof(char) * (n + 1)); + strlcpy(dst, s, n); + return dst; +} diff --git a/src/drivers/libretro/libretro-common/encodings/encoding_utf.c b/src/drivers/libretro/libretro-common/encodings/encoding_utf.c new file mode 100644 index 0000000..b6ad2f9 --- /dev/null +++ b/src/drivers/libretro/libretro-common/encodings/encoding_utf.c @@ -0,0 +1,510 @@ +/* Copyright (C) 2010-2018 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (encoding_utf.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 + +#if defined(_WIN32) && !defined(_XBOX) +#include +#elif defined(_XBOX) +#include +#endif + +static unsigned leading_ones(uint8_t c) +{ + unsigned ones = 0; + while (c & 0x80) + { + ones++; + c <<= 1; + } + + return ones; +} + +/* Simple implementation. Assumes the sequence is + * properly synchronized and terminated. */ + +size_t utf8_conv_utf32(uint32_t *out, size_t out_chars, + const char *in, size_t in_size) +{ + unsigned i; + size_t ret = 0; + while (in_size && out_chars) + { + unsigned extra, shift; + uint32_t c; + uint8_t first = *in++; + unsigned ones = leading_ones(first); + + if (ones > 6 || ones == 1) /* Invalid or desync. */ + break; + + extra = ones ? ones - 1 : ones; + if (1 + extra > in_size) /* Overflow. */ + break; + + shift = (extra - 1) * 6; + c = (first & ((1 << (7 - ones)) - 1)) << (6 * extra); + + for (i = 0; i < extra; i++, in++, shift -= 6) + c |= (*in & 0x3f) << shift; + + *out++ = c; + in_size -= 1 + extra; + out_chars--; + ret++; + } + + return ret; +} + +bool utf16_conv_utf8(uint8_t *out, size_t *out_chars, + const uint16_t *in, size_t in_size) +{ + static uint8_t kUtf8Limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + size_t out_pos = 0; + size_t in_pos = 0; + + for (;;) + { + unsigned numAdds; + uint32_t value; + + if (in_pos == in_size) + { + *out_chars = out_pos; + return true; + } + value = in[in_pos++]; + if (value < 0x80) + { + if (out) + out[out_pos] = (char)value; + out_pos++; + continue; + } + + if (value >= 0xD800 && value < 0xE000) + { + uint32_t c2; + + if (value >= 0xDC00 || in_pos == in_size) + break; + c2 = in[in_pos++]; + if (c2 < 0xDC00 || c2 >= 0xE000) + break; + value = (((value - 0xD800) << 10) | (c2 - 0xDC00)) + 0x10000; + } + + for (numAdds = 1; numAdds < 5; numAdds++) + if (value < (((uint32_t)1) << (numAdds * 5 + 6))) + break; + if (out) + out[out_pos] = (char)(kUtf8Limits[numAdds - 1] + + (value >> (6 * numAdds))); + out_pos++; + do + { + numAdds--; + if (out) + out[out_pos] = (char)(0x80 + + ((value >> (6 * numAdds)) & 0x3F)); + out_pos++; + }while (numAdds != 0); + } + + *out_chars = out_pos; + return false; +} + +/* Acts mostly like strlcpy. + * + * Copies the given number of UTF-8 characters, + * but at most d_len bytes. + * + * Always NULL terminates. + * Does not copy half a character. + * + * Returns number of bytes. 's' is assumed valid UTF-8. + * Use only if 'chars' is considerably less than 'd_len'. */ +size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars) +{ + const uint8_t *sb = (const uint8_t*)s; + const uint8_t *sb_org = sb; + + if (!s) + return 0; + + while (*sb && chars-- > 0) + { + sb++; + while ((*sb & 0xC0) == 0x80) sb++; + } + + if ((size_t)(sb - sb_org) > d_len-1 /* NUL */) + { + sb = sb_org + d_len-1; + while ((*sb & 0xC0) == 0x80) sb--; + } + + memcpy(d, sb_org, sb-sb_org); + d[sb-sb_org] = '\0'; + + return sb-sb_org; +} + +const char *utf8skip(const char *str, size_t chars) +{ + const uint8_t *strb = (const uint8_t*)str; + if (!chars) + return str; + do + { + strb++; + while ((*strb & 0xC0)==0x80) strb++; + chars--; + } while(chars); + return (const char*)strb; +} + +size_t utf8len(const char *string) +{ + size_t ret = 0; + + if (!string) + return 0; + + while (*string) + { + if ((*string & 0xC0) != 0x80) + ret++; + string++; + } + return ret; +} + +#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) +{ + uint8_t first = utf8_walkbyte(string); + uint32_t ret = 0; + + if (first < 128) + return first; + + 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); + return ret | (first & 7) << 18; + } + return ret | (first & 15) << 12; + } + + return ret | (first & 31) << 6; +} + +static bool utf16_to_char(uint8_t **utf_data, + size_t *dest_len, const uint16_t *in) +{ + unsigned len = 0; + + while (in[len] != '\0') + len++; + + utf16_conv_utf8(NULL, dest_len, in, len); + *dest_len += 1; + *utf_data = (uint8_t*)malloc(*dest_len); + if (*utf_data == 0) + return false; + + return utf16_conv_utf8(*utf_data, dest_len, in, len); +} + +bool utf16_to_char_string(const uint16_t *in, char *s, size_t len) +{ + size_t dest_len = 0; + uint8_t *utf16_data = NULL; + bool ret = utf16_to_char(&utf16_data, &dest_len, in); + + if (ret) + { + utf16_data[dest_len] = 0; + strlcpy(s, (const char*)utf16_data, len); + } + + free(utf16_data); + utf16_data = NULL; + + 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, + 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 + * 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. + */ + + 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 + return strdup(str); + + if (path_buf_wide) + free(path_buf_wide); + + return NULL; +} +#endif + +/* Returned pointer MUST be freed by the caller if non-NULL. */ +char* utf8_to_local_string_alloc(const char *str) +{ + 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) +{ + 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. */ +wchar_t* utf8_to_utf16_string_alloc(const char *str) +{ +#ifdef _WIN32 + int len = 0; + int out_len = 0; +#else + size_t len = 0; + size_t out_len = 0; +#endif + wchar_t *buf = NULL; + + if (!str || !*str) + return NULL; + +#ifdef _WIN32 + len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); + + if (len) + { + buf = (wchar_t*)calloc(len, sizeof(wchar_t)); + + if (!buf) + return NULL; + + out_len = MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, len); + } + else + { + /* fallback to ANSI codepage instead */ + len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); + + if (len) + { + buf = (wchar_t*)calloc(len, sizeof(wchar_t)); + + if (!buf) + return NULL; + + out_len = MultiByteToWideChar(CP_ACP, 0, str, -1, buf, len); + } + } + + if (out_len < 0) + { + free(buf); + return NULL; + } +#else + /* NOTE: For now, assume non-Windows platforms' locale is already UTF-8. */ + len = mbstowcs(NULL, str, 0) + 1; + + if (len) + { + buf = (wchar_t*)calloc(len, sizeof(wchar_t)); + + if (!buf) + return NULL; + + out_len = mbstowcs(buf, str, len); + } + + if (out_len == (size_t)-1) + { + free(buf); + return NULL; + } +#endif + + return buf; +} + +/* Returned pointer MUST be freed by the caller if non-NULL. */ +char* utf16_to_utf8_string_alloc(const wchar_t *str) +{ +#ifdef _WIN32 + int len = 0; +#else + size_t len = 0; +#endif + char *buf = NULL; + + if (!str || !*str) + return NULL; + +#ifdef _WIN32 + { + 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; + + if (WideCharToMultiByte(code_page, + 0, str, -1, buf, len, NULL, NULL) < 0) + { + free(buf); + return NULL; + } + } +#else + /* NOTE: For now, assume non-Windows platforms' + * locale is already UTF-8. */ + len = wcstombs(NULL, str, 0) + 1; + + if (len) + { + buf = (char*)calloc(len, sizeof(char)); + + if (!buf) + return NULL; + + if (wcstombs(buf, str, len) == (size_t)-1) + { + free(buf); + return NULL; + } + } +#endif + + return buf; +} diff --git a/src/drivers/libretro/libretro-common/include/boolean.h b/src/drivers/libretro/libretro-common/include/boolean.h new file mode 100644 index 0000000..f06ac5a --- /dev/null +++ b/src/drivers/libretro/libretro-common/include/boolean.h @@ -0,0 +1,39 @@ +/* Copyright (C) 2010-2018 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (boolean.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_BOOLEAN_H +#define __LIBRETRO_SDK_BOOLEAN_H + +#ifndef __cplusplus + +#if defined(_MSC_VER) && _MSC_VER < 1800 && !defined(SN_TARGET_PS3) +/* Hack applied for MSVC when compiling in C89 mode as it isn't C99 compliant. */ +#define bool unsigned char +#define true 1 +#define false 0 +#else +#include +#endif + +#endif + +#endif diff --git a/src/drivers/libretro/libretro-common/include/compat/msvc.h b/src/drivers/libretro/libretro-common/include/compat/msvc.h index 4681b12..1c24263 100644 --- a/src/drivers/libretro/libretro-common/include/compat/msvc.h +++ b/src/drivers/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__ @@ -41,6 +42,7 @@ extern "C" { /* 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 diff --git a/src/drivers/libretro/libretro-common/include/compat/strl.h b/src/drivers/libretro/libretro-common/include/compat/strl.h new file mode 100644 index 0000000..c70f119 --- /dev/null +++ b/src/drivers/libretro/libretro-common/include/compat/strl.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2010-2018 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (strl.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_COMPAT_STRL_H +#define __LIBRETRO_SDK_COMPAT_STRL_H + +#include +#include + +#if defined(RARCH_INTERNAL) && defined(HAVE_CONFIG_H) +#include "../../../config.h" +#endif + +#include + +RETRO_BEGIN_DECLS + +#ifdef __MACH__ +#ifndef HAVE_STRL +#define HAVE_STRL +#endif +#endif + +#ifndef HAVE_STRL +/* Avoid possible naming collisions during link since + * we prefer to use the actual name. */ +#define strlcpy(dst, src, size) strlcpy_retro__(dst, src, size) + +#define strlcat(dst, src, size) strlcat_retro__(dst, src, size) + +size_t strlcpy(char *dest, const char *source, size_t size); +size_t strlcat(char *dest, const char *source, size_t size); + +#endif + +char *strldup(const char *s, size_t n); + +RETRO_END_DECLS + +#endif diff --git a/src/drivers/libretro/libretro-common/include/encodings/base64.h b/src/drivers/libretro/libretro-common/include/encodings/base64.h new file mode 100644 index 0000000..11658d7 --- /dev/null +++ b/src/drivers/libretro/libretro-common/include/encodings/base64.h @@ -0,0 +1,48 @@ +#ifndef _LIBRETRO_ENCODINGS_BASE64_H +#define _LIBRETRO_ENCODINGS_BASE64_H + +#include +#include + +#include + +RETRO_BEGIN_DECLS + +const static char* b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/* maps A=>0,B=>1.. */ +const static unsigned char unb64[]={ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 62, 0, 0, 0, 63, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, +}; /* This array has 256 elements */ + +char* base64(const void* binaryData, int len, int *flen); +unsigned char* unbase64(const char* ascii, int len, int *flen); + +RETRO_END_DECLS + +#endif diff --git a/src/drivers/libretro/libretro-common/include/encodings/crc32.h b/src/drivers/libretro/libretro-common/include/encodings/crc32.h new file mode 100644 index 0000000..25e9262 --- /dev/null +++ b/src/drivers/libretro/libretro-common/include/encodings/crc32.h @@ -0,0 +1,38 @@ +/* Copyright (C) 2010-2018 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (crc32.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_ENCODINGS_CRC32_H +#define _LIBRETRO_ENCODINGS_CRC32_H + +#include +#include + +#include + +RETRO_BEGIN_DECLS + +uint32_t encoding_crc32(uint32_t crc, const uint8_t *buf, size_t len); +uint32_t file_crc32(uint32_t crc, const char *path); + +RETRO_END_DECLS + +#endif diff --git a/src/drivers/libretro/libretro-common/include/encodings/utf.h b/src/drivers/libretro/libretro-common/include/encodings/utf.h new file mode 100644 index 0000000..b513f28 --- /dev/null +++ b/src/drivers/libretro/libretro-common/include/encodings/utf.h @@ -0,0 +1,67 @@ +/* Copyright (C) 2010-2018 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (utf.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_ENCODINGS_UTF_H +#define _LIBRETRO_ENCODINGS_UTF_H + +#include +#include + +#include + +#include + +RETRO_BEGIN_DECLS + +enum CodePage +{ + CODEPAGE_LOCAL = 0, /* CP_ACP */ + CODEPAGE_UTF8 = 65001 /* CP_UTF8 */ +}; + +size_t utf8_conv_utf32(uint32_t *out, size_t out_chars, + const char *in, size_t in_size); + +bool utf16_conv_utf8(uint8_t *out, size_t *out_chars, + const uint16_t *in, size_t in_size); + +size_t utf8len(const char *string); + +size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars); + +const char *utf8skip(const char *str, size_t chars); + +uint32_t utf8_walk(const char **string); + +bool utf16_to_char_string(const uint16_t *in, char *s, size_t len); + +char* utf8_to_local_string_alloc(const char *str); + +char* local_to_utf8_string_alloc(const char *str); + +wchar_t* utf8_to_utf16_string_alloc(const char *str); + +char* utf16_to_utf8_string_alloc(const wchar_t *str); + +RETRO_END_DECLS + +#endif diff --git a/src/drivers/libretro/libretro-common/include/encodings/win32.h b/src/drivers/libretro/libretro-common/include/encodings/win32.h new file mode 100644 index 0000000..d4221bd --- /dev/null +++ b/src/drivers/libretro/libretro-common/include/encodings/win32.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2010-2018 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (utf.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_ENCODINGS_WIN32_H +#define _LIBRETRO_ENCODINGS_WIN32_H + +#ifndef _XBOX +#ifdef _WIN32 +/*#define UNICODE +#include +#include */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifdef __cplusplus +} +#endif + +#endif +#endif + +#ifdef UNICODE +#define CHAR_TO_WCHAR_ALLOC(s, ws) \ + size_t ws##_size = (NULL != s && s[0] ? strlen(s) : 0) + 1; \ + wchar_t *ws = (wchar_t*)calloc(ws##_size, 2); \ + if (NULL != s && s[0]) \ + MultiByteToWideChar(CP_UTF8, 0, s, -1, ws, ws##_size / sizeof(wchar_t)); + +#define WCHAR_TO_CHAR_ALLOC(ws, s) \ + size_t s##_size = ((NULL != ws && ws[0] ? wcslen((const wchar_t*)ws) : 0) / 2) + 1; \ + char *s = (char*)calloc(s##_size, 1); \ + if (NULL != ws && ws[0]) \ + utf16_to_char_string((const uint16_t*)ws, s, s##_size); + +#else +#define CHAR_TO_WCHAR_ALLOC(s, ws) char *ws = (NULL != s && s[0] ? strdup(s) : NULL); +#define WCHAR_TO_CHAR_ALLOC(ws, s) char *s = (NULL != ws && ws[0] ? strdup(ws) : NULL); +#endif + +#endif diff --git a/src/drivers/libretro/libretro-common/include/libretro.h b/src/drivers/libretro/libretro-common/include/libretro.h index 1fd2f5b..4ddb6e8 100644 --- a/src/drivers/libretro/libretro-common/include/libretro.h +++ b/src/drivers/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_ENHANCED_CORE_OPTIONS - * returns an API version of 1. + * This should only be called if RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION + * 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,8 +1169,6 @@ 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", @@ -1196,8 +1194,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_ENHANCED_CORE_OPTIONS - * returns an API version of 1. + * This should only be called if RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION + * 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). @@ -1248,6 +1246,47 @@ 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 + */ + +#define RETRO_ENVIRONMENT_GET_DISK_CONTROL_INTERFACE_VERSION 57 + /* unsigned * -- + * Unsigned value is the API version number of the disk control + * interface supported by the frontend. If callback return false, + * API version is assumed to be 0. + * + * In legacy code, the disk control interface is defined by passing + * a struct of type retro_disk_control_callback to + * RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE. + * This may be still be done regardless of the disk control + * interface version. + * + * If version is >= 1 however, the disk control interface may + * instead be defined by passing a struct of type + * retro_disk_control_ext_callback to + * RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE. + * This allows the core to provide additional information about + * disk images to the frontend and/or enables extra + * disk control functionality by the frontend. + */ + +#define RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE 58 + /* const struct retro_disk_control_ext_callback * -- + * Sets an interface which frontend can use to eject and insert + * disk images, and also obtain information about individual + * disk image files registered by the core. + * This is used for games which consist of multiple images and + * must be manually swapped out by the user (e.g. PSX, floppy disk + * based systems). + */ + /* VFS functionality */ /* File paths: @@ -1924,6 +1963,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 }; @@ -1932,6 +1975,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); @@ -2289,7 +2336,8 @@ struct retro_keyboard_callback retro_keyboard_event_t callback; }; -/* Callbacks for RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE. +/* Callbacks for RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE & + * RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE. * Should be set for implementations which can swap out multiple disk * images in runtime. * @@ -2347,6 +2395,53 @@ typedef bool (RETRO_CALLCONV *retro_replace_image_index_t)(unsigned index, * with replace_image_index. */ typedef bool (RETRO_CALLCONV *retro_add_image_index_t)(void); +/* Sets initial image to insert in drive when calling + * core_load_game(). + * Since we cannot pass the initial index when loading + * content (this would require a major API change), this + * is set by the frontend *before* calling the core's + * retro_load_game()/retro_load_game_special() implementation. + * A core should therefore cache the index/path values and handle + * them inside retro_load_game()/retro_load_game_special(). + * - If 'index' is invalid (index >= get_num_images()), the + * core should ignore the set value and instead use 0 + * - 'path' is used purely for error checking - i.e. when + * content is loaded, the core should verify that the + * disk specified by 'index' has the specified file path. + * This is to guard against auto selecting the wrong image + * if (for example) the user should modify an existing M3U + * playlist. We have to let the core handle this because + * set_initial_image() must be called before loading content, + * i.e. the frontend cannot access image paths in advance + * and thus cannot perform the error check itself. + * If set path and content path do not match, the core should + * ignore the set 'index' value and instead use 0 + * Returns 'false' if index or 'path' are invalid, or core + * does not support this functionality + */ +typedef bool (RETRO_CALLCONV *retro_set_initial_image_t)(unsigned index, const char *path); + +/* Fetches the path of the specified disk image file. + * Returns 'false' if index is invalid (index >= get_num_images()) + * or path is otherwise unavailable. + */ +typedef bool (RETRO_CALLCONV *retro_get_image_path_t)(unsigned index, char *path, size_t len); + +/* Fetches a core-provided 'label' for the specified disk + * image file. In the simplest case this may be a file name + * (without extension), but for cores with more complex + * content requirements information may be provided to + * facilitate user disk swapping - for example, a core + * running floppy-disk-based content may uniquely label + * save disks, data disks, level disks, etc. with names + * corresponding to in-game disk change prompts (so the + * frontend can provide better user guidance than a 'dumb' + * disk index value). + * Returns 'false' if index is invalid (index >= get_num_images()) + * or label is otherwise unavailable. + */ +typedef bool (RETRO_CALLCONV *retro_get_image_label_t)(unsigned index, char *label, size_t len); + struct retro_disk_control_callback { retro_set_eject_state_t set_eject_state; @@ -2360,6 +2455,27 @@ struct retro_disk_control_callback retro_add_image_index_t add_image_index; }; +struct retro_disk_control_ext_callback +{ + retro_set_eject_state_t set_eject_state; + retro_get_eject_state_t get_eject_state; + + retro_get_image_index_t get_image_index; + retro_set_image_index_t set_image_index; + retro_get_num_images_t get_num_images; + + retro_replace_image_index_t replace_image_index; + retro_add_image_index_t add_image_index; + + /* NOTE: Frontend will only attempt to record/restore + * last used disk index if both set_initial_image() + * and get_image_path() are implemented */ + retro_set_initial_image_t set_initial_image; /* Optional - may be NULL */ + + retro_get_image_path_t get_image_path; /* Optional - may be NULL */ + retro_get_image_label_t get_image_label; /* Optional - may be NULL */ +}; + enum retro_pixel_format { /* 0RGB1555, native endian. @@ -2504,8 +2620,20 @@ struct retro_core_option_display }; /* Maximum number of values permitted for a core option - * NOTE: This may be increased on a core-by-core basis - * if required (doing so has no effect on the frontend) */ + * > 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." + */ #define RETRO_NUM_CORE_OPTION_VALUES_MAX 128 struct retro_core_option_value diff --git a/src/drivers/libretro/libretro-common/include/retro_common_api.h b/src/drivers/libretro/libretro-common/include/retro_common_api.h index fc4f745..d784842 100644 --- a/src/drivers/libretro/libretro-common/include/retro_common_api.h +++ b/src/drivers/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 diff --git a/src/drivers/libretro/libretro-common/include/streams/memory_stream.h b/src/drivers/libretro/libretro-common/include/streams/memory_stream.h index c594808..f9e641b 100644 --- a/src/drivers/libretro/libretro-common/include/streams/memory_stream.h +++ b/src/drivers/libretro/libretro-common/include/streams/memory_stream.h @@ -56,6 +56,8 @@ void memstream_set_buffer(uint8_t *buffer, uint64_t size); uint64_t memstream_get_last_size(void); +uint64_t memstream_get_ptr(memstream_t *stream); + RETRO_END_DECLS #endif diff --git a/src/drivers/libretro/libretro-common/include/string/stdstring.h b/src/drivers/libretro/libretro-common/include/string/stdstring.h new file mode 100644 index 0000000..d57256a --- /dev/null +++ b/src/drivers/libretro/libretro-common/include/string/stdstring.h @@ -0,0 +1,151 @@ +/* Copyright (C) 2010-2019 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (stdstring.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_STDSTRING_H +#define __LIBRETRO_SDK_STDSTRING_H + +#include +#include +#include +#include +#include + +#include +#include +#include + +RETRO_BEGIN_DECLS + +static INLINE bool string_is_empty(const char *data) +{ + return !data || (*data == '\0'); +} + +static INLINE bool string_is_equal(const char *a, const char *b) +{ + return (a && b) ? !strcmp(a, b) : false; +} + +#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) +{ + 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 true; + + while ((result = tolower (*p1) - tolower (*p2++)) == 0) + if (*p1++ == '\0') + break; + + 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); + +char *string_ucwords(char *s); + +char *string_replace_substring(const char *in, const char *pattern, + const char *by); + +/* Remove leading whitespaces */ +char *string_trim_whitespace_left(char *const s); + +/* Remove trailing whitespaces */ +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); + +/* 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 + +#endif diff --git a/src/drivers/libretro/libretro-common/streams/memory_stream.c b/src/drivers/libretro/libretro-common/streams/memory_stream.c index 77a2c00..829ef03 100644 --- a/src/drivers/libretro/libretro-common/streams/memory_stream.c +++ b/src/drivers/libretro/libretro-common/streams/memory_stream.c @@ -71,7 +71,7 @@ static void memstream_init(memstream_t *stream, memstream_t *memstream_open(unsigned writing) { - memstream_t *stream; + memstream_t *stream; if (!g_buffer || !g_size) return NULL; @@ -92,6 +92,11 @@ void memstream_close(memstream_t *stream) free(stream); } +uint64_t memstream_get_ptr(memstream_t *stream) +{ + return stream->ptr; +} + uint64_t memstream_read(memstream_t *stream, void *data, uint64_t bytes) { uint64_t avail = 0; diff --git a/src/drivers/libretro/libretro-common/string/stdstring.c b/src/drivers/libretro/libretro-common/string/stdstring.c new file mode 100644 index 0000000..e2afef1 --- /dev/null +++ b/src/drivers/libretro/libretro-common/string/stdstring.c @@ -0,0 +1,384 @@ +/* Copyright (C) 2010-2018 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (stdstring.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 + +char *string_to_upper(char *s) +{ + char *cs = (char *)s; + for ( ; *cs != '\0'; cs++) + *cs = toupper((unsigned char)*cs); + return s; +} + +char *string_to_lower(char *s) +{ + char *cs = (char *)s; + for ( ; *cs != '\0'; cs++) + *cs = tolower((unsigned char)*cs); + return s; +} + +char *string_ucwords(char *s) +{ + char *cs = (char *)s; + for ( ; *cs != '\0'; cs++) + { + if (*cs == ' ') + *(cs+1) = toupper((unsigned char)*(cs+1)); + } + + s[0] = toupper((unsigned char)s[0]); + return s; +} + +char *string_replace_substring(const char *in, + const char *pattern, const char *replacement) +{ + size_t numhits, pattern_len, replacement_len, outlen; + const char *inat = NULL; + const char *inprev = NULL; + char *out = NULL; + char *outat = NULL; + + /* if either pattern or replacement is NULL, + * duplicate in and let caller handle it. */ + if (!pattern || !replacement) + return strdup(in); + + pattern_len = strlen(pattern); + replacement_len = strlen(replacement); + numhits = 0; + inat = in; + + while ((inat = strstr(inat, pattern))) + { + inat += pattern_len; + numhits++; + } + + outlen = strlen(in) - pattern_len*numhits + replacement_len*numhits; + out = (char *)malloc(outlen+1); + + if (!out) + return NULL; + + outat = out; + inat = in; + inprev = in; + + while ((inat = strstr(inat, pattern))) + { + memcpy(outat, inprev, inat-inprev); + outat += inat-inprev; + memcpy(outat, replacement, replacement_len); + outat += replacement_len; + inat += pattern_len; + inprev = inat; + } + strcpy(outat, inprev); + + return out; +} + +/* Remove leading whitespaces */ +char *string_trim_whitespace_left(char *const s) +{ + if(s && *s) + { + size_t len = strlen(s); + char *current = s; + + while(*current && isspace((unsigned char)*current)) + { + ++current; + --len; + } + + if(s != current) + memmove(s, current, len + 1); + } + + return s; +} + +/* Remove trailing whitespaces */ +char *string_trim_whitespace_right(char *const s) +{ + if(s && *s) + { + size_t len = strlen(s); + char *current = s + len - 1; + + while(current != s && isspace((unsigned char)*current)) + { + --current; + --len; + } + + current[isspace((unsigned char)*current) ? 0 : 1] = '\0'; + } + + return s; +} + +/* Remove leading and trailing whitespaces */ +char *string_trim_whitespace(char *const s) +{ + string_trim_whitespace_right(s); /* order matters */ + string_trim_whitespace_left(s); + + return s; +} + +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 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++) + { + const char *character; + unsigned char_len; + unsigned j = i; + + /* check if end of string reached */ + if (i == len) + { + buffer[i] = 0; + return buffer; + } + + character = utf8skip(&string[i], 1); + char_len = (unsigned)(character - &string[i]); + + 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 ((max_lines == 0 || lines < max_lines)) + { + buffer[i] = '\n'; + i++; + lines++; + } + } + 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; + + 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); +}