diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..5761abcf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.o diff --git a/Makefile.common b/Makefile.common index 23b3c428..867f7550 100644 --- a/Makefile.common +++ b/Makefile.common @@ -18,6 +18,12 @@ endif SOURCES_C := $(LIBRETRO_DIR)/libretro.c \ $(LIBRETRO_DIR)/libretro_sound.c \ + $(LIBRETRO_COMM_DIR)/compat/compat_strcasestr.c \ + $(LIBRETRO_COMM_DIR)/encodings/encoding_utf.c \ + $(LIBRETRO_COMM_DIR)/compat/compat_strl.c \ + $(LIBRETRO_COMM_DIR)/compat/compat_posix_string.c \ + $(LIBRETRO_COMM_DIR)/string/stdstring.c \ + $(LIBRETRO_COMM_DIR)/file/file_path.c SOURCES_C += $(DEPS_DIR)/libmad/bit.c \ $(DEPS_DIR)/libmad/decoder.c \ diff --git a/libretro/libretro-common/compat/compat_posix_string.c b/libretro/libretro-common/compat/compat_posix_string.c new file mode 100644 index 00000000..4bddf11f --- /dev/null +++ b/libretro/libretro-common/compat/compat_posix_string.c @@ -0,0 +1,104 @@ +/* Copyright (C) 2010-2017 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (compat_posix_string.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 + +#ifdef _WIN32 + +#undef strcasecmp +#undef strdup +#undef isblank +#undef strtok_r +#include +#include +#include +#include + +#include + +int retro_strcasecmp__(const char *a, const char *b) +{ + while (*a && *b) + { + int a_ = tolower(*a); + int b_ = tolower(*b); + + if (a_ != b_) + return a_ - b_; + + a++; + b++; + } + + return tolower(*a) - tolower(*b); +} + +char *retro_strdup__(const char *orig) +{ + size_t len = strlen(orig) + 1; + char *ret = (char*)malloc(len); + if (!ret) + return NULL; + + strlcpy(ret, orig, len); + return ret; +} + +int retro_isblank__(int c) +{ + return (c == ' ') || (c == '\t'); +} + +char *retro_strtok_r__(char *str, const char *delim, char **saveptr) +{ + char *first = NULL; + if (!saveptr || !delim) + return NULL; + + if (str) + *saveptr = str; + + do + { + char *ptr = NULL; + first = *saveptr; + while (*first && strchr(delim, *first)) + *first++ = '\0'; + + if (*first == '\0') + return NULL; + + ptr = first + 1; + + while (*ptr && !strchr(delim, *ptr)) + ptr++; + + *saveptr = ptr + (*ptr ? 1 : 0); + *ptr = '\0'; + } while (strlen(first) == 0); + + return first; +} + +#endif diff --git a/libretro/libretro-common/compat/compat_strcasestr.c b/libretro/libretro-common/compat/compat_strcasestr.c new file mode 100644 index 00000000..82ce5acb --- /dev/null +++ b/libretro/libretro-common/compat/compat_strcasestr.c @@ -0,0 +1,58 @@ +/* Copyright (C) 2010-2017 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (compat_strcasestr.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 + +/* Pretty much strncasecmp. */ +static int casencmp(const char *a, const char *b, size_t n) +{ + size_t i; + + for (i = 0; i < n; i++) + { + int a_lower = tolower(a[i]); + int b_lower = tolower(b[i]); + if (a_lower != b_lower) + return a_lower - b_lower; + } + + return 0; +} + +char *strcasestr_retro__(const char *haystack, const char *needle) +{ + size_t i, search_off; + size_t hay_len = strlen(haystack); + size_t needle_len = strlen(needle); + + if (needle_len > hay_len) + return NULL; + + search_off = hay_len - needle_len; + for (i = 0; i <= search_off; i++) + if (!casencmp(haystack + i, needle, needle_len)) + return (char*)haystack + i; + + return NULL; +} diff --git a/libretro/libretro-common/compat/compat_strl.c b/libretro/libretro-common/compat/compat_strl.c new file mode 100644 index 00000000..709982ff --- /dev/null +++ b/libretro/libretro-common/compat/compat_strl.c @@ -0,0 +1,65 @@ +/* Copyright (C) 2010-2015 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 + +#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 diff --git a/libretro/libretro-common/encodings/encoding_utf.c b/libretro/libretro-common/encodings/encoding_utf.c new file mode 100644 index 00000000..d6c4ff46 --- /dev/null +++ b/libretro/libretro-common/encodings/encoding_utf.c @@ -0,0 +1,516 @@ +/* Copyright (C) 2010-2017 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; +} + +static uint8_t utf8_walkbyte(const char **string) +{ + return *((*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); + + if (first >= 0xF0) + return ret | (first & 7) << 18; + if (first >= 0xE0) + 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; +} + +/* 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 = 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. + */ + path_buf_wide_len = MultiByteToWideChar(cp_in, 0, str, -1, NULL, 0); + + 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 +#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); +} + +/* 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); +} + +/* 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; + int out_len = 0; +#else + size_t len = 0; + size_t out_len = 0; +#endif + char *buf = NULL; + + if (!str || !*str) + return NULL; + +#ifdef _WIN32 + len = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL); + + if (len) + { + 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) + { + buf = (char*)calloc(len, sizeof(char)); + + if (!buf) + return NULL; + + out_len = WideCharToMultiByte(CP_ACP, 0, str, -1, buf, len, NULL, NULL); + } + } + + if (out_len < 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; + + out_len = wcstombs(buf, str, len); + } + + if (out_len == (size_t)-1) + { + free(buf); + return NULL; + } +#endif + + return buf; +} diff --git a/libretro/libretro-common/file/file_path.c b/libretro/libretro-common/file/file_path.c new file mode 100644 index 00000000..48308987 --- /dev/null +++ b/libretro/libretro-common/file/file_path.c @@ -0,0 +1,1203 @@ +/* Copyright (C) 2010-2017 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (file_path.c). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#endif +#ifdef __HAIKU__ +#include +#endif +#ifndef __MACH__ +#include +#include +#endif +#include +#include +#include + +#if defined(_WIN32) +#ifdef _MSC_VER +#define setmode _setmode +#endif +#include +#ifdef _XBOX +#include +#define INVALID_FILE_ATTRIBUTES -1 +#else +#include +#include +#include +#include +#if defined(_MSC_VER) && _MSC_VER <= 1200 +#define INVALID_FILE_ATTRIBUTES ((DWORD)-1) +#endif +#endif +#elif defined(VITA) +#define SCE_ERROR_ERRNO_EEXIST 0x80010011 +#include +#include +#include +#else +#include +#include +#include +#endif + +#if defined(PSP) +#include +#endif + +#if defined(__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) + +#ifndef LEGACY_WIN32 +#define LEGACY_WIN32 +#endif + +#endif + +enum stat_mode +{ + IS_DIRECTORY = 0, + IS_CHARACTER_SPECIAL, + IS_VALID +}; + +static bool path_stat(const char *path, enum stat_mode mode, int32_t *size) +{ +#if defined(VITA) || defined(PSP) + SceIoStat buf; + char *tmp = strdup(path); + size_t len = strlen(tmp); + if (tmp[len-1] == '/') + tmp[len-1]='\0'; + + if (sceIoGetstat(tmp, &buf) < 0) + { + free(tmp); + return false; + } + free(tmp); + +#elif defined(__CELLOS_LV2__) + CellFsStat buf; + if (cellFsStat(path, &buf) < 0) + return false; +#elif defined(_WIN32) + struct _stat buf; + char *path_local; + wchar_t *path_wide; + DWORD file_info; + + if (!path || !*path) + return false; + + (void)path_wide; + (void)path_local; + (void)file_info; + +#if defined(LEGACY_WIN32) + path_local = utf8_to_local_string_alloc(path); + file_info = GetFileAttributes(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 false; +#else + struct stat buf; + if (stat(path, &buf) < 0) + return false; +#endif + + if (size) + *size = (int32_t)buf.st_size; + + switch (mode) + { + case IS_DIRECTORY: +#if defined(VITA) || defined(PSP) + return FIO_S_ISDIR(buf.st_mode); +#elif defined(__CELLOS_LV2__) + return ((buf.st_mode & S_IFMT) == S_IFDIR); +#elif defined(_WIN32) + return (file_info & FILE_ATTRIBUTE_DIRECTORY); +#else + return S_ISDIR(buf.st_mode); +#endif + case IS_CHARACTER_SPECIAL: +#if defined(VITA) || defined(PSP) || defined(__CELLOS_LV2__) || defined(_WIN32) + return false; +#else + return S_ISCHR(buf.st_mode); +#endif + case IS_VALID: + return true; + } + + return false; +} + +/** + * path_is_directory: + * @path : path + * + * Checks if path is a directory. + * + * Returns: true (1) if path is a directory, otherwise false (0). + */ +bool path_is_directory(const char *path) +{ + return path_stat(path, IS_DIRECTORY, NULL); +} + +bool path_is_character_special(const char *path) +{ + return path_stat(path, IS_CHARACTER_SPECIAL, NULL); +} + +bool path_is_valid(const char *path) +{ + return path_stat(path, IS_VALID, NULL); +} + +int32_t path_get_size(const char *path) +{ + int32_t filesize = 0; + if (path_stat(path, IS_VALID, &filesize)) + return filesize; + + return -1; +} + +static bool path_mkdir_error(int ret) +{ +#if defined(VITA) + return (ret == SCE_ERROR_ERRNO_EEXIST); +#elif defined(PSP) || defined(_3DS) || defined(WIIU) + return (ret == -1); +#else + return (ret < 0 && errno == EEXIST); +#endif +} + +/** + * path_mkdir: + * @dir : directory + * + * Create directory on filesystem. + * + * Returns: true (1) if directory could be created, otherwise false (0). + **/ +bool path_mkdir(const char *dir) +{ + /* Use heap. Real chance of stack overflow if we recurse too hard. */ + const char *target = NULL; + bool sret = false; + bool norecurse = false; + char *basedir = NULL; + + if (dir && *dir) + basedir = strdup(dir); + + if (!basedir) + return false; + + path_parent_dir(basedir); + if (!*basedir || !strcmp(basedir, dir)) + goto end; + + if (path_is_directory(basedir)) + { + target = dir; + norecurse = true; + } + else + { + target = basedir; + sret = path_mkdir(basedir); + + if (sret) + { + target = dir; + norecurse = true; + } + } + + if (norecurse) + { +#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(__QNX__) + int ret = mkdir(dir, 0777); +#else + int ret = mkdir(dir, 0750); +#endif + + /* Don't treat this as an error. */ + if (path_mkdir_error(ret) && path_is_directory(dir)) + ret = 0; + + if (ret < 0) + printf("mkdir(%s) error: %s.\n", dir, strerror(errno)); + sret = (ret == 0); + } + +end: + if (target && !sret) + printf("Failed to create directory: \"%s\".\n", target); + free(basedir); + return sret; +} + +/** + * path_get_archive_delim: + * @path : path + * + * Find delimiter of an archive file. Only the first '#' + * after a compression extension is considered. + * + * Returns: pointer to the delimiter in the path if it contains + * a path inside a compressed file, otherwise NULL. + */ +const char *path_get_archive_delim(const char *path) +{ + const char *last = find_last_slash(path); + const char *delim = NULL; + + if (last) + { + delim = strcasestr(last, ".zip#"); + + if (!delim) + delim = strcasestr(last, ".apk#"); + } + + if (delim) + return delim + 4; + + if (last) + delim = strcasestr(last, ".7z#"); + + if (delim) + return delim + 3; + + return NULL; +} + +/** + * path_get_extension: + * @path : path + * + * Gets extension of file. Only '.'s + * after the last slash are considered. + * + * Returns: extension part from the path. + */ +const char *path_get_extension(const char *path) +{ + const char *ext = !string_is_empty(path) + ? strrchr(path_basename(path), '.') : NULL; + if (!ext) + return ""; + return ext + 1; +} + +/** + * path_remove_extension: + * @path : path + * + * Removes the extension from the path and returns the result. + * Removes all text after and including the last '.'. + * Only '.'s after the last slash are considered. + * + * Returns: path with the extension part removed. + */ +char *path_remove_extension(char *path) +{ + char *last = !string_is_empty(path) + ? (char*)strrchr(path_basename(path), '.') : NULL; + if (!last) + return NULL; + if (*last) + *last = '\0'; + return last; +} + +/** + * path_is_compressed_file: + * @path : path + * + * Checks if path is a compressed file. + * + * Returns: true (1) if path is a compressed file, otherwise false (0). + **/ +bool path_is_compressed_file(const char* path) +{ + const char *ext = path_get_extension(path); + + if ( strcasestr(ext, "zip") + || strcasestr(ext, "apk") + || strcasestr(ext, "7z")) + return true; + + return false; +} + +/** + * fill_pathname: + * @out_path : output path + * @in_path : input path + * @replace : what to replace + * @size : buffer size of output path + * + * FIXME: Verify + * + * Replaces filename extension with 'replace' and outputs result to out_path. + * The extension here is considered to be the string from the last '.' + * to the end. + * + * Only '.'s after the last slash are considered as extensions. + * If no '.' is present, in_path and replace will simply be concatenated. + * 'size' is buffer size of 'out_path'. + * E.g.: in_path = "/foo/bar/baz/boo.c", replace = ".asm" => + * out_path = "/foo/bar/baz/boo.asm" + * E.g.: in_path = "/foo/bar/baz/boo.c", replace = "" => + * out_path = "/foo/bar/baz/boo" + */ +void fill_pathname(char *out_path, const char *in_path, + const char *replace, size_t size) +{ + char tmp_path[PATH_MAX_LENGTH]; + char *tok = NULL; + + tmp_path[0] = '\0'; + + strlcpy(tmp_path, in_path, sizeof(tmp_path)); + if ((tok = (char*)strrchr(path_basename(tmp_path), '.'))) + *tok = '\0'; + + fill_pathname_noext(out_path, tmp_path, replace, size); +} + +/** + * fill_pathname_noext: + * @out_path : output path + * @in_path : input path + * @replace : what to replace + * @size : buffer size of output path + * + * Appends a filename extension 'replace' to 'in_path', and outputs + * result in 'out_path'. + * + * Assumes in_path has no extension. If an extension is still + * present in 'in_path', it will be ignored. + * + */ +void fill_pathname_noext(char *out_path, const char *in_path, + const char *replace, size_t size) +{ + strlcpy(out_path, in_path, size); + strlcat(out_path, replace, size); +} + +char *find_last_slash(const char *str) +{ + const char *slash = strrchr(str, '/'); +#ifdef _WIN32 + const char *backslash = strrchr(str, '\\'); + + if (backslash && ((slash && backslash > slash) || !slash)) + slash = backslash; +#endif + + return (char*)slash; +} + +/** + * fill_pathname_slash: + * @path : path + * @size : size of path + * + * Assumes path is a directory. Appends a slash + * if not already there. + **/ +void fill_pathname_slash(char *path, size_t size) +{ + size_t path_len = strlen(path); + const char *last_slash = find_last_slash(path); + + /* Try to preserve slash type. */ + if (last_slash && (last_slash != (path + path_len - 1))) + { + char join_str[2]; + + join_str[0] = '\0'; + + strlcpy(join_str, last_slash, sizeof(join_str)); + strlcat(path, join_str, size); + } + else if (!last_slash) + strlcat(path, path_default_slash(), size); +} + +/** + * fill_pathname_dir: + * @in_dir : input directory path + * @in_basename : input basename to be appended to @in_dir + * @replace : replacement to be appended to @in_basename + * @size : size of buffer + * + * Appends basename of 'in_basename', to 'in_dir', along with 'replace'. + * Basename of in_basename is the string after the last '/' or '\\', + * i.e the filename without directories. + * + * If in_basename has no '/' or '\\', the whole 'in_basename' will be used. + * 'size' is buffer size of 'in_dir'. + * + * E.g..: in_dir = "/tmp/some_dir", in_basename = "/some_content/foo.c", + * replace = ".asm" => in_dir = "/tmp/some_dir/foo.c.asm" + **/ +void fill_pathname_dir(char *in_dir, const char *in_basename, + const char *replace, size_t size) +{ + const char *base = NULL; + + fill_pathname_slash(in_dir, size); + base = path_basename(in_basename); + strlcat(in_dir, base, size); + strlcat(in_dir, replace, size); +} + +/** + * fill_pathname_base: + * @out : output path + * @in_path : input path + * @size : size of output path + * + * Copies basename of @in_path into @out_path. + **/ +void fill_pathname_base(char *out, const char *in_path, size_t size) +{ + const char *ptr = path_basename(in_path); + + if (!ptr) + ptr = in_path; + + strlcpy(out, ptr, size); +} + +void fill_pathname_base_noext(char *out, const char *in_path, size_t size) +{ + fill_pathname_base(out, in_path, size); + path_remove_extension(out); +} + +void fill_pathname_base_ext(char *out, const char *in_path, const char *ext, + size_t size) +{ + fill_pathname_base_noext(out, in_path, size); + strlcat(out, ext, size); +} + +/** + * fill_pathname_basedir: + * @out_dir : output directory + * @in_path : input path + * @size : size of output directory + * + * Copies base directory of @in_path into @out_path. + * If in_path is a path without any slashes (relative current directory), + * @out_path will get path "./". + **/ +void fill_pathname_basedir(char *out_dir, + const char *in_path, size_t size) +{ + if (out_dir != in_path) + strlcpy(out_dir, in_path, size); + path_basedir(out_dir); +} + +void fill_pathname_basedir_noext(char *out_dir, + const char *in_path, size_t size) +{ + fill_pathname_basedir(out_dir, in_path, size); + path_remove_extension(out_dir); +} + +/** + * fill_pathname_parent_dir_name: + * @out_dir : output directory + * @in_dir : input directory + * @size : size of output directory + * + * Copies only the parent directory name of @in_dir into @out_dir. + * The two buffers must not overlap. Removes trailing '/'. + * Returns true on success, false if a slash was not found in the path. + **/ +bool fill_pathname_parent_dir_name(char *out_dir, + const char *in_dir, size_t size) +{ + char *temp = strdup(in_dir); + char *last = find_last_slash(temp); + bool ret = false; + + *last = '\0'; + + in_dir = find_last_slash(temp); + + if (in_dir && in_dir + 1) + { + strlcpy(out_dir, in_dir + 1, size); + ret = true; + } + else + ret = false; + + free(temp); + + return ret; +} + +/** + * fill_pathname_parent_dir: + * @out_dir : output directory + * @in_dir : input directory + * @size : size of output directory + * + * Copies parent directory of @in_dir into @out_dir. + * Assumes @in_dir is a directory. Keeps trailing '/'. + **/ +void fill_pathname_parent_dir(char *out_dir, + const char *in_dir, size_t size) +{ + if (out_dir != in_dir) + strlcpy(out_dir, in_dir, size); + path_parent_dir(out_dir); +} + +/** + * fill_dated_filename: + * @out_filename : output filename + * @ext : extension of output filename + * @size : buffer size of output filename + * + * Creates a 'dated' filename prefixed by 'RetroArch', and + * concatenates extension (@ext) to it. + * + * E.g.: + * out_filename = "RetroArch-{month}{day}-{Hours}{Minutes}.{@ext}" + **/ +void fill_dated_filename(char *out_filename, + const char *ext, size_t size) +{ + time_t cur_time = time(NULL); + + strftime(out_filename, size, + "RetroArch-%m%d-%H%M%S.", localtime(&cur_time)); + strlcat(out_filename, ext, size); +} + +/** + * fill_str_dated_filename: + * @out_filename : output filename + * @in_str : input string + * @ext : extension of output filename + * @size : buffer size of output filename + * + * Creates a 'dated' filename prefixed by the string @in_str, and + * concatenates extension (@ext) to it. + * + * E.g.: + * out_filename = "RetroArch-{year}{month}{day}-{Hour}{Minute}{Second}.{@ext}" + **/ +void fill_str_dated_filename(char *out_filename, + const char *in_str, const char *ext, size_t size) +{ + char format[256]; + time_t cur_time = time(NULL); + + format[0] = '\0'; + + strftime(format, sizeof(format), "-%y%m%d-%H%M%S.", localtime(&cur_time)); + + fill_pathname_join_concat_noext(out_filename, + in_str, format, ext, + size); +} + +/** + * path_basedir: + * @path : path + * + * Extracts base directory by mutating path. + * Keeps trailing '/'. + **/ +void path_basedir(char *path) +{ + char *last = NULL; + if (strlen(path) < 2) + return; + + last = find_last_slash(path); + + if (last) + last[1] = '\0'; + else + snprintf(path, 3, ".%s", path_default_slash()); +} + +/** + * path_parent_dir: + * @path : path + * + * Extracts parent directory by mutating path. + * Assumes that path is a directory. Keeps trailing '/'. + **/ +void path_parent_dir(char *path) +{ + size_t len = strlen(path); + if (len && path_char_is_slash(path[len - 1])) + path[len - 1] = '\0'; + path_basedir(path); +} + +/** + * path_basename: + * @path : path + * + * Get basename from @path. + * + * Returns: basename from path. + **/ +const char *path_basename(const char *path) +{ + /* We cut either at the first compression-related hash + * or the last slash; whichever comes last */ + const char *last = find_last_slash(path); + const char *delim = path_get_archive_delim(path); + + if (delim) + return delim + 1; + + if (last) + return last + 1; + + return path; +} + +/** + * path_is_absolute: + * @path : path + * + * Checks if @path is an absolute path or a relative path. + * + * Returns: true if path is absolute, false if path is relative. + **/ +bool path_is_absolute(const char *path) +{ + if (path[0] == '/') + return true; +#ifdef _WIN32 + /* Many roads lead to Rome ... */ + if (( strstr(path, "\\\\") == path) + || strstr(path, ":/") + || strstr(path, ":\\") + || strstr(path, ":\\\\")) + return true; +#elif defined(__wiiu__) + if (strstr(path, ":/")) + return true; +#endif + return false; +} + +/** + * path_resolve_realpath: + * @buf : buffer for path + * @size : size of buffer + * + * Turns relative paths into absolute path. + * If relative, rebases on current working dir. + **/ +void path_resolve_realpath(char *buf, size_t size) +{ +#ifndef RARCH_CONSOLE + char tmp[PATH_MAX_LENGTH]; + + tmp[0] = '\0'; + + strlcpy(tmp, buf, sizeof(tmp)); + +#ifdef _WIN32 + if (!_fullpath(buf, tmp, size)) + strlcpy(buf, tmp, size); +#else + + /* NOTE: realpath() expects at least PATH_MAX_LENGTH bytes in buf. + * Technically, PATH_MAX_LENGTH needn't be defined, but we rely on it anyways. + * POSIX 2008 can automatically allocate for you, + * but don't rely on that. */ + if (!realpath(tmp, buf)) + strlcpy(buf, tmp, size); +#endif +#endif +} + +/** + * fill_pathname_resolve_relative: + * @out_path : output path + * @in_refpath : input reference path + * @in_path : input path + * @size : size of @out_path + * + * Joins basedir of @in_refpath together with @in_path. + * If @in_path is an absolute path, out_path = in_path. + * E.g.: in_refpath = "/foo/bar/baz.a", in_path = "foobar.cg", + * out_path = "/foo/bar/foobar.cg". + **/ +void fill_pathname_resolve_relative(char *out_path, + const char *in_refpath, const char *in_path, size_t size) +{ + if (path_is_absolute(in_path)) + { + strlcpy(out_path, in_path, size); + return; + } + + fill_pathname_basedir(out_path, in_refpath, size); + strlcat(out_path, in_path, size); +} + +/** + * fill_pathname_join: + * @out_path : output path + * @dir : directory + * @path : path + * @size : size of output path + * + * Joins a directory (@dir) and path (@path) together. + * Makes sure not to get two consecutive slashes + * between directory and path. + **/ +void fill_pathname_join(char *out_path, + const char *dir, const char *path, size_t size) +{ + if (out_path != dir) + strlcpy(out_path, dir, size); + + if (*out_path) + fill_pathname_slash(out_path, size); + + strlcat(out_path, path, size); +} + +void fill_pathname_join_special_ext(char *out_path, + const char *dir, const char *path, + const char *last, const char *ext, + size_t size) +{ + fill_pathname_join(out_path, dir, path, size); + if (*out_path) + fill_pathname_slash(out_path, size); + + strlcat(out_path, last, size); + strlcat(out_path, ext, size); +} + +void fill_pathname_join_concat_noext( + char *out_path, + const char *dir, const char *path, + const char *concat, + size_t size) +{ + fill_pathname_noext(out_path, dir, path, size); + strlcat(out_path, concat, size); +} + +void fill_pathname_join_concat(char *out_path, + const char *dir, const char *path, + const char *concat, + size_t size) +{ + fill_pathname_join(out_path, dir, path, size); + strlcat(out_path, concat, size); +} + +void fill_pathname_join_noext(char *out_path, + const char *dir, const char *path, size_t size) +{ + fill_pathname_join(out_path, dir, path, size); + path_remove_extension(out_path); +} + + +/** + * fill_pathname_join_delim: + * @out_path : output path + * @dir : directory + * @path : path + * @delim : delimiter + * @size : size of output path + * + * Joins a directory (@dir) and path (@path) together + * using the given delimiter (@delim). + **/ +void fill_pathname_join_delim(char *out_path, const char *dir, + const char *path, const char delim, size_t size) +{ + size_t copied = strlcpy(out_path, dir, size); + + out_path[copied] = delim; + out_path[copied+1] = '\0'; + + strlcat(out_path, path, size); +} + +void fill_pathname_join_delim_concat(char *out_path, const char *dir, + const char *path, const char delim, const char *concat, + size_t size) +{ + fill_pathname_join_delim(out_path, dir, path, delim, size); + strlcat(out_path, concat, size); +} + +/** + * fill_short_pathname_representation: + * @out_rep : output representation + * @in_path : input path + * @size : size of output representation + * + * Generates a short representation of path. It should only + * be used for displaying the result; the output representation is not + * binding in any meaningful way (for a normal path, this is the same as basename) + * In case of more complex URLs, this should cut everything except for + * the main image file. + * + * E.g.: "/path/to/game.img" -> game.img + * "/path/to/myarchive.7z#folder/to/game.img" -> game.img + */ +void fill_short_pathname_representation(char* out_rep, + const char *in_path, size_t size) +{ + char path_short[PATH_MAX_LENGTH]; + + path_short[0] = '\0'; + + fill_pathname(path_short, path_basename(in_path), "", + sizeof(path_short)); + + strlcpy(out_rep, path_short, size); +} + +void fill_short_pathname_representation_noext(char* out_rep, + const char *in_path, size_t size) +{ + fill_short_pathname_representation(out_rep, in_path, size); + path_remove_extension(out_rep); +} + +void fill_pathname_expand_special(char *out_path, + const char *in_path, size_t size) +{ +#if !defined(RARCH_CONSOLE) + if (*in_path == '~') + { + const char *home = getenv("HOME"); + if (home) + { + size_t src_size = strlcpy(out_path, home, size); + retro_assert(src_size < size); + + out_path += src_size; + size -= src_size; + in_path++; + } + } + else if ((in_path[0] == ':') && + ( + (in_path[1] == '/') +#ifdef _WIN32 + || (in_path[1] == '\\') +#endif + ) + ) + { + size_t src_size; + char *application_dir = (char*)malloc(PATH_MAX_LENGTH * sizeof(char)); + + application_dir[0] = '\0'; + + fill_pathname_application_path(application_dir, + PATH_MAX_LENGTH * sizeof(char)); + path_basedir_wrapper(application_dir); + + src_size = strlcpy(out_path, application_dir, size); + retro_assert(src_size < size); + + free(application_dir); + + out_path += src_size; + size -= src_size; + in_path += 2; + } +#endif + + retro_assert(strlcpy(out_path, in_path, size) < size); +} + +void fill_pathname_abbreviate_special(char *out_path, + const char *in_path, size_t size) +{ +#if !defined(RARCH_CONSOLE) + unsigned i; + const char *candidates[3]; + const char *notations[3]; + char *application_dir = (char*)malloc(PATH_MAX_LENGTH * sizeof(char)); + const char *home = getenv("HOME"); + + application_dir[0] = '\0'; + + /* application_dir could be zero-string. Safeguard against this. + * + * Keep application dir in front of home, moving app dir to a + * new location inside home would break otherwise. */ + + /* ugly hack - use application_dir pointer + * before filling it in. C89 reasons */ + candidates[0] = application_dir; + candidates[1] = home; + candidates[2] = NULL; + + notations [0] = ":"; + notations [1] = "~"; + notations [2] = NULL; + + fill_pathname_application_path(application_dir, + PATH_MAX_LENGTH * sizeof(char)); + path_basedir_wrapper(application_dir); + + for (i = 0; candidates[i]; i++) + { + if (!string_is_empty(candidates[i]) && + strstr(in_path, candidates[i]) == in_path) + { + size_t src_size = strlcpy(out_path, notations[i], size); + + retro_assert(src_size < size); + + out_path += src_size; + size -= src_size; + in_path += strlen(candidates[i]); + + if (!path_char_is_slash(*in_path)) + { + retro_assert(strlcpy(out_path, + path_default_slash(), size) < size); + out_path++; + size--; + } + + break; /* Don't allow more abbrevs to take place. */ + } + } + + free(application_dir); +#endif + + retro_assert(strlcpy(out_path, in_path, size) < size); +} + +/** + * path_basedir: + * @path : path + * + * Extracts base directory by mutating path. + * Keeps trailing '/'. + **/ +void path_basedir_wrapper(char *path) +{ + char *last = NULL; + if (strlen(path) < 2) + return; + +#ifdef HAVE_COMPRESSION + /* We want to find the directory with the archive in basedir. */ + last = (char*)path_get_archive_delim(path); + if (last) + *last = '\0'; +#endif + + last = find_last_slash(path); + + if (last) + last[1] = '\0'; + else + snprintf(path, 3, ".%s", path_default_slash()); +} + +#if !defined(RARCH_CONSOLE) +void fill_pathname_application_path(char *s, size_t len) +{ + size_t i; +#ifdef __APPLE__ + CFBundleRef bundle = CFBundleGetMainBundle(); +#endif +#ifdef _WIN32 + DWORD ret; + wchar_t wstr[PATH_MAX_LENGTH] = {0}; +#endif +#ifdef __HAIKU__ + image_info info; + int32_t cookie = 0; +#endif + (void)i; + + if (!len) + return; + +#ifdef _WIN32 +#ifdef LEGACY_WIN32 + ret = GetModuleFileNameA(GetModuleHandle(NULL), s, len); +#else + ret = GetModuleFileNameW(GetModuleHandle(NULL), wstr, ARRAY_SIZE(wstr)); + + if (*wstr) + { + char *str = utf16_to_utf8_string_alloc(wstr); + + if (str) + { + strlcpy(s, str, len); + free(str); + } + } +#endif + s[ret] = '\0'; +#elif defined(__APPLE__) + if (bundle) + { + CFURLRef bundle_url = CFBundleCopyBundleURL(bundle); + CFStringRef bundle_path = CFURLCopyPath(bundle_url); + CFStringGetCString(bundle_path, s, len, kCFStringEncodingUTF8); + CFRelease(bundle_path); + CFRelease(bundle_url); + + retro_assert(strlcat(s, "nobin", len) < len); + return; + } +#elif defined(__HAIKU__) + while (get_next_image_info(0, &cookie, &info) == B_OK) + { + if (info.type == B_APP_IMAGE) + { + strlcpy(s, info.name, len); + return; + } + } +#elif defined(__QNX__) + char *buff = malloc(len); + + if(_cmdname(buff)) + strlcpy(s, buff, len); + + free(buff); +#else + { + pid_t pid; + static const char *exts[] = { "exe", "file", "path/a.out" }; + char link_path[255]; + + link_path[0] = *s = '\0'; + pid = getpid(); + + /* Linux, BSD and Solaris paths. Not standardized. */ + for (i = 0; i < ARRAY_SIZE(exts); i++) + { + ssize_t ret; + + snprintf(link_path, sizeof(link_path), "/proc/%u/%s", + (unsigned)pid, exts[i]); + ret = readlink(link_path, s, len - 1); + + if (ret >= 0) + { + s[ret] = '\0'; + return; + } + } + } +#endif +} +#endif diff --git a/libretro/libretro-common/include/compat/posix_string.h b/libretro/libretro-common/include/compat/posix_string.h new file mode 100644 index 00000000..2731231e --- /dev/null +++ b/libretro/libretro-common/include/compat/posix_string.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2010-2016 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (posix_string.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_POSIX_STRING_H +#define __LIBRETRO_SDK_COMPAT_POSIX_STRING_H + +#include + +#ifdef _MSC_VER +#include +#endif + +RETRO_BEGIN_DECLS + +#ifdef _WIN32 +#undef strtok_r +#define strtok_r(str, delim, saveptr) retro_strtok_r__(str, delim, saveptr) + +char *strtok_r(char *str, const char *delim, char **saveptr); +#endif + +#ifdef _MSC_VER +#undef strcasecmp +#undef strdup +#define strcasecmp(a, b) retro_strcasecmp__(a, b) +#define strdup(orig) retro_strdup__(orig) +int strcasecmp(const char *a, const char *b); +char *strdup(const char *orig); + +/* isblank is available since MSVC 2013 */ +#if _MSC_VER < 1800 +#undef isblank +#define isblank(c) retro_isblank__(c) +int isblank(int c); +#endif + +#endif + + +RETRO_END_DECLS + +#endif diff --git a/libretro/libretro-common/include/compat/strcasestr.h b/libretro/libretro-common/include/compat/strcasestr.h new file mode 100644 index 00000000..31dc11bb --- /dev/null +++ b/libretro/libretro-common/include/compat/strcasestr.h @@ -0,0 +1,48 @@ +/* Copyright (C) 2010-2016 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (strcasestr.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_STRCASESTR_H +#define __LIBRETRO_SDK_COMPAT_STRCASESTR_H + +#include + +#if defined(RARCH_INTERNAL) && defined(HAVE_CONFIG_H) +#include "../../../config.h" +#endif + +#ifndef HAVE_STRCASESTR + +#include + +RETRO_BEGIN_DECLS + +/* Avoid possible naming collisions during link + * since we prefer to use the actual name. */ +#define strcasestr(haystack, needle) strcasestr_retro__(haystack, needle) + +char *strcasestr(const char *haystack, const char *needle); + +RETRO_END_DECLS + +#endif + +#endif + diff --git a/libretro/libretro-common/include/compat/strl.h b/libretro/libretro-common/include/compat/strl.h new file mode 100644 index 00000000..50d31aa6 --- /dev/null +++ b/libretro/libretro-common/include/compat/strl.h @@ -0,0 +1,58 @@ +/* Copyright (C) 2010-2016 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 + +#ifdef 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 + +RETRO_END_DECLS + +#endif + diff --git a/libretro/libretro-common/include/encodings/utf.h b/libretro/libretro-common/include/encodings/utf.h new file mode 100644 index 00000000..bad94589 --- /dev/null +++ b/libretro/libretro-common/include/encodings/utf.h @@ -0,0 +1,67 @@ +/* Copyright (C) 2010-2017 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/libretro/libretro-common/include/file/file_path.h b/libretro/libretro-common/include/file/file_path.h new file mode 100644 index 00000000..85f190c6 --- /dev/null +++ b/libretro/libretro-common/include/file/file_path.h @@ -0,0 +1,489 @@ +/* Copyright (C) 2010-2017 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (file_path.h). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __LIBRETRO_SDK_FILE_PATH_H +#define __LIBRETRO_SDK_FILE_PATH_H + +#include +#include +#include +#include + +#include + +#include + +RETRO_BEGIN_DECLS + +/* Order in this enum is equivalent to negative sort order in filelist + * (i.e. DIRECTORY is on top of PLAIN_FILE) */ +enum +{ + RARCH_FILETYPE_UNSET, + RARCH_PLAIN_FILE, + RARCH_COMPRESSED_FILE_IN_ARCHIVE, + RARCH_COMPRESSED_ARCHIVE, + RARCH_DIRECTORY, + RARCH_FILE_UNSUPPORTED +}; + + +/** + * path_is_compressed_file: + * @path : path + * + * Checks if path is a compressed file. + * + * Returns: true (1) if path is a compressed file, otherwise false (0). + **/ +bool path_is_compressed_file(const char *path); + +/** + * path_contains_compressed_file: + * @path : path + * + * Checks if path contains a compressed file. + * + * Currently we only check for hash symbol (#) inside the pathname. + * If path is ever expanded to a general URI, we should check for that here. + * + * Example: Somewhere in the path there might be a compressed file + * E.g.: /path/to/file.7z#mygame.img + * + * Returns: true (1) if path contains compressed file, otherwise false (0). + **/ +#define path_contains_compressed_file(path) (path_get_archive_delim((path)) != NULL) + +/** + * path_get_archive_delim: + * @path : path + * + * Gets delimiter of an archive file. Only the first '#' + * after a compression extension is considered. + * + * Returns: pointer to the delimiter in the path if it contains + * a compressed file, otherwise NULL. + */ +const char *path_get_archive_delim(const char *path); + +/** + * path_get_extension: + * @path : path + * + * Gets extension of file. Only '.'s + * after the last slash are considered. + * + * Returns: extension part from the path. + */ +const char *path_get_extension(const char *path); + +/** + * path_remove_extension: + * @path : path + * + * Removes the extension from the path and returns the result. + * Removes all text after and including the last '.'. + * Only '.'s after the last slash are considered. + * + * Returns: path with the extension part removed. + */ +char *path_remove_extension(char *path); + +/** + * path_basename: + * @path : path + * + * Get basename from @path. + * + * Returns: basename from path. + **/ +const char *path_basename(const char *path); + +/** + * path_basedir: + * @path : path + * + * Extracts base directory by mutating path. + * Keeps trailing '/'. + **/ +void path_basedir(char *path); + +/** + * path_parent_dir: + * @path : path + * + * Extracts parent directory by mutating path. + * Assumes that path is a directory. Keeps trailing '/'. + **/ +void path_parent_dir(char *path); + +/** + * path_resolve_realpath: + * @buf : buffer for path + * @size : size of buffer + * + * Turns relative paths into absolute path. + * If relative, rebases on current working dir. + **/ +void path_resolve_realpath(char *buf, size_t size); + +/** + * path_is_absolute: + * @path : path + * + * Checks if @path is an absolute path or a relative path. + * + * Returns: true if path is absolute, false if path is relative. + **/ +bool path_is_absolute(const char *path); + +/** + * fill_pathname: + * @out_path : output path + * @in_path : input path + * @replace : what to replace + * @size : buffer size of output path + * + * FIXME: Verify + * + * Replaces filename extension with 'replace' and outputs result to out_path. + * The extension here is considered to be the string from the last '.' + * to the end. + * + * Only '.'s after the last slash are considered as extensions. + * If no '.' is present, in_path and replace will simply be concatenated. + * 'size' is buffer size of 'out_path'. + * E.g.: in_path = "/foo/bar/baz/boo.c", replace = ".asm" => + * out_path = "/foo/bar/baz/boo.asm" + * E.g.: in_path = "/foo/bar/baz/boo.c", replace = "" => + * out_path = "/foo/bar/baz/boo" + */ +void fill_pathname(char *out_path, const char *in_path, + const char *replace, size_t size); + +/** + * fill_dated_filename: + * @out_filename : output filename + * @ext : extension of output filename + * @size : buffer size of output filename + * + * Creates a 'dated' filename prefixed by 'RetroArch', and + * concatenates extension (@ext) to it. + * + * E.g.: + * out_filename = "RetroArch-{month}{day}-{Hours}{Minutes}.{@ext}" + **/ +void fill_dated_filename(char *out_filename, + const char *ext, size_t size); + +/** + * fill_str_dated_filename: + * @out_filename : output filename + * @in_str : input string + * @ext : extension of output filename + * @size : buffer size of output filename + * + * Creates a 'dated' filename prefixed by the string @in_str, and + * concatenates extension (@ext) to it. + * + * E.g.: + * out_filename = "RetroArch-{year}{month}{day}-{Hour}{Minute}{Second}.{@ext}" + **/ +void fill_str_dated_filename(char *out_filename, + const char *in_str, const char *ext, size_t size); + +/** + * fill_pathname_noext: + * @out_path : output path + * @in_path : input path + * @replace : what to replace + * @size : buffer size of output path + * + * Appends a filename extension 'replace' to 'in_path', and outputs + * result in 'out_path'. + * + * Assumes in_path has no extension. If an extension is still + * present in 'in_path', it will be ignored. + * + */ +void fill_pathname_noext(char *out_path, const char *in_path, + const char *replace, size_t size); + +/** + * find_last_slash: + * @str : input path + * + * Gets a pointer to the last slash in the input path. + * + * Returns: a pointer to the last slash in the input path. + **/ +char *find_last_slash(const char *str); + +/** + * fill_pathname_dir: + * @in_dir : input directory path + * @in_basename : input basename to be appended to @in_dir + * @replace : replacement to be appended to @in_basename + * @size : size of buffer + * + * Appends basename of 'in_basename', to 'in_dir', along with 'replace'. + * Basename of in_basename is the string after the last '/' or '\\', + * i.e the filename without directories. + * + * If in_basename has no '/' or '\\', the whole 'in_basename' will be used. + * 'size' is buffer size of 'in_dir'. + * + * E.g..: in_dir = "/tmp/some_dir", in_basename = "/some_content/foo.c", + * replace = ".asm" => in_dir = "/tmp/some_dir/foo.c.asm" + **/ +void fill_pathname_dir(char *in_dir, const char *in_basename, + const char *replace, size_t size); + +/** + * fill_pathname_base: + * @out : output path + * @in_path : input path + * @size : size of output path + * + * Copies basename of @in_path into @out_path. + **/ +void fill_pathname_base(char *out_path, const char *in_path, size_t size); + +void fill_pathname_base_noext(char *out_dir, + const char *in_path, size_t size); + +void fill_pathname_base_ext(char *out, + const char *in_path, const char *ext, + size_t size); + +/** + * fill_pathname_basedir: + * @out_dir : output directory + * @in_path : input path + * @size : size of output directory + * + * Copies base directory of @in_path into @out_path. + * If in_path is a path without any slashes (relative current directory), + * @out_path will get path "./". + **/ +void fill_pathname_basedir(char *out_path, const char *in_path, size_t size); + +void fill_pathname_basedir_noext(char *out_dir, + const char *in_path, size_t size); + +/** + * fill_pathname_parent_dir_name: + * @out_dir : output directory + * @in_dir : input directory + * @size : size of output directory + * + * Copies only the parent directory name of @in_dir into @out_dir. + * The two buffers must not overlap. Removes trailing '/'. + * Returns true on success, false if a slash was not found in the path. + **/ +bool fill_pathname_parent_dir_name(char *out_dir, + const char *in_dir, size_t size); + +/** + * fill_pathname_parent_dir: + * @out_dir : output directory + * @in_dir : input directory + * @size : size of output directory + * + * Copies parent directory of @in_dir into @out_dir. + * Assumes @in_dir is a directory. Keeps trailing '/'. + **/ +void fill_pathname_parent_dir(char *out_dir, + const char *in_dir, size_t size); + +/** + * fill_pathname_resolve_relative: + * @out_path : output path + * @in_refpath : input reference path + * @in_path : input path + * @size : size of @out_path + * + * Joins basedir of @in_refpath together with @in_path. + * If @in_path is an absolute path, out_path = in_path. + * E.g.: in_refpath = "/foo/bar/baz.a", in_path = "foobar.cg", + * out_path = "/foo/bar/foobar.cg". + **/ +void fill_pathname_resolve_relative(char *out_path, const char *in_refpath, + const char *in_path, size_t size); + +/** + * fill_pathname_join: + * @out_path : output path + * @dir : directory + * @path : path + * @size : size of output path + * + * Joins a directory (@dir) and path (@path) together. + * Makes sure not to get two consecutive slashes + * between directory and path. + **/ +void fill_pathname_join(char *out_path, const char *dir, + const char *path, size_t size); + +void fill_pathname_join_special_ext(char *out_path, + const char *dir, const char *path, + const char *last, const char *ext, + size_t size); + +void fill_pathname_join_concat_noext( + char *out_path, + const char *dir, const char *path, + const char *concat, + size_t size); + +void fill_pathname_join_concat(char *out_path, + const char *dir, const char *path, + const char *concat, + size_t size); + +void fill_pathname_join_noext(char *out_path, + const char *dir, const char *path, size_t size); + +/** + * fill_pathname_join_delim: + * @out_path : output path + * @dir : directory + * @path : path + * @delim : delimiter + * @size : size of output path + * + * Joins a directory (@dir) and path (@path) together + * using the given delimiter (@delim). + **/ +void fill_pathname_join_delim(char *out_path, const char *dir, + const char *path, const char delim, size_t size); + +void fill_pathname_join_delim_concat(char *out_path, const char *dir, + const char *path, const char delim, const char *concat, + size_t size); + +/** + * fill_short_pathname_representation: + * @out_rep : output representation + * @in_path : input path + * @size : size of output representation + * + * Generates a short representation of path. It should only + * be used for displaying the result; the output representation is not + * binding in any meaningful way (for a normal path, this is the same as basename) + * In case of more complex URLs, this should cut everything except for + * the main image file. + * + * E.g.: "/path/to/game.img" -> game.img + * "/path/to/myarchive.7z#folder/to/game.img" -> game.img + */ +void fill_short_pathname_representation(char* out_rep, + const char *in_path, size_t size); + +void fill_short_pathname_representation_noext(char* out_rep, + const char *in_path, size_t size); + +void fill_pathname_expand_special(char *out_path, + const char *in_path, size_t size); + +void fill_pathname_abbreviate_special(char *out_path, + const char *in_path, size_t size); + +/** + * path_basedir: + * @path : path + * + * Extracts base directory by mutating path. + * Keeps trailing '/'. + **/ +void path_basedir_wrapper(char *path); + +/** + * path_char_is_slash: + * @c : character + * + * Checks if character (@c) is a slash. + * + * Returns: true (1) if character is a slash, otherwise false (0). + */ +#ifdef _WIN32 +#define path_char_is_slash(c) (((c) == '/') || ((c) == '\\')) +#else +#define path_char_is_slash(c) ((c) == '/') +#endif + +/** + * path_default_slash: + * + * Gets the default slash separator. + * + * Returns: default slash separator. + */ +#ifdef _WIN32 +#define path_default_slash() "\\" +#else +#define path_default_slash() "/" +#endif + +/** + * fill_pathname_slash: + * @path : path + * @size : size of path + * + * Assumes path is a directory. Appends a slash + * if not already there. + **/ +void fill_pathname_slash(char *path, size_t size); + +#ifndef RARCH_CONSOLE +void fill_pathname_application_path(char *buf, size_t size); +#endif + +/** + * path_mkdir: + * @dir : directory + * + * Create directory on filesystem. + * + * Returns: true (1) if directory could be created, otherwise false (0). + **/ +bool path_mkdir(const char *dir); + +/** + * path_is_directory: + * @path : path + * + * Checks if path is a directory. + * + * Returns: true (1) if path is a directory, otherwise false (0). + */ +bool path_is_directory(const char *path); + +bool path_is_character_special(const char *path); + +bool path_is_valid(const char *path); + +int32_t path_get_size(const char *path); + +RETRO_END_DECLS + +#endif diff --git a/libretro/libretro-common/include/retro_assert.h b/libretro/libretro-common/include/retro_assert.h new file mode 100644 index 00000000..5870c3e9 --- /dev/null +++ b/libretro/libretro-common/include/retro_assert.h @@ -0,0 +1,36 @@ +/* Copyright (C) 2010-2016 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (retro_assert.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 __RETRO_ASSERT_H +#define __RETRO_ASSERT_H + +#include + +#ifdef RARCH_INTERNAL +#define retro_assert(cond) do { \ + if (!(cond)) { printf("Assertion failed at %s:%d.\n", __FILE__, __LINE__); abort(); } \ +} while(0) +#else +#define retro_assert(cond) assert(cond) +#endif + +#endif diff --git a/libretro/libretro-common/include/retro_common_api.h b/libretro/libretro-common/include/retro_common_api.h new file mode 100644 index 00000000..05326124 --- /dev/null +++ b/libretro/libretro-common/include/retro_common_api.h @@ -0,0 +1,108 @@ +/* Copyright (C) 2010-2016 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (retro_common_api.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_COMMON_RETRO_COMMON_API_H +#define _LIBRETRO_COMMON_RETRO_COMMON_API_H + +/* +This file is designed to normalize the libretro-common compiling environment +for public API headers. This should be leaner than a normal compiling environment, +since it gets #included into other project's sources. +*/ + +/* ------------------------------------ */ + +/* +Ordinarily we want to put #ifdef __cplusplus extern "C" in C library +headers to enable them to get used by c++ sources. +However, we want to support building this library as C++ as well, so a +special technique is called for. +*/ + +#define RETRO_BEGIN_DECLS +#define RETRO_END_DECLS + +#ifdef __cplusplus + +#ifdef CXX_BUILD +/* build wants everything to be built as c++, so no extern "C" */ +#else +#undef RETRO_BEGIN_DECLS +#undef RETRO_END_DECLS +#define RETRO_BEGIN_DECLS extern "C" { +#define RETRO_END_DECLS } +#endif + +#else + +/* header is included by a C source file, so no extern "C" */ + +#endif + +/* +IMO, this non-standard ssize_t should not be used. +However, it's a good example of how to handle something like this. +*/ +#ifdef _MSC_VER +#ifndef HAVE_SSIZE_T +#define HAVE_SSIZE_T +#if defined(_WIN64) +typedef __int64 ssize_t; +#elif defined(_WIN32) +typedef int ssize_t; +#endif +#endif +#elif defined(__MACH__) +#include +#endif + +#ifdef _WIN32 +#define STRING_REP_INT64 "%I64u" +#define STRING_REP_UINT64 "%I64u" +#define STRING_REP_ULONG "%Iu" +#elif defined(__STDC_VERSION__) && __STDC_VERSION__>=199901L +#define STRING_REP_INT64 "%llu" +#define STRING_REP_UINT64 "%llu" +#define STRING_REP_ULONG "%zu" +#else +#define STRING_REP_INT64 "%llu" +#define STRING_REP_UINT64 "%llu" +#define STRING_REP_ULONG "%lu" +#endif + +/* +I would like to see retro_inline.h moved in here; possibly boolean too. + +rationale: these are used in public APIs, and it is easier to find problems +and write code that works the first time portably when theyre included uniformly +than to do the analysis from scratch each time you think you need it, for each feature. + +Moreover it helps force you to make hard decisions: if you EVER bring in boolean.h, +then you should pay the price everywhere, so you can see how much grief it will cause. + +Of course, another school of thought is that you should do as little damage as possible +in as few places as possible... +*/ + + +/* _LIBRETRO_COMMON_RETRO_COMMON_API_H */ +#endif diff --git a/libretro/libretro-common/include/retro_inline.h b/libretro/libretro-common/include/retro_inline.h new file mode 100644 index 00000000..8535d848 --- /dev/null +++ b/libretro/libretro-common/include/retro_inline.h @@ -0,0 +1,39 @@ +/* Copyright (C) 2010-2015 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (retro_inline.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_INLINE_H +#define __LIBRETRO_SDK_INLINE_H + +#ifndef INLINE + +#if !defined(__cplusplus) && defined(_WIN32) +#define INLINE _inline +#elif defined(__STDC_VERSION__) && __STDC_VERSION__>=199901L +#define INLINE inline +#elif defined(__GNUC__) +#define INLINE __inline__ +#else +#define INLINE +#endif + +#endif +#endif diff --git a/libretro/libretro-common/include/retro_miscellaneous.h b/libretro/libretro-common/include/retro_miscellaneous.h new file mode 100644 index 00000000..83d004b8 --- /dev/null +++ b/libretro/libretro-common/include/retro_miscellaneous.h @@ -0,0 +1,211 @@ +/* Copyright (C) 2010-2016 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (retro_miscellaneous.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 __RARCH_MISCELLANEOUS_H +#define __RARCH_MISCELLANEOUS_H + +#include +#include + +#if defined(__CELLOS_LV2__) && !defined(__PSL1GHT__) +#include +#elif defined(XENON) +#include