update libretro-common
parent
fd812d4cf8
commit
32f0a6463a
|
@ -1416,6 +1416,7 @@ struct string_list* cdrom_get_available_drives(void)
|
|||
}
|
||||
}
|
||||
string_list_deinitialize(&mods);
|
||||
free(buf);
|
||||
|
||||
#ifdef CDROM_DEBUG
|
||||
if (found)
|
||||
|
|
|
@ -37,22 +37,28 @@
|
|||
void *fopen_utf8(const char * filename, const char * mode)
|
||||
{
|
||||
#if defined(LEGACY_WIN32)
|
||||
FILE *ret = NULL;
|
||||
char * filename_local = utf8_to_local_string_alloc(filename);
|
||||
|
||||
if (!filename_local)
|
||||
return NULL;
|
||||
ret = fopen(filename_local, mode);
|
||||
if (filename_local)
|
||||
{
|
||||
FILE *ret = fopen(filename_local, mode);
|
||||
free(filename_local);
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
wchar_t * filename_w = utf8_to_utf16_string_alloc(filename);
|
||||
wchar_t * mode_w = utf8_to_utf16_string_alloc(mode);
|
||||
FILE* ret = _wfopen(filename_w, mode_w);
|
||||
free(filename_w);
|
||||
free(mode_w);
|
||||
return ret;
|
||||
wchar_t * filename_w = utf8_to_utf16_string_alloc(filename);
|
||||
if (filename_w)
|
||||
{
|
||||
FILE *ret = NULL;
|
||||
wchar_t *mode_w = utf8_to_utf16_string_alloc(mode);
|
||||
if (mode_w)
|
||||
{
|
||||
ret = _wfopen(filename_w, mode_w);
|
||||
free(mode_w);
|
||||
}
|
||||
free(filename_w);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -51,9 +51,12 @@ static unsigned leading_ones(uint8_t c)
|
|||
return ones;
|
||||
}
|
||||
|
||||
/* Simple implementation. Assumes the sequence is
|
||||
* properly synchronized and terminated. */
|
||||
|
||||
/**
|
||||
* utf8_conv_utf32:
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
|
@ -79,7 +82,7 @@ size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
|
|||
for (i = 0; i < extra; i++, in++, shift -= 6)
|
||||
c |= (*in & 0x3f) << shift;
|
||||
|
||||
*out++ = c;
|
||||
*out++ = c;
|
||||
in_size -= 1 + extra;
|
||||
out_chars--;
|
||||
ret++;
|
||||
|
@ -88,6 +91,11 @@ size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* utf16_conv_utf8:
|
||||
*
|
||||
* Leaf function.
|
||||
**/
|
||||
bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
|
||||
const uint16_t *in, size_t in_size)
|
||||
{
|
||||
|
@ -148,16 +156,20 @@ bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Acts mostly like strlcpy.
|
||||
/**
|
||||
* utf8cpy:
|
||||
*
|
||||
* Acts mostly like strlcpy.
|
||||
*
|
||||
* Copies the given number of UTF-8 characters,
|
||||
* but at most d_len bytes.
|
||||
* but at most @d_len bytes.
|
||||
*
|
||||
* Always NULL terminates.
|
||||
* Does not copy half a character.
|
||||
* Always NULL terminates. Does not copy half a character.
|
||||
* @s is assumed valid UTF-8.
|
||||
* Use only if @chars is considerably less than @d_len.
|
||||
*
|
||||
* Returns number of bytes. 's' is assumed valid UTF-8.
|
||||
* Use only if 'chars' is considerably less than 'd_len'. */
|
||||
* @return Number of bytes.
|
||||
**/
|
||||
size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars)
|
||||
{
|
||||
const uint8_t *sb = (const uint8_t*)s;
|
||||
|
@ -186,6 +198,11 @@ size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars)
|
|||
return sb-sb_org;
|
||||
}
|
||||
|
||||
/**
|
||||
* utf8skip:
|
||||
*
|
||||
* Leaf function
|
||||
**/
|
||||
const char *utf8skip(const char *str, size_t chars)
|
||||
{
|
||||
const uint8_t *strb = (const uint8_t*)str;
|
||||
|
@ -204,6 +221,11 @@ const char *utf8skip(const char *str, size_t chars)
|
|||
return (const char*)strb;
|
||||
}
|
||||
|
||||
/**
|
||||
* utf8len:
|
||||
*
|
||||
* Leaf function.
|
||||
**/
|
||||
size_t utf8len(const char *string)
|
||||
{
|
||||
size_t ret = 0;
|
||||
|
@ -220,7 +242,15 @@ size_t utf8len(const char *string)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Does not validate the input, returns garbage if it's not UTF-8. */
|
||||
/**
|
||||
* utf8_walk:
|
||||
*
|
||||
* Does not validate the input.
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* @return Returns garbage if it's not UTF-8.
|
||||
**/
|
||||
uint32_t utf8_walk(const char **string)
|
||||
{
|
||||
uint8_t first = UTF8_WALKBYTE(string);
|
||||
|
@ -248,24 +278,23 @@ 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);
|
||||
if ((*utf_data = (uint8_t*)malloc(*dest_len)) != 0)
|
||||
return utf16_conv_utf8(*utf_data, dest_len, in, len);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* utf16_to_char_string:
|
||||
**/
|
||||
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);
|
||||
size_t dest_len = 0;
|
||||
uint8_t *utf16_data = NULL;
|
||||
bool ret = utf16_to_char(&utf16_data, &dest_len, in);
|
||||
|
||||
if (ret)
|
||||
{
|
||||
|
@ -274,13 +303,17 @@ bool utf16_to_char_string(const uint16_t *in, char *s, size_t len)
|
|||
}
|
||||
|
||||
free(utf16_data);
|
||||
utf16_data = NULL;
|
||||
utf16_data = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE)
|
||||
/* Returned pointer MUST be freed by the caller if non-NULL. */
|
||||
/**
|
||||
* mb_to_mb_string_alloc:
|
||||
*
|
||||
* @return 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)
|
||||
{
|
||||
|
@ -300,10 +333,8 @@ static char *mb_to_mb_string_alloc(const char *str,
|
|||
if (!path_buf_wide_len)
|
||||
return strdup(str);
|
||||
|
||||
path_buf_wide = (wchar_t*)
|
||||
calloc(path_buf_wide_len + sizeof(wchar_t), sizeof(wchar_t));
|
||||
|
||||
if (path_buf_wide)
|
||||
if ((path_buf_wide = (wchar_t*)
|
||||
calloc(path_buf_wide_len + sizeof(wchar_t), sizeof(wchar_t))))
|
||||
{
|
||||
MultiByteToWideChar(cp_in, 0,
|
||||
str, -1, path_buf_wide, path_buf_wide_len);
|
||||
|
@ -347,45 +378,49 @@ static char *mb_to_mb_string_alloc(const char *str,
|
|||
}
|
||||
#endif
|
||||
|
||||
/* Returned pointer MUST be freed by the caller if non-NULL. */
|
||||
/**
|
||||
* utf8_to_local_string_alloc:
|
||||
*
|
||||
* @return 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);
|
||||
return strdup(str); /* Assume string needs no modification if not on Windows */
|
||||
#endif
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Returned pointer MUST be freed by the caller if non-NULL. */
|
||||
char* local_to_utf8_string_alloc(const char *str)
|
||||
/**
|
||||
* local_to_utf8_string_alloc:
|
||||
*
|
||||
* @return Returned pointer MUST be freed by the caller if non-NULL.
|
||||
**/
|
||||
char *local_to_utf8_string_alloc(const char *str)
|
||||
{
|
||||
if (str && *str)
|
||||
{
|
||||
if (str && *str)
|
||||
#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE)
|
||||
return mb_to_mb_string_alloc(str, CODEPAGE_LOCAL, CODEPAGE_UTF8);
|
||||
return mb_to_mb_string_alloc(str, CODEPAGE_LOCAL, CODEPAGE_UTF8);
|
||||
#else
|
||||
/* assume string needs no modification if not on Windows */
|
||||
return strdup(str);
|
||||
return strdup(str); /* Assume string needs no modification if not on Windows */
|
||||
#endif
|
||||
}
|
||||
return NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Returned pointer MUST be freed by the caller if non-NULL. */
|
||||
/**
|
||||
* utf8_to_utf16_string_alloc:
|
||||
*
|
||||
* @return 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;
|
||||
|
||||
|
@ -393,63 +428,55 @@ wchar_t* utf8_to_utf16_string_alloc(const char *str)
|
|||
return NULL;
|
||||
|
||||
#ifdef _WIN32
|
||||
len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
|
||||
|
||||
if (len)
|
||||
if ((len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0)))
|
||||
{
|
||||
buf = (wchar_t*)calloc(len, sizeof(wchar_t));
|
||||
|
||||
if (!buf)
|
||||
if (!(buf = (wchar_t*)calloc(len, sizeof(wchar_t))))
|
||||
return NULL;
|
||||
|
||||
out_len = MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, len);
|
||||
if ((MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, len)) < 0)
|
||||
{
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* fallback to ANSI codepage instead */
|
||||
len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
|
||||
|
||||
if (len)
|
||||
/* Fallback to ANSI codepage instead */
|
||||
if ((len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0)))
|
||||
{
|
||||
buf = (wchar_t*)calloc(len, sizeof(wchar_t));
|
||||
|
||||
if (!buf)
|
||||
if (!(buf = (wchar_t*)calloc(len, sizeof(wchar_t))))
|
||||
return NULL;
|
||||
|
||||
out_len = MultiByteToWideChar(CP_ACP, 0, str, -1, buf, len);
|
||||
if ((MultiByteToWideChar(CP_ACP, 0, str, -1, buf, len)) < 0)
|
||||
{
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (out_len < 0)
|
||||
{
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
/* NOTE: For now, assume non-Windows platforms' locale is already UTF-8. */
|
||||
len = mbstowcs(NULL, str, 0) + 1;
|
||||
|
||||
if (len)
|
||||
if ((len = mbstowcs(NULL, str, 0) + 1))
|
||||
{
|
||||
buf = (wchar_t*)calloc(len, sizeof(wchar_t));
|
||||
|
||||
if (!buf)
|
||||
if (!(buf = (wchar_t*)calloc(len, sizeof(wchar_t))))
|
||||
return NULL;
|
||||
|
||||
out_len = mbstowcs(buf, str, len);
|
||||
}
|
||||
|
||||
if (out_len == (size_t)-1)
|
||||
{
|
||||
free(buf);
|
||||
return NULL;
|
||||
if ((mbstowcs(buf, str, len)) == (size_t)-1)
|
||||
{
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Returned pointer MUST be freed by the caller if non-NULL. */
|
||||
/**
|
||||
* utf16_to_utf8_string_alloc:
|
||||
*
|
||||
* @return Returned pointer MUST be freed by the caller if non-NULL.
|
||||
**/
|
||||
char* utf16_to_utf8_string_alloc(const wchar_t *str)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
@ -465,20 +492,17 @@ char* utf16_to_utf8_string_alloc(const wchar_t *str)
|
|||
#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)
|
||||
if (!(len = WideCharToMultiByte(code_page,
|
||||
0, str, -1, NULL, 0, NULL, NULL)))
|
||||
{
|
||||
code_page = CP_ACP;
|
||||
len = WideCharToMultiByte(code_page,
|
||||
0, str, -1, NULL, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
buf = (char*)calloc(len, sizeof(char));
|
||||
|
||||
if (!buf)
|
||||
if (!(buf = (char*)calloc(len, sizeof(char))))
|
||||
return NULL;
|
||||
|
||||
if (WideCharToMultiByte(code_page,
|
||||
|
@ -491,13 +515,9 @@ char* utf16_to_utf8_string_alloc(const wchar_t *str)
|
|||
#else
|
||||
/* NOTE: For now, assume non-Windows platforms'
|
||||
* locale is already UTF-8. */
|
||||
len = wcstombs(NULL, str, 0) + 1;
|
||||
|
||||
if (len)
|
||||
if ((len = wcstombs(NULL, str, 0) + 1))
|
||||
{
|
||||
buf = (char*)calloc(len, sizeof(char));
|
||||
|
||||
if (!buf)
|
||||
if (!(buf = (char*)calloc(len, sizeof(char))))
|
||||
return NULL;
|
||||
|
||||
if (wcstombs(buf, str, len) == (size_t)-1)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -63,20 +63,34 @@ static INLINE int compat_ctz(unsigned x)
|
|||
return __builtin_ctz(x);
|
||||
#elif _MSC_VER >= 1400 && !defined(_XBOX) && !defined(__WINRT__)
|
||||
unsigned long r = 0;
|
||||
_BitScanReverse((unsigned long*)&r, x);
|
||||
_BitScanForward((unsigned long*)&r, x);
|
||||
return (int)r;
|
||||
#else
|
||||
/* Only checks at nibble granularity,
|
||||
* because that's what we need. */
|
||||
if (x & 0x000f)
|
||||
return 0;
|
||||
if (x & 0x00f0)
|
||||
return 4;
|
||||
if (x & 0x0f00)
|
||||
return 8;
|
||||
if (x & 0xf000)
|
||||
return 12;
|
||||
return 16;
|
||||
int count = 0;
|
||||
if (!(x & 0xffff))
|
||||
{
|
||||
x >>= 16;
|
||||
count |= 16;
|
||||
}
|
||||
if (!(x & 0xff))
|
||||
{
|
||||
x >>= 8;
|
||||
count |= 8;
|
||||
}
|
||||
if (!(x & 0xf))
|
||||
{
|
||||
x >>= 4;
|
||||
count |= 4;
|
||||
}
|
||||
if (!(x & 0x3))
|
||||
{
|
||||
x >>= 2;
|
||||
count |= 2;
|
||||
}
|
||||
if (!(x & 0x1))
|
||||
count |= 1;
|
||||
|
||||
return count;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -38,29 +38,99 @@ enum CodePage
|
|||
CODEPAGE_UTF8 = 65001 /* CP_UTF8 */
|
||||
};
|
||||
|
||||
/**
|
||||
* utf8_conv_utf32:
|
||||
*
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* utf16_conv_utf8:
|
||||
*
|
||||
* Leaf function.
|
||||
**/
|
||||
bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
|
||||
const uint16_t *in, size_t in_size);
|
||||
|
||||
/**
|
||||
* utf8len:
|
||||
*
|
||||
* Leaf function.
|
||||
**/
|
||||
size_t utf8len(const char *string);
|
||||
|
||||
/**
|
||||
* utf8cpy:
|
||||
*
|
||||
* 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.
|
||||
* @s is assumed valid UTF-8.
|
||||
* Use only if @chars is considerably less than @d_len.
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls memcpy
|
||||
*
|
||||
* @return Number of bytes.
|
||||
**/
|
||||
size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars);
|
||||
|
||||
/**
|
||||
* utf8skip:
|
||||
*
|
||||
* Leaf function
|
||||
**/
|
||||
const char *utf8skip(const char *str, size_t chars);
|
||||
|
||||
/**
|
||||
* utf8_walk:
|
||||
*
|
||||
* Does not validate the input.
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* @return Returns garbage if it's not UTF-8.
|
||||
**/
|
||||
uint32_t utf8_walk(const char **string);
|
||||
|
||||
/**
|
||||
* utf16_to_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);
|
||||
/**
|
||||
* utf8_to_local_string_alloc:
|
||||
*
|
||||
* @return Returned pointer MUST be freed by the caller if non-NULL.
|
||||
**/
|
||||
char *utf8_to_local_string_alloc(const char *str);
|
||||
|
||||
char* local_to_utf8_string_alloc(const char *str);
|
||||
/**
|
||||
* local_to_utf8_string_alloc:
|
||||
*
|
||||
* @return Returned pointer MUST be freed by the caller if non-NULL.
|
||||
**/
|
||||
char *local_to_utf8_string_alloc(const char *str);
|
||||
|
||||
wchar_t* utf8_to_utf16_string_alloc(const char *str);
|
||||
/**
|
||||
* utf8_to_utf16_string_alloc:
|
||||
*
|
||||
* @return Returned pointer MUST be freed by the caller if non-NULL.
|
||||
**/
|
||||
wchar_t *utf8_to_utf16_string_alloc(const char *str);
|
||||
|
||||
char* utf16_to_utf8_string_alloc(const wchar_t *str);
|
||||
/**
|
||||
* utf16_to_utf8_string_alloc:
|
||||
*
|
||||
* @return Returned pointer MUST be freed by the caller if non-NULL.
|
||||
**/
|
||||
char *utf16_to_utf8_string_alloc(const wchar_t *str);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
|
|
|
@ -51,6 +51,28 @@ enum
|
|||
RARCH_FILE_UNSUPPORTED
|
||||
};
|
||||
|
||||
struct path_linked_list
|
||||
{
|
||||
char *path;
|
||||
struct path_linked_list *next;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new linked list with one item in it
|
||||
* The path on this item will be set to NULL
|
||||
**/
|
||||
struct path_linked_list* path_linked_list_new(void);
|
||||
|
||||
/* Free the entire linked list */
|
||||
void path_linked_list_free(struct path_linked_list *in_path_linked_list);
|
||||
|
||||
/**
|
||||
* Add a node to the linked list with this path
|
||||
* If the first node's path if it's not yet set,
|
||||
* set this instead
|
||||
**/
|
||||
void path_linked_list_add_path(struct path_linked_list *in_path_linked_list, char *path);
|
||||
|
||||
/**
|
||||
* path_is_compressed_file:
|
||||
* @path : path
|
||||
|
@ -81,12 +103,12 @@ bool path_is_compressed_file(const char *path);
|
|||
* path_get_archive_delim:
|
||||
* @path : path
|
||||
*
|
||||
* Gets delimiter of an archive file. Only the first '#'
|
||||
* 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 compressed file, otherwise NULL.
|
||||
*/
|
||||
* @return 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);
|
||||
|
||||
/**
|
||||
|
@ -96,10 +118,28 @@ const char *path_get_archive_delim(const char *path);
|
|||
* Gets extension of file. Only '.'s
|
||||
* after the last slash are considered.
|
||||
*
|
||||
* Returns: extension part from the path.
|
||||
*/
|
||||
* Hidden non-leaf function cost:
|
||||
* - calls string_is_empty()
|
||||
* - calls strrchr
|
||||
*
|
||||
* @return extension part from the path.
|
||||
**/
|
||||
const char *path_get_extension(const char *path);
|
||||
|
||||
/**
|
||||
* path_get_extension_mutable:
|
||||
* @path : path
|
||||
*
|
||||
* Specialized version of path_get_extension(). Return
|
||||
* value is mutable.
|
||||
*
|
||||
* Gets extension of file. Only '.'s
|
||||
* after the last slash are considered.
|
||||
*
|
||||
* @return extension part from the path.
|
||||
**/
|
||||
char *path_get_extension_mutable(const char *path);
|
||||
|
||||
/**
|
||||
* path_remove_extension:
|
||||
* @path : path
|
||||
|
@ -108,7 +148,10 @@ const char *path_get_extension(const char *path);
|
|||
* text after and including the last '.'.
|
||||
* Only '.'s after the last slash are considered.
|
||||
*
|
||||
* Returns:
|
||||
* Hidden non-leaf function cost:
|
||||
* - calls strrchr
|
||||
*
|
||||
* @return
|
||||
* 1) If path has an extension, returns path with the
|
||||
* extension removed.
|
||||
* 2) If there is no extension, returns NULL.
|
||||
|
@ -122,10 +165,28 @@ char *path_remove_extension(char *path);
|
|||
*
|
||||
* Get basename from @path.
|
||||
*
|
||||
* Returns: basename from path.
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls path_get_archive_delim()
|
||||
* - can call find_last_slash() once if it returns NULL
|
||||
*
|
||||
* @return basename from path.
|
||||
**/
|
||||
const char *path_basename(const char *path);
|
||||
|
||||
/**
|
||||
* path_basename_nocompression:
|
||||
* @path : path
|
||||
*
|
||||
* Specialized version of path_basename().
|
||||
* Get basename from @path.
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls find_last_slash()
|
||||
*
|
||||
* @return basename from path.
|
||||
**/
|
||||
const char *path_basename_nocompression(const char *path);
|
||||
|
||||
/**
|
||||
* path_basedir:
|
||||
* @path : path
|
||||
|
@ -138,12 +199,13 @@ void path_basedir(char *path);
|
|||
/**
|
||||
* path_parent_dir:
|
||||
* @path : path
|
||||
* @len : length of @path
|
||||
*
|
||||
* Extracts parent directory by mutating path.
|
||||
* Assumes that path is a directory. Keeps trailing '/'.
|
||||
* If the path was already at the root directory, returns empty string
|
||||
**/
|
||||
void path_parent_dir(char *path);
|
||||
void path_parent_dir(char *path, size_t len);
|
||||
|
||||
/**
|
||||
* path_resolve_realpath:
|
||||
|
@ -155,7 +217,7 @@ void path_parent_dir(char *path);
|
|||
*
|
||||
* Relative paths are rebased on the current working dir.
|
||||
*
|
||||
* Returns: @buf if successful, NULL otherwise.
|
||||
* @return @buf if successful, NULL otherwise.
|
||||
* Note: Not implemented on consoles
|
||||
* Note: Symlinks are only resolved on Unix-likes
|
||||
* Note: The current working dir might not be what you expect,
|
||||
|
@ -177,8 +239,11 @@ char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks);
|
|||
* Both @path and @base are assumed to be absolute paths without "." or "..".
|
||||
*
|
||||
* E.g. path /a/b/e/f.cgp with base /a/b/c/d/ turns into ../../e/f.cgp
|
||||
*
|
||||
* @return Length of the string copied into @out
|
||||
**/
|
||||
size_t path_relative_to(char *out, const char *path, const char *base, size_t size);
|
||||
size_t path_relative_to(char *out, const char *path, const char *base,
|
||||
size_t size);
|
||||
|
||||
/**
|
||||
* path_is_absolute:
|
||||
|
@ -186,7 +251,7 @@ size_t path_relative_to(char *out, const char *path, const char *base, size_t si
|
|||
*
|
||||
* Checks if @path is an absolute path or a relative path.
|
||||
*
|
||||
* Returns: true if path is absolute, false if path is relative.
|
||||
* @return true if path is absolute, false if path is relative.
|
||||
**/
|
||||
bool path_is_absolute(const char *path);
|
||||
|
||||
|
@ -210,8 +275,15 @@ bool path_is_absolute(const char *path);
|
|||
* out_path = "/foo/bar/baz/boo.asm"
|
||||
* E.g.: in_path = "/foo/bar/baz/boo.c", replace = "" =>
|
||||
* out_path = "/foo/bar/baz/boo"
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - calls strlcpy 2x
|
||||
* - calls strrchr
|
||||
* - calls strlcat
|
||||
*
|
||||
* @return Length of the string copied into @out
|
||||
*/
|
||||
void fill_pathname(char *out_path, const char *in_path,
|
||||
size_t fill_pathname(char *out_path, const char *in_path,
|
||||
const char *replace, size_t size);
|
||||
|
||||
/**
|
||||
|
@ -225,6 +297,12 @@ void fill_pathname(char *out_path, const char *in_path,
|
|||
*
|
||||
* E.g.:
|
||||
* out_filename = "RetroArch-{month}{day}-{Hours}{Minutes}.{@ext}"
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls rtime_localtime()
|
||||
* - Calls strftime
|
||||
* - Calls strlcat
|
||||
*
|
||||
**/
|
||||
size_t fill_dated_filename(char *out_filename,
|
||||
const char *ext, size_t size);
|
||||
|
@ -241,34 +319,33 @@ size_t fill_dated_filename(char *out_filename,
|
|||
*
|
||||
* E.g.:
|
||||
* out_filename = "RetroArch-{year}{month}{day}-{Hour}{Minute}{Second}.{@ext}"
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls time
|
||||
* - Calls rtime_localtime()
|
||||
* - Calls strlcpy
|
||||
* - Calls string_is_empty()
|
||||
* - Calls strftime
|
||||
* - Calls strlcat at least 2x
|
||||
*
|
||||
* @return Length of the string copied into @out_path
|
||||
**/
|
||||
void fill_str_dated_filename(char *out_filename,
|
||||
size_t 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.
|
||||
*
|
||||
*/
|
||||
size_t fill_pathname_noext(char *out_path, const char *in_path,
|
||||
const char *replace, size_t size);
|
||||
|
||||
/**
|
||||
* find_last_slash:
|
||||
* @str : input path
|
||||
* @str : path
|
||||
* @size : size of path
|
||||
*
|
||||
* Gets a pointer to the last slash in the input path.
|
||||
* Find last slash in path. Tries to find
|
||||
* a backslash on Windows too which takes precedence
|
||||
* over regular slash.
|
||||
|
||||
* Hidden non-leaf function cost:
|
||||
* - calls strrchr
|
||||
*
|
||||
* Returns: a pointer to the last slash in the input path.
|
||||
* @return pointer to last slash/backslash found in @str.
|
||||
**/
|
||||
char *find_last_slash(const char *str);
|
||||
|
||||
|
@ -288,6 +365,11 @@ char *find_last_slash(const char *str);
|
|||
*
|
||||
* E.g..: in_dir = "/tmp/some_dir", in_basename = "/some_content/foo.c",
|
||||
* replace = ".asm" => in_dir = "/tmp/some_dir/foo.c.asm"
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls fill_pathname_slash()
|
||||
* - Calls path_basename()
|
||||
* - Calls strlcat 2x
|
||||
**/
|
||||
size_t fill_pathname_dir(char *in_dir, const char *in_basename,
|
||||
const char *replace, size_t size);
|
||||
|
@ -299,16 +381,15 @@ size_t fill_pathname_dir(char *in_dir, const char *in_basename,
|
|||
* @size : size of output path
|
||||
*
|
||||
* Copies basename of @in_path into @out_path.
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls path_basename()
|
||||
* - Calls strlcpy
|
||||
*
|
||||
* @return length of the string copied into @out
|
||||
**/
|
||||
size_t 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);
|
||||
|
||||
size_t fill_pathname_base_ext(char *out,
|
||||
const char *in_path, const char *ext,
|
||||
size_t size);
|
||||
|
||||
/**
|
||||
* fill_pathname_basedir:
|
||||
* @out_dir : output directory
|
||||
|
@ -318,12 +399,13 @@ size_t fill_pathname_base_ext(char *out,
|
|||
* 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 "./".
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls strlcpy
|
||||
* - Calls path_basedir()
|
||||
**/
|
||||
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
|
||||
|
@ -332,7 +414,13 @@ void fill_pathname_basedir_noext(char *out_dir,
|
|||
*
|
||||
* 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.
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls strdup
|
||||
* - Calls find_last_slash() x times
|
||||
* - Can call strlcpy
|
||||
*
|
||||
* @return 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);
|
||||
|
@ -346,6 +434,11 @@ bool fill_pathname_parent_dir_name(char *out_dir,
|
|||
* Copies parent directory of @in_dir into @out_dir.
|
||||
* Assumes @in_dir is a directory. Keeps trailing '/'.
|
||||
* If the path was already at the root directory, @out_dir will be an empty string.
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Can call strlcpy if (@out_dir != @in_dir)
|
||||
* - Calls strlen if (@out_dir == @in_dir)
|
||||
* - Calls path_parent_dir()
|
||||
**/
|
||||
void fill_pathname_parent_dir(char *out_dir,
|
||||
const char *in_dir, size_t size);
|
||||
|
@ -373,30 +466,53 @@ void fill_pathname_resolve_relative(char *out_path, const char *in_refpath,
|
|||
* @size : size of output path
|
||||
*
|
||||
* Joins a directory (@dir) and path (@path) together.
|
||||
* Makes sure not to get two consecutive slashes
|
||||
* Makes sure not to get two consecutive slashes
|
||||
* between directory and path.
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - calls strlcpy
|
||||
* - calls fill_pathname_slash()
|
||||
* - calls strlcat
|
||||
*
|
||||
* Deprecated. Use fill_pathname_join_special() instead
|
||||
* if you can ensure @dir != @out_path
|
||||
*
|
||||
* @return Length of the string copied into @out_path
|
||||
**/
|
||||
size_t fill_pathname_join(char *out_path, const char *dir,
|
||||
const char *path, size_t size);
|
||||
|
||||
/**
|
||||
* fill_pathname_join_special:
|
||||
* @out_path : output path
|
||||
* @dir : directory. Cannot be identical to @out_path
|
||||
* @path : path
|
||||
* @size : size of output path
|
||||
*
|
||||
*
|
||||
* Specialized version of fill_pathname_join.
|
||||
* Unlike fill_pathname_join(),
|
||||
* @dir and @out_path CANNOT be identical.
|
||||
*
|
||||
* Joins a directory (@dir) and path (@path) together.
|
||||
* Makes sure not to get two consecutive slashes
|
||||
* between directory and path.
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - calls strlcpy
|
||||
* - calls find_last_slash()
|
||||
* - calls strlcat
|
||||
*
|
||||
* @return Length of the string copied into @out_path
|
||||
**/
|
||||
size_t fill_pathname_join_special(char *out_path,
|
||||
const char *dir, const char *path, size_t size);
|
||||
|
||||
size_t fill_pathname_join_special_ext(char *out_path,
|
||||
const char *dir, const char *path,
|
||||
const char *last, const char *ext,
|
||||
size_t size);
|
||||
|
||||
size_t fill_pathname_join_concat_noext(char *out_path,
|
||||
const char *dir, const char *path,
|
||||
const char *concat,
|
||||
size_t size);
|
||||
|
||||
size_t 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
|
||||
|
@ -407,40 +523,58 @@ void fill_pathname_join_noext(char *out_path,
|
|||
*
|
||||
* Joins a directory (@dir) and path (@path) together
|
||||
* using the given delimiter (@delim).
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - can call strlen
|
||||
* - can call strlcpy
|
||||
* - can call strlcat
|
||||
**/
|
||||
size_t fill_pathname_join_delim(char *out_path, const char *dir,
|
||||
const char *path, const char delim, size_t size);
|
||||
|
||||
size_t fill_pathname_join_delim_concat(char *out_path, const char *dir,
|
||||
const char *path, const char delim, const char *concat,
|
||||
size_t size);
|
||||
size_t fill_pathname_expand_special(char *out_path,
|
||||
const char *in_path, size_t size);
|
||||
|
||||
size_t fill_pathname_abbreviate_special(char *out_path,
|
||||
const char *in_path, size_t size);
|
||||
|
||||
/**
|
||||
* fill_short_pathname_representation:
|
||||
* @out_rep : output representation
|
||||
* @in_path : input path
|
||||
* @size : size of output representation
|
||||
* fill_pathname_abbreviated_or_relative:
|
||||
*
|
||||
* 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.
|
||||
* Fills the supplied path with either the abbreviated path or
|
||||
* the relative path, which ever one has less depth / number of slashes
|
||||
*
|
||||
* If lengths of abbreviated and relative paths are the same,
|
||||
* the relative path will be used
|
||||
* @in_path can be an absolute, relative or abbreviated path
|
||||
*
|
||||
* E.g.: "/path/to/game.img" -> game.img
|
||||
* "/path/to/myarchive.7z#folder/to/game.img" -> game.img
|
||||
*/
|
||||
size_t fill_short_pathname_representation(char* out_rep,
|
||||
const char *in_path, size_t size);
|
||||
* @return Length of the string copied into @out_path
|
||||
**/
|
||||
size_t fill_pathname_abbreviated_or_relative(char *out_path,
|
||||
const char *in_refpath, const char *in_path, size_t size);
|
||||
|
||||
void fill_short_pathname_representation_noext(char* out_rep,
|
||||
const char *in_path, size_t size);
|
||||
/**
|
||||
* pathname_conform_slashes_to_os:
|
||||
*
|
||||
* @path : path
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Changes the slashes to the correct kind for the os
|
||||
* So forward slash on linux and backslash on Windows
|
||||
**/
|
||||
void pathname_conform_slashes_to_os(char *path);
|
||||
|
||||
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);
|
||||
/**
|
||||
* pathname_make_slashes_portable:
|
||||
* @path : path
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Change all slashes to forward so they are more
|
||||
* portable between Windows and Linux
|
||||
**/
|
||||
void pathname_make_slashes_portable(char *path);
|
||||
|
||||
/**
|
||||
* path_basedir:
|
||||
|
@ -457,8 +591,8 @@ void path_basedir_wrapper(char *path);
|
|||
*
|
||||
* Checks if character (@c) is a slash.
|
||||
*
|
||||
* Returns: true (1) if character is a slash, otherwise false (0).
|
||||
*/
|
||||
* @return true if character is a slash, otherwise false.
|
||||
**/
|
||||
#ifdef _WIN32
|
||||
#define PATH_CHAR_IS_SLASH(c) (((c) == '/') || ((c) == '\\'))
|
||||
#else
|
||||
|
@ -470,8 +604,8 @@ void path_basedir_wrapper(char *path);
|
|||
*
|
||||
* Gets the default slash separator.
|
||||
*
|
||||
* Returns: default slash separator.
|
||||
*/
|
||||
* @return default slash separator.
|
||||
**/
|
||||
#ifdef _WIN32
|
||||
#define PATH_DEFAULT_SLASH() "\\"
|
||||
#define PATH_DEFAULT_SLASH_C() '\\'
|
||||
|
@ -487,6 +621,11 @@ void path_basedir_wrapper(char *path);
|
|||
*
|
||||
* Assumes path is a directory. Appends a slash
|
||||
* if not already there.
|
||||
|
||||
* Hidden non-leaf function cost:
|
||||
* - calls find_last_slash()
|
||||
* - can call strlcat once if it returns false
|
||||
* - calls strlen
|
||||
**/
|
||||
void fill_pathname_slash(char *path, size_t size);
|
||||
|
||||
|
@ -502,7 +641,16 @@ void fill_pathname_home_dir(char *buf, size_t size);
|
|||
*
|
||||
* Create directory on filesystem.
|
||||
*
|
||||
* Returns: true (1) if directory could be created, otherwise false (0).
|
||||
* Recursive function.
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls strdup
|
||||
* - Calls path_parent_dir()
|
||||
* - Calls strcmp
|
||||
* - Calls path_is_directory()
|
||||
* - Calls path_mkdir()
|
||||
*
|
||||
* @return true if directory could be created, otherwise false.
|
||||
**/
|
||||
bool path_mkdir(const char *dir);
|
||||
|
||||
|
@ -512,7 +660,7 @@ bool path_mkdir(const char *dir);
|
|||
*
|
||||
* Checks if path is a directory.
|
||||
*
|
||||
* Returns: true (1) if path is a directory, otherwise false (0).
|
||||
* @return true if path is a directory, otherwise false.
|
||||
*/
|
||||
bool path_is_directory(const char *path);
|
||||
|
||||
|
|
|
@ -28,6 +28,9 @@ typedef unsigned long long int GLuint64;
|
|||
typedef unsigned long long int GLuint64EXT;
|
||||
typedef struct __GLsync *GLsync;
|
||||
#endif
|
||||
#ifndef GL_APIENTRYP
|
||||
#define GL_APIENTRYP GL_APIENTRY*
|
||||
#endif
|
||||
typedef void (GL_APIENTRYP RGLSYMGLBLENDBARRIERKHRPROC) (void);
|
||||
typedef void (GL_APIENTRYP RGLSYMGLDEBUGMESSAGECONTROLKHRPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled);
|
||||
typedef void (GL_APIENTRYP RGLSYMGLDEBUGMESSAGEINSERTKHRPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf);
|
||||
|
|
|
@ -1767,6 +1767,33 @@ enum retro_mod
|
|||
* (see enum retro_savestate_context)
|
||||
*/
|
||||
|
||||
#define RETRO_ENVIRONMENT_GET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_SUPPORT (73 | RETRO_ENVIRONMENT_EXPERIMENTAL)
|
||||
/* struct retro_hw_render_context_negotiation_interface * --
|
||||
* Before calling SET_HW_RNEDER_CONTEXT_NEGOTIATION_INTERFACE, a core can query
|
||||
* which version of the interface is supported.
|
||||
*
|
||||
* Frontend looks at interface_type and returns the maximum supported
|
||||
* context negotiation interface version.
|
||||
* If the interface_type is not supported or recognized by the frontend, a version of 0
|
||||
* must be returned in interface_version and true is returned by frontend.
|
||||
*
|
||||
* If this environment call returns true with interface_version greater than 0,
|
||||
* a core can always use a negotiation interface version larger than what the frontend returns, but only
|
||||
* earlier versions of the interface will be used by the frontend.
|
||||
* A frontend must not reject a negotiation interface version that is larger than
|
||||
* what the frontend supports. Instead, the frontend will use the older entry points that it recognizes.
|
||||
* If this is incompatible with a particular core's requirements, it can error out early.
|
||||
*
|
||||
* Backwards compatibility note:
|
||||
* This environment call was introduced after Vulkan v1 context negotiation.
|
||||
* If this environment call is not supported by frontend - i.e. the environment call returns false -
|
||||
* only Vulkan v1 context negotiation is supported (if Vulkan HW rendering is supported at all).
|
||||
* If a core uses Vulkan negotiation interface with version > 1, negotiation may fail unexpectedly.
|
||||
* All future updates to the context negotiation interface implies that frontend must support
|
||||
* this environment call to query support.
|
||||
*/
|
||||
|
||||
|
||||
/* VFS functionality */
|
||||
|
||||
/* File paths:
|
||||
|
@ -3011,13 +3038,13 @@ enum retro_savestate_context
|
|||
|
||||
/* Savestate where you are guaranteed that the same instance will load the save state.
|
||||
* You can store internal pointers to code or data.
|
||||
* It's still a full serialization and deserialization, and could be loaded or saved at any time.
|
||||
* It's still a full serialization and deserialization, and could be loaded or saved at any time.
|
||||
* It won't be written to disk or sent over the network.
|
||||
*/
|
||||
RETRO_SAVESTATE_CONTEXT_RUNAHEAD_SAME_INSTANCE = 1,
|
||||
|
||||
/* Savestate where you are guaranteed that the same emulator binary will load that savestate.
|
||||
* You can skip anything that would slow down saving or loading state but you can not store internal pointers.
|
||||
* You can skip anything that would slow down saving or loading state but you can not store internal pointers.
|
||||
* It won't be written to disk or sent over the network.
|
||||
* Example: "Second Instance" runahead
|
||||
*/
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#include <vulkan/vulkan.h>
|
||||
|
||||
#define RETRO_HW_RENDER_INTERFACE_VULKAN_VERSION 5
|
||||
#define RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION 1
|
||||
#define RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION 2
|
||||
|
||||
struct retro_vulkan_image
|
||||
{
|
||||
|
@ -64,6 +64,8 @@ struct retro_vulkan_context
|
|||
uint32_t presentation_queue_family_index;
|
||||
};
|
||||
|
||||
/* This is only used in v1 of the negotiation interface.
|
||||
* It is deprecated since it cannot express PDF2 features or optional extensions. */
|
||||
typedef bool (*retro_vulkan_create_device_t)(
|
||||
struct retro_vulkan_context *context,
|
||||
VkInstance instance,
|
||||
|
@ -78,6 +80,32 @@ typedef bool (*retro_vulkan_create_device_t)(
|
|||
|
||||
typedef void (*retro_vulkan_destroy_device_t)(void);
|
||||
|
||||
/* v2 CONTEXT_NEGOTIATION_INTERFACE only. */
|
||||
typedef VkInstance (*retro_vulkan_create_instance_wrapper_t)(
|
||||
void *opaque, const VkInstanceCreateInfo *create_info);
|
||||
|
||||
/* v2 CONTEXT_NEGOTIATION_INTERFACE only. */
|
||||
typedef VkInstance (*retro_vulkan_create_instance_t)(
|
||||
PFN_vkGetInstanceProcAddr get_instance_proc_addr,
|
||||
const VkApplicationInfo *app,
|
||||
retro_vulkan_create_instance_wrapper_t create_instance_wrapper,
|
||||
void *opaque);
|
||||
|
||||
/* v2 CONTEXT_NEGOTIATION_INTERFACE only. */
|
||||
typedef VkDevice (*retro_vulkan_create_device_wrapper_t)(
|
||||
VkPhysicalDevice gpu, void *opaque,
|
||||
const VkDeviceCreateInfo *create_info);
|
||||
|
||||
/* v2 CONTEXT_NEGOTIATION_INTERFACE only. */
|
||||
typedef bool (*retro_vulkan_create_device2_t)(
|
||||
struct retro_vulkan_context *context,
|
||||
VkInstance instance,
|
||||
VkPhysicalDevice gpu,
|
||||
VkSurfaceKHR surface,
|
||||
PFN_vkGetInstanceProcAddr get_instance_proc_addr,
|
||||
retro_vulkan_create_device_wrapper_t create_device_wrapper,
|
||||
void *opaque);
|
||||
|
||||
/* Note on thread safety:
|
||||
* The Vulkan API is heavily designed around multi-threading, and
|
||||
* the libretro interface for it should also be threading friendly.
|
||||
|
@ -89,11 +117,24 @@ struct retro_hw_render_context_negotiation_interface_vulkan
|
|||
{
|
||||
/* Must be set to RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN. */
|
||||
enum retro_hw_render_context_negotiation_interface_type interface_type;
|
||||
/* Must be set to RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION. */
|
||||
/* Usually set to RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION,
|
||||
* but can be lower depending on GET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_SUPPORT. */
|
||||
unsigned interface_version;
|
||||
|
||||
/* If non-NULL, returns a VkApplicationInfo struct that the frontend can use instead of
|
||||
* its "default" application info.
|
||||
* VkApplicationInfo::apiVersion also controls the target core Vulkan version for instance level functionality.
|
||||
* Lifetime of the returned pointer must remain until the retro_vulkan_context is initialized.
|
||||
*
|
||||
* NOTE: For optimal compatibility with e.g. Android which is very slow to update its loader,
|
||||
* a core version of 1.1 should be requested. Features beyond that can be requested with extensions.
|
||||
* Vulkan 1.0 is only appropriate for legacy cores, but is still supported.
|
||||
* A frontend is free to bump the instance creation apiVersion as necessary if the frontend requires more advanced core features.
|
||||
*
|
||||
* v2: This function must not be NULL, and must not return NULL.
|
||||
* v1: It was not clearly defined if this function could return NULL.
|
||||
* Frontends should be defensive and provide a default VkApplicationInfo
|
||||
* if this function returns NULL or if this function is NULL.
|
||||
*/
|
||||
retro_vulkan_get_application_info_t get_application_info;
|
||||
|
||||
|
@ -102,8 +143,8 @@ struct retro_hw_render_context_negotiation_interface_vulkan
|
|||
* The core must prepare a designated PhysicalDevice, Device, Queue and queue family index
|
||||
* which the frontend will use for its internal operation.
|
||||
*
|
||||
* If gpu is not VK_NULL_HANDLE, the physical device provided to the frontend must be this PhysicalDevice.
|
||||
* The core is still free to use other physical devices.
|
||||
* If gpu is not VK_NULL_HANDLE, the physical device provided to the frontend must be this PhysicalDevice if the call succeeds.
|
||||
* The core is still free to use other physical devices for other purposes that are private to the core.
|
||||
*
|
||||
* The frontend will request certain extensions and layers for a device which is created.
|
||||
* The core must ensure that the queue and queue_family_index support GRAPHICS and COMPUTE.
|
||||
|
@ -132,8 +173,64 @@ struct retro_hw_render_context_negotiation_interface_vulkan
|
|||
* tearing down its own device resources.
|
||||
*
|
||||
* Only auxillary resources should be freed here, i.e. resources which are not part of retro_vulkan_context.
|
||||
* v2: Auxillary instance resources created during create_instance can also be freed here.
|
||||
*/
|
||||
retro_vulkan_destroy_device_t destroy_device;
|
||||
|
||||
/* v2 API: If interface_version is < 2, fields below must be ignored.
|
||||
* If the frontend does not support interface version 2, the v1 entry points will be used instead. */
|
||||
|
||||
/* If non-NULL, this is called to create an instance, otherwise a VkInstance is created by the frontend.
|
||||
* v1 interface bug: The only way to enable instance features is through core versions signalled in VkApplicationInfo.
|
||||
* The frontend may request that certain extensions and layers
|
||||
* are enabled on the VkInstance. Application may add additional features.
|
||||
* If app is non-NULL, apiVersion controls the minimum core version required by the application.
|
||||
* Return a VkInstance or VK_NULL_HANDLE. The VkInstance is owned by the frontend.
|
||||
*
|
||||
* Rather than call vkCreateInstance directly, a core must call the CreateInstance wrapper provided with:
|
||||
* VkInstance instance = create_instance_wrapper(opaque, &create_info);
|
||||
* If the core wishes to create a private instance for whatever reason (relying on shared memory for example),
|
||||
* it may call vkCreateInstance directly. */
|
||||
retro_vulkan_create_instance_t create_instance;
|
||||
|
||||
/* If non-NULL and frontend recognizes negotiation interface >= 2, create_device2 takes precedence over create_device.
|
||||
* Similar to create_device, but is extended to better understand new core versions and PDF2 feature enablement.
|
||||
* Requirements for create_device2 are the same as create_device unless a difference is mentioned.
|
||||
*
|
||||
* v2 consideration:
|
||||
* If the chosen gpu by frontend cannot be supported, a core must return false.
|
||||
*
|
||||
* NOTE: "Cannot be supported" is intentionally vaguely defined.
|
||||
* Refusing to run on an iGPU for a very intensive core with desktop GPU as a minimum spec may be in the gray area.
|
||||
* Not supporting optional features is not a good reason to reject a physical device, however.
|
||||
*
|
||||
* On device creation feature with explicit gpu, a frontend should fall back create_device2 with gpu == VK_NULL_HANDLE and let core
|
||||
* decide on a supported device if possible.
|
||||
*
|
||||
* A core must assume that the explicitly provided GPU is the only guaranteed attempt it has to create a device.
|
||||
* A fallback may not be attempted if there are particular reasons why only a specific physical device can work,
|
||||
* but these situations should be esoteric and rare in nature, e.g. a libretro frontend is implemented with external memory
|
||||
* and only LUID matching would work.
|
||||
* Cores and frontends should ensure "best effort" when negotiating like this and appropriate logging is encouraged.
|
||||
*
|
||||
* v1 note: In the v1 version of create_device, it was never expected that create_device would fail like this,
|
||||
* and frontends are not expected to attempt fall backs.
|
||||
*
|
||||
* Rather than call vkCreateDevice directly, a core must call the CreateDevice wrapper provided with:
|
||||
* VkDevice device = create_device_wrapper(gpu, opaque, &create_info);
|
||||
* If the core wishes to create a private device for whatever reason (relying on shared memory for example),
|
||||
* it may call vkCreateDevice directly.
|
||||
*
|
||||
* This allows the frontend to add additional extensions that it requires as well as adjust the PDF2 pNext as required.
|
||||
* It is also possible adjust the queue create infos in case the frontend desires to allocate some private queues.
|
||||
*
|
||||
* The get_instance_proc_addr provided in create_device2 must be the same as create_instance.
|
||||
*
|
||||
* NOTE: The frontend must not disable features requested by application.
|
||||
* NOTE: The frontend must not add any robustness features as some API behavior may change (VK_EXT_descriptor_buffer comes to mind).
|
||||
* I.e. robustBufferAccess and the like. (nullDescriptor from robustness2 is allowed to be enabled).
|
||||
*/
|
||||
retro_vulkan_create_device2_t create_device2;
|
||||
};
|
||||
|
||||
struct retro_hw_render_interface_vulkan
|
||||
|
|
|
@ -42,7 +42,7 @@ RETRO_BEGIN_DECLS
|
|||
*
|
||||
* Create a directory listing, appending to an existing list
|
||||
*
|
||||
* Returns: true success, false in case of error.
|
||||
* @return Returns true on success, otherwise false.
|
||||
**/
|
||||
bool dir_list_append(struct string_list *list, const char *dir, const char *ext,
|
||||
bool include_dirs, bool include_hidden, bool include_compressed, bool recursive);
|
||||
|
@ -58,15 +58,18 @@ bool dir_list_append(struct string_list *list, const char *dir, const char *ext,
|
|||
*
|
||||
* Create a directory listing.
|
||||
*
|
||||
* Returns: pointer to a directory listing of type 'struct string_list *' on success,
|
||||
* @return pointer to a directory listing of type 'struct string_list *' on success,
|
||||
* NULL in case of error. Has to be freed manually.
|
||||
**/
|
||||
struct string_list *dir_list_new(const char *dir, const char *ext,
|
||||
bool include_dirs, bool include_hidden, bool include_compressed, bool recursive);
|
||||
|
||||
/* Warning: 'list' must zero initialised before
|
||||
* calling this function, otherwise memory leaks/
|
||||
* undefined behaviour will occur */
|
||||
/**
|
||||
* dir_list_initialize:
|
||||
*
|
||||
* NOTE: @list must zero initialised before
|
||||
* calling this function, otherwise UB.
|
||||
**/
|
||||
bool dir_list_initialize(struct string_list *list,
|
||||
const char *dir,
|
||||
const char *ext, bool include_dirs,
|
||||
|
@ -79,7 +82,6 @@ bool dir_list_initialize(struct string_list *list,
|
|||
* @dir_first : move the directories in the listing to the top?
|
||||
*
|
||||
* Sorts a directory listing.
|
||||
*
|
||||
**/
|
||||
void dir_list_sort(struct string_list *list, bool dir_first);
|
||||
|
||||
|
@ -88,7 +90,6 @@ void dir_list_sort(struct string_list *list, bool dir_first);
|
|||
* @list : pointer to the directory listing
|
||||
*
|
||||
* Frees a directory listing.
|
||||
*
|
||||
**/
|
||||
void dir_list_free(struct string_list *list);
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ struct string_list
|
|||
*
|
||||
* Searches for an element (@elem) inside the string list.
|
||||
*
|
||||
* Returns: true (1) if element could be found, otherwise false (0).
|
||||
* @return Number of elements found, otherwise 0.
|
||||
*/
|
||||
int string_list_find_elem(const struct string_list *list, const char *elem);
|
||||
|
||||
|
@ -100,8 +100,8 @@ bool string_split_noalloc(struct string_list *list,
|
|||
* Includes empty strings - i.e. two adjacent delimiters will resolve
|
||||
* to a string list element of "".
|
||||
*
|
||||
* Returns: new string list if successful, otherwise NULL.
|
||||
*/
|
||||
* @return New string list if successful, otherwise NULL.
|
||||
**/
|
||||
struct string_list *string_separate(char *str, const char *delim);
|
||||
|
||||
bool string_separate_noalloc(struct string_list *list,
|
||||
|
@ -116,8 +116,8 @@ bool string_list_initialize(struct string_list *list);
|
|||
*
|
||||
* Creates a new string list. Has to be freed manually.
|
||||
*
|
||||
* Returns: new string list if successful, otherwise NULL.
|
||||
*/
|
||||
* @return New string list if successful, otherwise NULL.
|
||||
**/
|
||||
struct string_list *string_list_new(void);
|
||||
|
||||
/**
|
||||
|
@ -127,8 +127,12 @@ struct string_list *string_list_new(void);
|
|||
* @attr : attributes of new element.
|
||||
*
|
||||
* Appends a new element to the string list.
|
||||
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls string_list_capacity()
|
||||
* - Calls strdup
|
||||
*
|
||||
* Returns: true (1) if successful, otherwise false (0).
|
||||
* @return true if successful, otherwise false.
|
||||
**/
|
||||
bool string_list_append(struct string_list *list, const char *elem,
|
||||
union string_list_elem_attr attr);
|
||||
|
@ -142,7 +146,12 @@ bool string_list_append(struct string_list *list, const char *elem,
|
|||
*
|
||||
* Appends a new element to the string list.
|
||||
*
|
||||
* Returns: true (1) if successful, otherwise false (0).
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls string_list_capacity()
|
||||
* - Calls malloc
|
||||
* - Calls strlcpy
|
||||
*
|
||||
* @return true if successful, otherwise false.
|
||||
**/
|
||||
bool string_list_append_n(struct string_list *list, const char *elem,
|
||||
unsigned length, union string_list_elem_attr attr);
|
||||
|
@ -152,7 +161,7 @@ bool string_list_append_n(struct string_list *list, const char *elem,
|
|||
* @list : pointer to string list object
|
||||
*
|
||||
* Frees a string list.
|
||||
*/
|
||||
**/
|
||||
void string_list_free(struct string_list *list);
|
||||
|
||||
/**
|
||||
|
@ -164,7 +173,13 @@ void string_list_free(struct string_list *list);
|
|||
*
|
||||
* A string list will be joined/concatenated as a
|
||||
* string to @buffer, delimited by @delim.
|
||||
*/
|
||||
*
|
||||
* NOTE: @buffer must be NULL-terminated.
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls strlen_size()
|
||||
* - Calls strlcat x times in a loop
|
||||
**/
|
||||
void string_list_join_concat(char *buffer, size_t size,
|
||||
const struct string_list *list, const char *sep);
|
||||
|
||||
|
@ -175,6 +190,10 @@ void string_list_join_concat(char *buffer, size_t size,
|
|||
* @str : value for the element.
|
||||
*
|
||||
* Set value of element inside string list.
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls free
|
||||
* - Calls strdup
|
||||
**/
|
||||
void string_list_set(struct string_list *list, unsigned idx,
|
||||
const char *str);
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(__CELLOS_LV2__) || defined(PSP) || defined(PS2) || defined(GEKKO) || defined(VITA) || defined(_XBOX) || defined(_3DS) || defined(WIIU) || defined(SWITCH) || defined(HAVE_LIBNX)
|
||||
#if defined(PSP) || defined(PS2) || defined(GEKKO) || defined(VITA) || defined(_XBOX) || defined(_3DS) || defined(WIIU) || defined(SWITCH) || defined(HAVE_LIBNX) || defined(__PS3__) || defined(__PSL1GHT__)
|
||||
/* No mman available */
|
||||
#elif defined(_WIN32) && !defined(_XBOX)
|
||||
#include <windows.h>
|
||||
|
|
|
@ -27,9 +27,7 @@
|
|||
|
||||
#ifdef RARCH_INTERNAL
|
||||
#include <stdio.h>
|
||||
#define retro_assert(cond) do { \
|
||||
if (!(cond)) { printf("Assertion failed at %s:%d.\n", __FILE__, __LINE__); abort(); } \
|
||||
} while(0)
|
||||
#define retro_assert(cond) ((void)( (cond) || (printf("Assertion failed at %s:%d.\n", __FILE__, __LINE__), abort(), 0) ))
|
||||
#else
|
||||
#define retro_assert(cond) assert(cond)
|
||||
#endif
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
|
||||
#include <libretro.h>
|
||||
#include <retro_common_api.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
|
||||
#include <boolean.h>
|
||||
|
||||
|
|
|
@ -74,31 +74,29 @@ static INLINE uint64_t SWAP64(uint64_t val)
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined (LSB_FIRST) || defined (MSB_FIRST)
|
||||
# warning Defining MSB_FIRST and LSB_FIRST in compile options is deprecated
|
||||
# undef LSB_FIRST
|
||||
# undef MSB_FIRST
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_XBOX)
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#if _M_IX86 || _M_AMD64 || _M_ARM || _M_ARM64
|
||||
/* MSVC pre-defines macros depending on target arch */
|
||||
#if defined (_M_IX86) || defined (_M_AMD64) || defined (_M_ARM) || defined (_M_ARM64)
|
||||
#ifndef LSB_FIRST
|
||||
#define LSB_FIRST 1
|
||||
#endif
|
||||
#elif _M_PPC
|
||||
#ifndef MSB_FIRST
|
||||
#define MSB_FIRST 1
|
||||
#endif
|
||||
#else
|
||||
/* MSVC can run on _M_ALPHA and _M_IA64 too, but they're both bi-endian; need to find what mode MSVC runs them at */
|
||||
#error "unknown platform, can't determine endianness"
|
||||
#endif
|
||||
#else
|
||||
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
#ifndef MSB_FIRST
|
||||
#define MSB_FIRST 1
|
||||
#endif
|
||||
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
#ifndef LSB_FIRST
|
||||
#define LSB_FIRST 1
|
||||
#endif
|
||||
#else
|
||||
#error "Invalid endianness macros"
|
||||
#endif
|
||||
|
|
|
@ -91,4 +91,100 @@ static INLINE uint32_t prev_pow2(uint32_t v)
|
|||
return v - (v >> 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* clamp:
|
||||
* @v : initial value
|
||||
*
|
||||
* Get the clamped value based on initial value.
|
||||
*
|
||||
* Returns: clamped value (derived from @v).
|
||||
**/
|
||||
static INLINE float clamp_value(float v, float min, float max)
|
||||
{
|
||||
return v <= min ? min : v >= max ? max : v;
|
||||
}
|
||||
|
||||
/**
|
||||
* saturate_value:
|
||||
* @v : initial value
|
||||
*
|
||||
* Get the clamped 0.0-1.0 value based on initial value.
|
||||
*
|
||||
* Returns: clamped 0.0-1.0 value (derived from @v).
|
||||
**/
|
||||
static INLINE float saturate_value(float v)
|
||||
{
|
||||
return clamp_value(v, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* dot_product:
|
||||
* @a : left hand vector value
|
||||
* @b : right hand vector value
|
||||
*
|
||||
* Get the dot product of the two passed in vectors.
|
||||
*
|
||||
* Returns: dot product value (derived from @a and @b).
|
||||
**/
|
||||
static INLINE float dot_product(const float* a, const float* b)
|
||||
{
|
||||
return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* convert_rgb_to_yxy:
|
||||
* @rgb : in RGB colour space value
|
||||
* @Yxy : out Yxy colour space value
|
||||
*
|
||||
* Convert from RGB colour space to Yxy colour space.
|
||||
*
|
||||
* Returns: Yxy colour space value (derived from @rgb).
|
||||
**/
|
||||
static INLINE void convert_rgb_to_yxy(const float* rgb, float* Yxy)
|
||||
{
|
||||
float inv;
|
||||
float xyz[3];
|
||||
float one[3] = {1.0, 1.0, 1.0};
|
||||
float rgb_xyz[3][3] = {
|
||||
{0.4124564, 0.3575761, 0.1804375},
|
||||
{0.2126729, 0.7151522, 0.0721750},
|
||||
{0.0193339, 0.1191920, 0.9503041}
|
||||
};
|
||||
|
||||
xyz[0] = dot_product(rgb_xyz[0], rgb);
|
||||
xyz[1] = dot_product(rgb_xyz[1], rgb);
|
||||
xyz[2] = dot_product(rgb_xyz[2], rgb);
|
||||
|
||||
inv = 1.0f / dot_product(xyz, one);
|
||||
Yxy[0] = xyz[1];
|
||||
Yxy[1] = xyz[0] * inv;
|
||||
Yxy[2] = xyz[1] * inv;
|
||||
}
|
||||
|
||||
/**
|
||||
* convert_yxy_to_rgb:
|
||||
* @rgb : in Yxy colour space value
|
||||
* @Yxy : out rgb colour space value
|
||||
*
|
||||
* Convert from Yxy colour space to rgb colour space.
|
||||
*
|
||||
* Returns: rgb colour space value (derived from @Yxy).
|
||||
**/
|
||||
static INLINE void convert_yxy_to_rgb(const float* Yxy, float* rgb)
|
||||
{
|
||||
float xyz[3];
|
||||
float xyz_rgb[3][3] = {
|
||||
{3.2404542, -1.5371385, -0.4985314},
|
||||
{-0.9692660, 1.8760108, 0.0415560},
|
||||
{0.0556434, -0.2040259, 1.0572252}
|
||||
};
|
||||
xyz[0] = Yxy[0] * Yxy[1] / Yxy[2];
|
||||
xyz[1] = Yxy[0];
|
||||
xyz[2] = Yxy[0] * (1.0 - Yxy[1] - Yxy[2]) / Yxy[2];
|
||||
|
||||
rgb[0] = dot_product(xyz_rgb[0], xyz);
|
||||
rgb[1] = dot_product(xyz_rgb[1], xyz);
|
||||
rgb[2] = dot_product(xyz_rgb[2], xyz);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -43,10 +43,6 @@
|
|||
|
||||
#endif
|
||||
|
||||
#if defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
|
||||
#include <sys/fs_external.h>
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
@ -78,16 +74,29 @@ static INLINE bool bits_any_set(uint32_t* ptr, uint32_t count)
|
|||
return false;
|
||||
}
|
||||
|
||||
static INLINE bool bits_any_different(uint32_t *a, uint32_t *b, uint32_t count)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (a[i] != b[i])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef PATH_MAX_LENGTH
|
||||
#if defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
|
||||
#define PATH_MAX_LENGTH CELL_FS_MAX_FS_PATH_LENGTH
|
||||
#elif defined(_XBOX1) || defined(_3DS) || defined(PSP) || defined(PS2) || defined(GEKKO)|| defined(WIIU) || defined(ORBIS)
|
||||
#if defined(_XBOX1) || defined(_3DS) || defined(PSP) || defined(PS2) || defined(GEKKO)|| defined(WIIU) || defined(__PSL1GHT__) || defined(__PS3__)
|
||||
#define PATH_MAX_LENGTH 512
|
||||
#else
|
||||
#define PATH_MAX_LENGTH 4096
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef NAME_MAX_LENGTH
|
||||
#define NAME_MAX_LENGTH 256
|
||||
#endif
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
|
@ -140,6 +149,16 @@ static INLINE bool bits_any_set(uint32_t* ptr, uint32_t count)
|
|||
#define BIT256_GET_PTR(a, bit) BIT256_GET(*a, bit)
|
||||
#define BIT256_CLEAR_ALL_PTR(a) BIT256_CLEAR_ALL(*a)
|
||||
|
||||
#define BIT512_SET(a, bit) BIT256_SET(a, bit)
|
||||
#define BIT512_CLEAR(a, bit) BIT256_CLEAR(a, bit)
|
||||
#define BIT512_GET(a, bit) BIT256_GET(a, bit)
|
||||
#define BIT512_CLEAR_ALL(a) BIT256_CLEAR_ALL(a)
|
||||
|
||||
#define BIT512_SET_PTR(a, bit) BIT512_SET(*a, bit)
|
||||
#define BIT512_CLEAR_PTR(a, bit) BIT512_CLEAR(*a, bit)
|
||||
#define BIT512_GET_PTR(a, bit) BIT512_GET(*a, bit)
|
||||
#define BIT512_CLEAR_ALL_PTR(a) BIT512_CLEAR_ALL(*a)
|
||||
|
||||
#define BITS_COPY16_PTR(a,bits) \
|
||||
{ \
|
||||
BIT128_CLEAR_ALL_PTR(a); \
|
||||
|
@ -152,6 +171,13 @@ static INLINE bool bits_any_set(uint32_t* ptr, uint32_t count)
|
|||
BITS_GET_ELEM_PTR(a, 0) = (bits); \
|
||||
}
|
||||
|
||||
#define BITS_COPY64_PTR(a,bits) \
|
||||
{ \
|
||||
BIT128_CLEAR_ALL_PTR(a); \
|
||||
BITS_GET_ELEM_PTR(a, 0) = (bits); \
|
||||
BITS_GET_ELEM_PTR(a, 1) = (bits >> 32); \
|
||||
}
|
||||
|
||||
/* Helper macros and struct to keep track of many booleans. */
|
||||
/* This struct has 256 bits. */
|
||||
typedef struct
|
||||
|
@ -159,6 +185,12 @@ typedef struct
|
|||
uint32_t data[8];
|
||||
} retro_bits_t;
|
||||
|
||||
/* This struct has 512 bits. */
|
||||
typedef struct
|
||||
{
|
||||
uint32_t data[16];
|
||||
} retro_bits_512_t;
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifdef _WIN64
|
||||
# define PRI_SIZET PRIu64
|
||||
|
|
|
@ -25,10 +25,10 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
|
||||
#include <sys/timer.h>
|
||||
#elif defined(XENON)
|
||||
#if defined(XENON)
|
||||
#include <time/time.h>
|
||||
#elif !defined(__PSL1GHT__) && defined(__PS3__)
|
||||
#include <sys/timer.h>
|
||||
#elif defined(GEKKO) || defined(__PSL1GHT__) || defined(__QNX__)
|
||||
#include <unistd.h>
|
||||
#elif defined(WIIU)
|
||||
|
@ -83,9 +83,7 @@ static int nanosleepDOS(const struct timespec *rqtp, struct timespec *rmtp)
|
|||
*
|
||||
* Sleeps for a specified amount of milliseconds (@msec).
|
||||
**/
|
||||
#if defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
|
||||
#define retro_sleep(msec) (sys_timer_usleep(1000 * (msec)))
|
||||
#elif defined(PSP) || defined(VITA)
|
||||
#if defined(VITA)
|
||||
#define retro_sleep(msec) (sceKernelDelayThread(1000 * (msec)))
|
||||
#elif defined(_3DS)
|
||||
#define retro_sleep(msec) (svcSleepThread(1000000 * (s64)(msec)))
|
||||
|
@ -95,6 +93,8 @@ static int nanosleepDOS(const struct timespec *rqtp, struct timespec *rmtp)
|
|||
#define retro_sleep(msec) (Sleep((msec)))
|
||||
#elif defined(XENON)
|
||||
#define retro_sleep(msec) (udelay(1000 * (msec)))
|
||||
#elif !defined(__PSL1GHT__) && defined(__PS3__)
|
||||
#define retro_sleep(msec) (sys_timer_usleep(1000 * (msec)))
|
||||
#elif defined(GEKKO) || defined(__PSL1GHT__) || defined(__QNX__)
|
||||
#define retro_sleep(msec) (usleep(1000 * (msec)))
|
||||
#elif defined(WIIU)
|
||||
|
@ -102,7 +102,7 @@ static int nanosleepDOS(const struct timespec *rqtp, struct timespec *rmtp)
|
|||
#else
|
||||
static INLINE void retro_sleep(unsigned msec)
|
||||
{
|
||||
struct timespec tv = {0};
|
||||
struct timespec tv;
|
||||
tv.tv_sec = msec / 1000;
|
||||
tv.tv_nsec = (msec % 1000) * 1000000;
|
||||
nanosleep(&tv, NULL);
|
||||
|
|
|
@ -59,7 +59,7 @@ int64_t filestream_truncate(RFILE *stream, int64_t length);
|
|||
* @bufsize : optional buffer size (-1 or 0 to use default)
|
||||
*
|
||||
* Opens a file for reading or writing, depending on the requested mode.
|
||||
* Returns a pointer to an RFILE if opened successfully, otherwise NULL.
|
||||
* @return A pointer to an RFILE if opened successfully, otherwise NULL.
|
||||
**/
|
||||
RFILE* filestream_open(const char *path, unsigned mode, unsigned hints);
|
||||
|
||||
|
@ -75,16 +75,39 @@ void filestream_rewind(RFILE *stream);
|
|||
|
||||
int filestream_close(RFILE *stream);
|
||||
|
||||
/**
|
||||
* filestream_read_file:
|
||||
* @path : path to file.
|
||||
* @buf : buffer to allocate and read the contents of the
|
||||
* file into. Needs to be freed manually.
|
||||
* @len : optional output integer containing bytes read.
|
||||
*
|
||||
* Read the contents of a file into @buf.
|
||||
*
|
||||
* @return Non-zero on success.
|
||||
*/
|
||||
int64_t filestream_read_file(const char *path, void **buf, int64_t *len);
|
||||
|
||||
char* filestream_gets(RFILE *stream, char *s, size_t len);
|
||||
|
||||
int filestream_getc(RFILE *stream);
|
||||
|
||||
int filestream_vscanf(RFILE *stream, const char* format, va_list *args);
|
||||
|
||||
int filestream_scanf(RFILE *stream, const char* format, ...);
|
||||
|
||||
int filestream_eof(RFILE *stream);
|
||||
|
||||
/**
|
||||
* filestream_write_file:
|
||||
* @path : path to file.
|
||||
* @data : contents to write to the file.
|
||||
* @size : size of the contents.
|
||||
*
|
||||
* Writes data to a file.
|
||||
*
|
||||
* @return true on success, otherwise false.
|
||||
**/
|
||||
bool filestream_write_file(const char *path, const void *data, int64_t size);
|
||||
|
||||
int filestream_putc(RFILE *stream, int c);
|
||||
|
@ -105,7 +128,11 @@ const char* filestream_get_path(RFILE *stream);
|
|||
|
||||
bool filestream_exists(const char *path);
|
||||
|
||||
/* Returned pointer must be freed by the caller. */
|
||||
/**
|
||||
* filestream_getline:
|
||||
*
|
||||
* Returned pointer must be freed by the caller.
|
||||
**/
|
||||
char* filestream_getline(RFILE *stream);
|
||||
|
||||
libretro_vfs_implementation_file* filestream_get_vfs_handle(RFILE *stream);
|
||||
|
|
|
@ -44,20 +44,20 @@ RETRO_BEGIN_DECLS
|
|||
#define string_is_not_equal_fast(a, b, size) (memcmp(a, b, size) != 0)
|
||||
#define string_is_equal_fast(a, b, size) (memcmp(a, b, size) == 0)
|
||||
|
||||
#define TOLOWER(c) (c | (lr_char_props[c] & 0x20))
|
||||
#define TOUPPER(c) (c & ~(lr_char_props[c] & 0x20))
|
||||
#define TOLOWER(c) ((c) | (lr_char_props[(unsigned char)(c)] & 0x20))
|
||||
#define TOUPPER(c) ((c) & ~(lr_char_props[(unsigned char)(c)] & 0x20))
|
||||
|
||||
/* C standard says \f \v are space, but this one disagrees */
|
||||
#define ISSPACE(c) (lr_char_props[c] & 0x80)
|
||||
#define ISSPACE(c) (lr_char_props[(unsigned char)(c)] & 0x80)
|
||||
|
||||
#define ISDIGIT(c) (lr_char_props[c] & 0x40)
|
||||
#define ISALPHA(c) (lr_char_props[c] & 0x20)
|
||||
#define ISLOWER(c) (lr_char_props[c] & 0x04)
|
||||
#define ISUPPER(c) (lr_char_props[c] & 0x02)
|
||||
#define ISALNUM(c) (lr_char_props[c] & 0x60)
|
||||
#define ISUALPHA(c) (lr_char_props[c] & 0x28)
|
||||
#define ISUALNUM(c) (lr_char_props[c] & 0x68)
|
||||
#define IS_XDIGIT(c) (lr_char_props[c] & 0x01)
|
||||
#define ISDIGIT(c) (lr_char_props[(unsigned char)(c)] & 0x40)
|
||||
#define ISALPHA(c) (lr_char_props[(unsigned char)(c)] & 0x20)
|
||||
#define ISLOWER(c) (lr_char_props[(unsigned char)(c)] & 0x04)
|
||||
#define ISUPPER(c) (lr_char_props[(unsigned char)(c)] & 0x02)
|
||||
#define ISALNUM(c) (lr_char_props[(unsigned char)(c)] & 0x60)
|
||||
#define ISUALPHA(c) (lr_char_props[(unsigned char)(c)] & 0x28)
|
||||
#define ISUALNUM(c) (lr_char_props[(unsigned char)(c)] & 0x68)
|
||||
#define IS_XDIGIT(c) (lr_char_props[(unsigned char)(c)] & 0x01)
|
||||
|
||||
/* Deprecated alias, all callers should use string_is_equal_case_insensitive instead */
|
||||
#define string_is_equal_noncase string_is_equal_case_insensitive
|
||||
|
@ -92,16 +92,20 @@ static INLINE bool string_ends_with_size(const char *str, const char *suffix,
|
|||
|
||||
static INLINE bool string_ends_with(const char *str, const char *suffix)
|
||||
{
|
||||
if (!str || !suffix)
|
||||
return false;
|
||||
return string_ends_with_size(str, suffix, strlen(str), strlen(suffix));
|
||||
return str && suffix && string_ends_with_size(str, suffix, strlen(str), strlen(suffix));
|
||||
}
|
||||
|
||||
/* Returns the length of 'str' (c.f. strlen()), but only
|
||||
/**
|
||||
* strlen_size:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* @return the length of 'str' (c.f. strlen()), but only
|
||||
* checks the first 'size' characters
|
||||
* - If 'str' is NULL, returns 0
|
||||
* - If 'str' is not NULL and no '\0' character is found
|
||||
* in the first 'size' characters, returns 'size' */
|
||||
* in the first 'size' characters, returns 'size'
|
||||
**/
|
||||
static INLINE size_t strlen_size(const char *str, size_t size)
|
||||
{
|
||||
size_t i = 0;
|
||||
|
@ -130,32 +134,126 @@ static INLINE bool string_is_equal_case_insensitive(const char *a,
|
|||
return (result == 0);
|
||||
}
|
||||
|
||||
static INLINE bool string_starts_with_case_insensitive(const char *str,
|
||||
const char *prefix)
|
||||
{
|
||||
int result = 0;
|
||||
const unsigned char *p1 = (const unsigned char*)str;
|
||||
const unsigned char *p2 = (const unsigned char*)prefix;
|
||||
|
||||
if (!str || !prefix)
|
||||
return false;
|
||||
if (p1 == p2)
|
||||
return true;
|
||||
|
||||
while ((result = tolower (*p1++) - tolower (*p2)) == 0)
|
||||
if (*p2++ == '\0')
|
||||
break;
|
||||
|
||||
return (result == 0 || *p2 == '\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);
|
||||
char *string_replace_substring(const char *in,
|
||||
const char *pattern, size_t pattern_len,
|
||||
const char *replacement, size_t replacement_len);
|
||||
|
||||
/* Remove leading whitespaces */
|
||||
/**
|
||||
* string_trim_whitespace_left:
|
||||
*
|
||||
* Remove leading whitespaces
|
||||
**/
|
||||
char *string_trim_whitespace_left(char *const s);
|
||||
|
||||
/* Remove trailing whitespaces */
|
||||
/**
|
||||
* string_trim_whitespace_right:
|
||||
*
|
||||
* Remove trailing whitespaces
|
||||
**/
|
||||
char *string_trim_whitespace_right(char *const s);
|
||||
|
||||
/* Remove leading and trailing whitespaces */
|
||||
/**
|
||||
* string_trim_whitespace:
|
||||
*
|
||||
* 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);
|
||||
/**
|
||||
* word_wrap:
|
||||
* @dst : pointer to destination buffer.
|
||||
* @dst_size : size of destination buffer.
|
||||
* @src : pointer to input string.
|
||||
* @src_len : length of @src
|
||||
* @line_width : max number of characters per line.
|
||||
* @wideglyph_width : not used, but is necessary to keep
|
||||
* compatibility with word_wrap_wideglyph().
|
||||
* @max_lines : max lines of destination string.
|
||||
* 0 means no limit.
|
||||
*
|
||||
* Wraps string specified by 'src' to destination buffer
|
||||
* specified by 'dst' and 'dst_size'.
|
||||
* This function assumes that all glyphs in the string
|
||||
* have an on-screen pixel width similar to that of
|
||||
* regular Latin characters - i.e. it will not wrap
|
||||
* correctly any text containing so-called 'wide' Unicode
|
||||
* characters (e.g. CJK languages, emojis, etc.).
|
||||
**/
|
||||
void word_wrap(char *dst, size_t dst_size, const char *src, size_t src_len,
|
||||
int line_width, int wideglyph_width, unsigned max_lines);
|
||||
|
||||
/* Splits string into tokens seperated by 'delim'
|
||||
/**
|
||||
* word_wrap_wideglyph:
|
||||
* @dst : pointer to destination buffer.
|
||||
* @dst_size : size of destination buffer.
|
||||
* @src : pointer to input string.
|
||||
* @src_len : length of @src
|
||||
* @line_width : max number of characters per line.
|
||||
* @wideglyph_width : effective width of 'wide' Unicode glyphs.
|
||||
* the value here is normalised relative to the
|
||||
* typical on-screen pixel width of a regular
|
||||
* Latin character:
|
||||
* - a regular Latin character is defined to
|
||||
* have an effective width of 100
|
||||
* - wideglyph_width = 100 * (wide_character_pixel_width / latin_character_pixel_width)
|
||||
* - e.g. if 'wide' Unicode characters in 'src'
|
||||
* have an on-screen pixel width twice that of
|
||||
* regular Latin characters, wideglyph_width
|
||||
* would be 200
|
||||
* @max_lines : max lines of destination string.
|
||||
* 0 means no limit.
|
||||
*
|
||||
* Wraps string specified by @src to destination buffer
|
||||
* specified by @dst and @dst_size.
|
||||
* This function assumes that all glyphs in the string
|
||||
* are:
|
||||
* - EITHER 'non-wide' Unicode glyphs, with an on-screen
|
||||
* pixel width similar to that of regular Latin characters
|
||||
* - OR 'wide' Unicode glyphs (e.g. CJK languages, emojis, etc.)
|
||||
* with an on-screen pixel width defined by @wideglyph_width
|
||||
* Note that wrapping may occur in inappropriate locations
|
||||
* if @src string contains 'wide' Unicode characters whose
|
||||
* on-screen pixel width deviates greatly from the set
|
||||
* @wideglyph_width value.
|
||||
**/
|
||||
void word_wrap_wideglyph(
|
||||
char *dst, size_t dst_size,
|
||||
const char *src, size_t src_len,
|
||||
int line_width, int wideglyph_width,
|
||||
unsigned max_lines);
|
||||
|
||||
/**
|
||||
* string_tokenize:
|
||||
*
|
||||
* 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
|
||||
* > After each call, @str is set to the position after the
|
||||
* last found token
|
||||
* > Tokens *include* empty strings
|
||||
* Usage example:
|
||||
|
@ -168,29 +266,120 @@ char *word_wrap(char *buffer, const char *string,
|
|||
* free(token);
|
||||
* token = NULL;
|
||||
* }
|
||||
*/
|
||||
**/
|
||||
char* string_tokenize(char **str, const char *delim);
|
||||
|
||||
/* Removes every instance of character 'c' from 'str' */
|
||||
/**
|
||||
* string_remove_all_chars:
|
||||
* @str : input string (must be non-NULL, otherwise UB)
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* 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' */
|
||||
/**
|
||||
* string_replace_all_chars:
|
||||
* @str : input string (must be non-NULL, otherwise UB)
|
||||
* @find : character to find
|
||||
* @replace : character to replace @find with
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls strchr (in a loop)
|
||||
*
|
||||
* 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 */
|
||||
/**
|
||||
* string_to_unsigned:
|
||||
* @str : input string
|
||||
*
|
||||
* Converts string to unsigned integer.
|
||||
*
|
||||
* @return 0 if string is invalid, otherwise > 0
|
||||
**/
|
||||
unsigned string_to_unsigned(const char *str);
|
||||
|
||||
/* Converts hexadecimal string to unsigned integer.
|
||||
/**
|
||||
* string_hex_to_unsigned:
|
||||
* @str : input string (must be non-NULL, otherwise UB)
|
||||
*
|
||||
* Converts hexadecimal string to unsigned integer.
|
||||
* Handles optional leading '0x'.
|
||||
* Returns 0 if string is invalid */
|
||||
*
|
||||
* @return 0 if string is invalid, otherwise > 0
|
||||
**/
|
||||
unsigned string_hex_to_unsigned(const char *str);
|
||||
|
||||
char *string_init(const char *src);
|
||||
|
||||
void string_set(char **string, const char *src);
|
||||
|
||||
/**
|
||||
* string_count_occurrences_single_character:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Get the total number of occurrences of character @c in @str.
|
||||
*
|
||||
* @return Total number of occurrences of character @c
|
||||
*/
|
||||
int string_count_occurrences_single_character(const char *str, char c);
|
||||
|
||||
/**
|
||||
* string_replace_whitespace_with_single_character:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Replaces all spaces with given character @c.
|
||||
**/
|
||||
void string_replace_whitespace_with_single_character(char *str, char c);
|
||||
|
||||
/**
|
||||
* string_replace_multi_space_with_single_space:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Replaces multiple spaces with a single space in a string.
|
||||
**/
|
||||
void string_replace_multi_space_with_single_space(char *str);
|
||||
|
||||
/**
|
||||
* string_remove_all_whitespace:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Remove all spaces from the given string.
|
||||
**/
|
||||
void string_remove_all_whitespace(char *str_trimmed, const char *str);
|
||||
|
||||
/* Retrieve the last occurance of the given character in a string. */
|
||||
int string_index_last_occurance(const char *str, char c);
|
||||
|
||||
/**
|
||||
* string_find_index_substring_string:
|
||||
* @str : input string (must be non-NULL, otherwise UB)
|
||||
* @substr : substring to find in @str
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls strstr
|
||||
*
|
||||
* Find the position of substring @substr in string @str.
|
||||
**/
|
||||
int string_find_index_substring_string(const char *str, const char *substr);
|
||||
|
||||
/**
|
||||
* string_copy_only_ascii:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Strips non-ASCII characters from a string.
|
||||
**/
|
||||
void string_copy_only_ascii(char *str_stripped, const char *str);
|
||||
|
||||
extern const unsigned char lr_char_props[256];
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
|
|
@ -62,7 +62,7 @@ enum vfs_scheme
|
|||
VFS_SCHEME_CDROM
|
||||
};
|
||||
|
||||
#ifndef __WINRT__
|
||||
#if !(defined(__WINRT__) && defined(__cplusplus_winrt))
|
||||
#ifdef VFS_FRONTEND
|
||||
struct retro_vfs_file_handle
|
||||
#else
|
||||
|
|
|
@ -71,6 +71,12 @@ bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *dirstream);
|
|||
|
||||
int retro_vfs_closedir_impl(libretro_vfs_implementation_dir *dirstream);
|
||||
|
||||
#ifdef __WINRT__
|
||||
|
||||
void uwp_set_acl(const wchar_t* path, const wchar_t* AccessString);
|
||||
|
||||
#endif
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
|
@ -65,7 +65,6 @@ static int qstrcmp_dir(const void *a_, const void *b_)
|
|||
* @dir_first : move the directories in the listing to the top?
|
||||
*
|
||||
* Sorts a directory listing.
|
||||
*
|
||||
**/
|
||||
void dir_list_sort(struct string_list *list, bool dir_first)
|
||||
{
|
||||
|
@ -79,7 +78,6 @@ void dir_list_sort(struct string_list *list, bool dir_first)
|
|||
* @list : pointer to the directory listing
|
||||
*
|
||||
* Frees a directory listing.
|
||||
*
|
||||
**/
|
||||
void dir_list_free(struct string_list *list)
|
||||
{
|
||||
|
@ -105,7 +103,7 @@ bool dir_list_deinitialize(struct string_list *list)
|
|||
*
|
||||
* Add files within a directory to an existing string list
|
||||
*
|
||||
* Returns: -1 on error, 0 on success.
|
||||
* @return -1 on error, 0 on success.
|
||||
**/
|
||||
static int dir_list_read(const char *dir,
|
||||
struct string_list *list, struct string_list *ext_list,
|
||||
|
@ -139,8 +137,7 @@ static int dir_list_read(const char *dir,
|
|||
continue;
|
||||
}
|
||||
|
||||
file_path[0] = '\0';
|
||||
fill_pathname_join(file_path, dir, name, sizeof(file_path));
|
||||
fill_pathname_join_special(file_path, dir, name, sizeof(file_path));
|
||||
|
||||
if (retro_dirent_is_dir(entry, NULL))
|
||||
{
|
||||
|
@ -206,7 +203,7 @@ error:
|
|||
*
|
||||
* Create a directory listing, appending to an existing list
|
||||
*
|
||||
* Returns: true success, false in case of error.
|
||||
* @return Returns true on success, otherwise false.
|
||||
**/
|
||||
bool dir_list_append(struct string_list *list,
|
||||
const char *dir,
|
||||
|
@ -241,7 +238,7 @@ bool dir_list_append(struct string_list *list,
|
|||
*
|
||||
* Create a directory listing.
|
||||
*
|
||||
* Returns: pointer to a directory listing of type 'struct string_list *' on success,
|
||||
* @return pointer to a directory listing of type 'struct string_list *' on success,
|
||||
* NULL in case of error. Has to be freed manually.
|
||||
**/
|
||||
struct string_list *dir_list_new(const char *dir,
|
||||
|
@ -264,17 +261,20 @@ struct string_list *dir_list_new(const char *dir,
|
|||
return list;
|
||||
}
|
||||
|
||||
/* Warning: 'list' must zero initialised before
|
||||
* calling this function, otherwise memory leaks/
|
||||
* undefined behaviour will occur */
|
||||
/**
|
||||
* dir_list_initialize:
|
||||
*
|
||||
* NOTE: @list must zero initialised before
|
||||
* calling this function, otherwise UB.
|
||||
**/
|
||||
bool dir_list_initialize(struct string_list *list,
|
||||
const char *dir,
|
||||
const char *ext, bool include_dirs,
|
||||
bool include_hidden, bool include_compressed,
|
||||
bool recursive)
|
||||
{
|
||||
if (!list || !string_list_initialize(list))
|
||||
return false;
|
||||
return dir_list_append(list, dir, ext, include_dirs,
|
||||
if (list && string_list_initialize(list))
|
||||
return dir_list_append(list, dir, ext, include_dirs,
|
||||
include_hidden, include_compressed, recursive);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ static bool string_list_deinitialize_internal(struct string_list *list)
|
|||
*
|
||||
* Change maximum capacity of string list's size.
|
||||
*
|
||||
* Returns: true (1) if successful, otherwise false (0).
|
||||
* @return true if successful, otherwise false.
|
||||
**/
|
||||
static bool string_list_capacity(struct string_list *list, size_t cap)
|
||||
{
|
||||
|
@ -85,7 +85,7 @@ static bool string_list_capacity(struct string_list *list, size_t cap)
|
|||
* @list : pointer to string list object
|
||||
*
|
||||
* Frees a string list.
|
||||
*/
|
||||
**/
|
||||
void string_list_free(struct string_list *list)
|
||||
{
|
||||
if (!list)
|
||||
|
@ -113,8 +113,8 @@ bool string_list_deinitialize(struct string_list *list)
|
|||
*
|
||||
* Creates a new string list. Has to be freed manually.
|
||||
*
|
||||
* Returns: new string list if successful, otherwise NULL.
|
||||
*/
|
||||
* @return New string list if successful, otherwise NULL.
|
||||
**/
|
||||
struct string_list *string_list_new(void)
|
||||
{
|
||||
struct string_list_elem *
|
||||
|
@ -164,7 +164,7 @@ bool string_list_initialize(struct string_list *list)
|
|||
*
|
||||
* Appends a new element to the string list.
|
||||
*
|
||||
* Returns: true (1) if successful, otherwise false (0).
|
||||
* @return true if successful, otherwise false.
|
||||
**/
|
||||
bool string_list_append(struct string_list *list, const char *elem,
|
||||
union string_list_elem_attr attr)
|
||||
|
@ -182,8 +182,7 @@ bool string_list_append(struct string_list *list, const char *elem,
|
|||
(list->cap > 0) ? (list->cap * 2) : 32))
|
||||
return false;
|
||||
|
||||
data_dup = strdup(elem);
|
||||
if (!data_dup)
|
||||
if (!(data_dup = strdup(elem)))
|
||||
return false;
|
||||
|
||||
list->elems[list->size].data = data_dup;
|
||||
|
@ -202,7 +201,7 @@ bool string_list_append(struct string_list *list, const char *elem,
|
|||
*
|
||||
* Appends a new element to the string list.
|
||||
*
|
||||
* Returns: true (1) if successful, otherwise false (0).
|
||||
* @return true if successful, otherwise false.
|
||||
**/
|
||||
bool string_list_append_n(struct string_list *list, const char *elem,
|
||||
unsigned length, union string_list_elem_attr attr)
|
||||
|
@ -213,9 +212,7 @@ bool string_list_append_n(struct string_list *list, const char *elem,
|
|||
!string_list_capacity(list, list->cap * 2))
|
||||
return false;
|
||||
|
||||
data_dup = (char*)malloc(length + 1);
|
||||
|
||||
if (!data_dup)
|
||||
if (!(data_dup = (char*)malloc(length + 1)))
|
||||
return false;
|
||||
|
||||
strlcpy(data_dup, elem, length + 1);
|
||||
|
@ -251,7 +248,7 @@ void string_list_set(struct string_list *list,
|
|||
*
|
||||
* A string list will be joined/concatenated as a
|
||||
* string to @buffer, delimited by @delim.
|
||||
*/
|
||||
**/
|
||||
void string_list_join_concat(char *buffer, size_t size,
|
||||
const struct string_list *list, const char *delim)
|
||||
{
|
||||
|
@ -261,7 +258,7 @@ void string_list_join_concat(char *buffer, size_t size,
|
|||
/* If buffer is already 'full', nothing
|
||||
* further can be added
|
||||
* > This condition will also be triggered
|
||||
* if buffer is not NUL-terminated,
|
||||
* if buffer is not NULL-terminated,
|
||||
* in which case any attempt to increment
|
||||
* buffer or decrement size would lead to
|
||||
* undefined behaviour */
|
||||
|
@ -298,8 +295,7 @@ struct string_list *string_split(const char *str, const char *delim)
|
|||
if (!list)
|
||||
return NULL;
|
||||
|
||||
copy = strdup(str);
|
||||
if (!copy)
|
||||
if (!(copy = strdup(str)))
|
||||
goto error;
|
||||
|
||||
tmp = strtok_r(copy, delim, &save);
|
||||
|
@ -334,8 +330,7 @@ bool string_split_noalloc(struct string_list *list,
|
|||
if (!list)
|
||||
return false;
|
||||
|
||||
copy = strdup(str);
|
||||
if (!copy)
|
||||
if (!(copy = strdup(str)))
|
||||
return false;
|
||||
|
||||
tmp = strtok_r(copy, delim, &save);
|
||||
|
@ -367,8 +362,8 @@ bool string_split_noalloc(struct string_list *list,
|
|||
* Includes empty strings - i.e. two adjacent delimiters will resolve
|
||||
* to a string list element of "".
|
||||
*
|
||||
* Returns: new string list if successful, otherwise NULL.
|
||||
*/
|
||||
* @return New string list if successful, otherwise NULL.
|
||||
**/
|
||||
struct string_list *string_separate(char *str, const char *delim)
|
||||
{
|
||||
char *token = NULL;
|
||||
|
@ -377,15 +372,13 @@ struct string_list *string_separate(char *str, const char *delim)
|
|||
|
||||
/* Sanity check */
|
||||
if (!str || string_is_empty(delim))
|
||||
goto error;
|
||||
return NULL;
|
||||
if (!(list = string_list_new()))
|
||||
return NULL;
|
||||
|
||||
str_ptr = &str;
|
||||
list = string_list_new();
|
||||
token = string_tokenize(str_ptr, delim);
|
||||
|
||||
if (!list)
|
||||
goto error;
|
||||
|
||||
token = string_tokenize(str_ptr, delim);
|
||||
while (token)
|
||||
{
|
||||
union string_list_elem_attr attr;
|
||||
|
@ -393,22 +386,17 @@ struct string_list *string_separate(char *str, const char *delim)
|
|||
attr.i = 0;
|
||||
|
||||
if (!string_list_append(list, token, attr))
|
||||
goto error;
|
||||
{
|
||||
free(token);
|
||||
string_list_free(list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
free(token);
|
||||
token = NULL;
|
||||
|
||||
token = string_tokenize(str_ptr, delim);
|
||||
}
|
||||
|
||||
return list;
|
||||
|
||||
error:
|
||||
if (token)
|
||||
free(token);
|
||||
if (list)
|
||||
string_list_free(list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool string_separate_noalloc(
|
||||
|
@ -446,68 +434,66 @@ bool string_separate_noalloc(
|
|||
|
||||
/**
|
||||
* string_list_find_elem:
|
||||
* @list : pointer to string list
|
||||
* @elem : element to find inside the string list.
|
||||
*
|
||||
* @param list
|
||||
* Pointer to string list
|
||||
* @param elem
|
||||
* Element to find inside the string list.
|
||||
*
|
||||
* Searches for an element (@elem) inside the string list.
|
||||
*
|
||||
* Returns: true (1) if element could be found, otherwise false (0).
|
||||
* @return Number of elements found, otherwise 0.
|
||||
*/
|
||||
int string_list_find_elem(const struct string_list *list, const char *elem)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!list)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < list->size; i++)
|
||||
if (list)
|
||||
{
|
||||
if (string_is_equal_noncase(list->elems[i].data, elem))
|
||||
return (int)(i + 1);
|
||||
size_t i;
|
||||
for (i = 0; i < list->size; i++)
|
||||
{
|
||||
if (string_is_equal_noncase(list->elems[i].data, elem))
|
||||
return (int)(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* string_list_find_elem_prefix:
|
||||
* @list : pointer to string list
|
||||
* @prefix : prefix to append to @elem
|
||||
* @elem : element to find inside the string list.
|
||||
*
|
||||
* @param list
|
||||
* Pointer to string list
|
||||
* @param prefix
|
||||
* Prefix to append to @elem
|
||||
* @param elem
|
||||
* Element to find inside the string list.
|
||||
*
|
||||
* Searches for an element (@elem) inside the string list. Will
|
||||
* also search for the same element prefixed by @prefix.
|
||||
*
|
||||
* Returns: true (1) if element could be found, otherwise false (0).
|
||||
* @return true if element could be found, otherwise false.
|
||||
*/
|
||||
bool string_list_find_elem_prefix(const struct string_list *list,
|
||||
const char *prefix, const char *elem)
|
||||
{
|
||||
size_t i;
|
||||
char prefixed[255];
|
||||
|
||||
if (!list)
|
||||
return false;
|
||||
|
||||
prefixed[0] = '\0';
|
||||
|
||||
strlcpy(prefixed, prefix, sizeof(prefixed));
|
||||
strlcat(prefixed, elem, sizeof(prefixed));
|
||||
|
||||
for (i = 0; i < list->size; i++)
|
||||
{
|
||||
if (string_is_equal_noncase(list->elems[i].data, elem) ||
|
||||
string_is_equal_noncase(list->elems[i].data, prefixed))
|
||||
if ( string_is_equal_noncase(list->elems[i].data, elem)
|
||||
|| string_is_equal_noncase(list->elems[i].data, prefixed))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct string_list *string_list_clone(
|
||||
const struct string_list *src)
|
||||
struct string_list *string_list_clone(const struct string_list *src)
|
||||
{
|
||||
unsigned i;
|
||||
size_t i;
|
||||
struct string_list_elem
|
||||
*elems = NULL;
|
||||
struct string_list
|
||||
|
@ -519,14 +505,13 @@ struct string_list *string_list_clone(
|
|||
|
||||
dest->elems = NULL;
|
||||
dest->size = src->size;
|
||||
dest->cap = src->cap;
|
||||
if (dest->cap < dest->size)
|
||||
if (src->cap < dest->size)
|
||||
dest->cap = dest->size;
|
||||
else
|
||||
dest->cap = src->cap;
|
||||
|
||||
elems = (struct string_list_elem*)
|
||||
calloc(dest->cap, sizeof(struct string_list_elem));
|
||||
|
||||
if (!elems)
|
||||
if (!(elems = (struct string_list_elem*)
|
||||
calloc(dest->cap, sizeof(struct string_list_elem))))
|
||||
{
|
||||
free(dest);
|
||||
return NULL;
|
||||
|
@ -536,11 +521,11 @@ struct string_list *string_list_clone(
|
|||
|
||||
for (i = 0; i < src->size; i++)
|
||||
{
|
||||
const char *_src = src->elems[i].data;
|
||||
size_t len = _src ? strlen(_src) : 0;
|
||||
const char *_src = src->elems[i].data;
|
||||
size_t len = _src ? strlen(_src) : 0;
|
||||
|
||||
dest->elems[i].data = NULL;
|
||||
dest->elems[i].attr = src->elems[i].attr;
|
||||
dest->elems[i].data = NULL;
|
||||
dest->elems[i].attr = src->elems[i].attr;
|
||||
|
||||
if (len != 0)
|
||||
{
|
||||
|
|
|
@ -84,10 +84,13 @@
|
|||
|
||||
typedef OSThread pthread_t;
|
||||
typedef mutex_t pthread_mutex_t;
|
||||
typedef OSCond pthread_cond_t;
|
||||
|
||||
#if defined(GX_PTHREAD_LEGACY)
|
||||
typedef void* pthread_mutexattr_t;
|
||||
typedef int pthread_attr_t;
|
||||
typedef OSCond pthread_cond_t;
|
||||
typedef OSCond pthread_condattr_t;
|
||||
#endif
|
||||
|
||||
static INLINE int pthread_create(pthread_t *thread,
|
||||
const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg)
|
||||
|
@ -180,6 +183,4 @@ static INLINE int pthread_cond_destroy(pthread_cond_t *cond)
|
|||
return LWP_CondDestroy(*cond);
|
||||
}
|
||||
|
||||
extern int pthread_equal(pthread_t t1, pthread_t t2);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -96,7 +96,7 @@ static INLINE int pthread_mutex_init(pthread_mutex_t *mutex,
|
|||
|
||||
#ifdef VITA
|
||||
*mutex = sceKernelCreateMutex(name_buffer, 0, 0, 0);
|
||||
if(*mutex<0)
|
||||
if (*mutex<0)
|
||||
return *mutex;
|
||||
return 0;
|
||||
#else
|
||||
|
@ -220,13 +220,13 @@ static INLINE int pthread_cond_init(pthread_cond_t *cond,
|
|||
|
||||
pthread_mutex_init(&cond->mutex,NULL);
|
||||
|
||||
if(cond->mutex<0)
|
||||
if (cond->mutex<0)
|
||||
return cond->mutex;
|
||||
|
||||
snprintf(name_buffer, sizeof(name_buffer), "0x%08X", (unsigned int) cond);
|
||||
cond->sema = sceKernelCreateSema(name_buffer, 0, 0, 1, 0);
|
||||
|
||||
if(cond->sema < 0)
|
||||
if (cond->sema < 0)
|
||||
{
|
||||
pthread_mutex_destroy(&cond->mutex);
|
||||
return cond->sema;
|
||||
|
@ -269,7 +269,7 @@ static INLINE int pthread_cond_destroy(pthread_cond_t *cond)
|
|||
{
|
||||
#ifdef VITA
|
||||
int ret = sceKernelDeleteSema(cond->sema);
|
||||
if(ret < 0)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sceKernelDeleteMutex(cond->mutex);
|
||||
|
|
|
@ -47,18 +47,16 @@
|
|||
#include <mmsystem.h>
|
||||
#endif
|
||||
#elif defined(GEKKO)
|
||||
#include <ogc/lwp_watchdog.h>
|
||||
#include "gx_pthread.h"
|
||||
#elif defined(_3DS)
|
||||
#include "ctr_pthread.h"
|
||||
#elif defined(__CELLOS_LV2__)
|
||||
#include <pthread.h>
|
||||
#include <sys/sys_time.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
#if defined(VITA) || defined(BSD) || defined(ORBIS)
|
||||
#if defined(VITA) || defined(BSD) || defined(ORBIS) || defined(__mips__) || defined(_3DS)
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
|
@ -196,9 +194,11 @@ sthread_t *sthread_create_with_priority(void (*thread_func)(void*), void *userda
|
|||
if (!thread)
|
||||
return NULL;
|
||||
|
||||
data = (struct thread_data*)malloc(sizeof(*data));
|
||||
if (!data)
|
||||
goto error;
|
||||
if (!(data = (struct thread_data*)malloc(sizeof(*data))))
|
||||
{
|
||||
free(thread);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data->func = thread_func;
|
||||
data->userdata = userdata;
|
||||
|
@ -245,10 +245,7 @@ sthread_t *sthread_create_with_priority(void (*thread_func)(void*), void *userda
|
|||
|
||||
if (thread_created)
|
||||
return thread;
|
||||
|
||||
error:
|
||||
if (data)
|
||||
free(data);
|
||||
free(data);
|
||||
free(thread);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -301,6 +298,7 @@ void sthread_join(sthread_t *thread)
|
|||
free(thread);
|
||||
}
|
||||
|
||||
#if !defined(GEKKO)
|
||||
/**
|
||||
* sthread_isself:
|
||||
* @thread : pointer to thread object
|
||||
|
@ -309,16 +307,13 @@ void sthread_join(sthread_t *thread)
|
|||
*/
|
||||
bool sthread_isself(sthread_t *thread)
|
||||
{
|
||||
/* This thread can't possibly be a null thread */
|
||||
if (!thread)
|
||||
return false;
|
||||
|
||||
#ifdef USE_WIN32_THREADS
|
||||
return GetCurrentThreadId() == thread->id;
|
||||
return thread ? GetCurrentThreadId() == thread->id : false;
|
||||
#else
|
||||
return pthread_equal(pthread_self(),thread->id);
|
||||
return thread ? pthread_equal(pthread_self(), thread->id) : false;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* slock_new:
|
||||
|
@ -330,27 +325,19 @@ bool sthread_isself(sthread_t *thread)
|
|||
**/
|
||||
slock_t *slock_new(void)
|
||||
{
|
||||
bool mutex_created = false;
|
||||
slock_t *lock = (slock_t*)calloc(1, sizeof(*lock));
|
||||
if (!lock)
|
||||
return NULL;
|
||||
|
||||
|
||||
#ifdef USE_WIN32_THREADS
|
||||
InitializeCriticalSection(&lock->lock);
|
||||
mutex_created = true;
|
||||
#else
|
||||
mutex_created = (pthread_mutex_init(&lock->lock, NULL) == 0);
|
||||
if (pthread_mutex_init(&lock->lock, NULL) != 0)
|
||||
{
|
||||
free(lock);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!mutex_created)
|
||||
goto error;
|
||||
|
||||
return lock;
|
||||
|
||||
error:
|
||||
free(lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -400,12 +387,10 @@ void slock_lock(slock_t *lock)
|
|||
**/
|
||||
bool slock_try_lock(slock_t *lock)
|
||||
{
|
||||
if (!lock)
|
||||
return false;
|
||||
#ifdef USE_WIN32_THREADS
|
||||
return TryEnterCriticalSection(&lock->lock);
|
||||
return lock && TryEnterCriticalSection(&lock->lock);
|
||||
#else
|
||||
return pthread_mutex_trylock(&lock->lock)==0;
|
||||
return lock && (pthread_mutex_trylock(&lock->lock) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -468,11 +453,9 @@ scond_t *scond_new(void)
|
|||
*
|
||||
* Note: We might could simplify this using vista+ condition variables,
|
||||
* but we wanted an XP compatible solution. */
|
||||
cond->event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
if (!cond->event)
|
||||
if (!(cond->event = CreateEvent(NULL, FALSE, FALSE, NULL)))
|
||||
goto error;
|
||||
cond->hot_potato = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
if (!cond->hot_potato)
|
||||
if (!(cond->hot_potato = CreateEvent(NULL, FALSE, FALSE, NULL)))
|
||||
{
|
||||
CloseHandle(cond->event);
|
||||
goto error;
|
||||
|
@ -526,7 +509,6 @@ static bool _scond_wait_win32(scond_t *cond, slock_t *lock, DWORD dwMilliseconds
|
|||
static bool beginPeriod = false;
|
||||
DWORD tsBegin;
|
||||
#endif
|
||||
|
||||
DWORD waitResult;
|
||||
DWORD dwFinalTimeout = dwMilliseconds; /* Careful! in case we begin in the head,
|
||||
we don't do the hot potato stuff,
|
||||
|
@ -546,9 +528,7 @@ static bool _scond_wait_win32(scond_t *cond, slock_t *lock, DWORD dwMilliseconds
|
|||
}
|
||||
|
||||
if (performanceCounterFrequency.QuadPart == 0)
|
||||
{
|
||||
QueryPerformanceFrequency(&performanceCounterFrequency);
|
||||
}
|
||||
#else
|
||||
if (!beginPeriod)
|
||||
{
|
||||
|
@ -571,9 +551,9 @@ static bool _scond_wait_win32(scond_t *cond, slock_t *lock, DWORD dwMilliseconds
|
|||
|
||||
/* walk to the end of the linked list */
|
||||
while (*ptr)
|
||||
ptr = &((*ptr)->next);
|
||||
ptr = &((*ptr)->next);
|
||||
|
||||
*ptr = &myentry;
|
||||
*ptr = &myentry;
|
||||
myentry.next = NULL;
|
||||
|
||||
cond->waiters++;
|
||||
|
@ -747,18 +727,17 @@ void scond_wait(scond_t *cond, slock_t *lock)
|
|||
int scond_broadcast(scond_t *cond)
|
||||
{
|
||||
#ifdef USE_WIN32_THREADS
|
||||
/* remember: we currently have mutex */
|
||||
if (cond->waiters == 0)
|
||||
return 0;
|
||||
|
||||
/* awaken everything which is currently queued up */
|
||||
if (cond->wakens == 0)
|
||||
SetEvent(cond->event);
|
||||
cond->wakens = cond->waiters;
|
||||
|
||||
/* Since there is now at least one pending waken, the potato must be in play */
|
||||
SetEvent(cond->hot_potato);
|
||||
/* Remember, we currently have mutex */
|
||||
if (cond->waiters != 0)
|
||||
{
|
||||
/* Awaken everything which is currently queued up */
|
||||
if (cond->wakens == 0)
|
||||
SetEvent(cond->event);
|
||||
cond->wakens = cond->waiters;
|
||||
|
||||
/* Since there is now at least one pending waken, the potato must be in play */
|
||||
SetEvent(cond->hot_potato);
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
return pthread_cond_broadcast(&cond->cond);
|
||||
|
@ -831,11 +810,6 @@ bool scond_wait_timeout(scond_t *cond, slock_t *lock, int64_t timeout_us)
|
|||
* Someone asking for a 0 timeout clearly wants immediate timeout.
|
||||
* Someone asking for a 1 timeout clearly wants an actual timeout
|
||||
* of the minimum length */
|
||||
|
||||
/* Someone asking for 1000 or 1001 timeout shouldn't
|
||||
* accidentally get 2ms. */
|
||||
DWORD dwMilliseconds = timeout_us/1000;
|
||||
|
||||
/* The implementation of a 0 timeout here with pthreads is sketchy.
|
||||
* It isn't clear what happens if pthread_cond_timedwait is called with NOW.
|
||||
* Moreover, it is possible that this thread gets pre-empted after the
|
||||
|
@ -846,62 +820,61 @@ bool scond_wait_timeout(scond_t *cond, slock_t *lock, int64_t timeout_us)
|
|||
if (timeout_us == 0)
|
||||
return false;
|
||||
else if (timeout_us < 1000)
|
||||
dwMilliseconds = 1;
|
||||
|
||||
return _scond_wait_win32(cond,lock,dwMilliseconds);
|
||||
return _scond_wait_win32(cond, lock, 1);
|
||||
/* Someone asking for 1000 or 1001 timeout shouldn't
|
||||
* accidentally get 2ms. */
|
||||
return _scond_wait_win32(cond, lock, timeout_us / 1000);
|
||||
#else
|
||||
int ret;
|
||||
int64_t seconds, remainder;
|
||||
struct timespec now = {0};
|
||||
|
||||
struct timespec now;
|
||||
#ifdef __MACH__
|
||||
/* OSX doesn't have clock_gettime. */
|
||||
clock_serv_t cclock;
|
||||
mach_timespec_t mts;
|
||||
|
||||
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
|
||||
clock_get_time(cclock, &mts);
|
||||
mach_port_deallocate(mach_task_self(), cclock);
|
||||
now.tv_sec = mts.tv_sec;
|
||||
now.tv_nsec = mts.tv_nsec;
|
||||
#elif defined(__CELLOS_LV2__)
|
||||
#elif !defined(__PSL1GHT__) && defined(__PS3__)
|
||||
sys_time_sec_t s;
|
||||
sys_time_nsec_t n;
|
||||
|
||||
sys_time_get_current_time(&s, &n);
|
||||
now.tv_sec = s;
|
||||
now.tv_nsec = n;
|
||||
now.tv_sec = s;
|
||||
now.tv_nsec = n;
|
||||
#elif defined(PS2)
|
||||
int tickms = ps2_clock();
|
||||
now.tv_sec = tickms/1000;
|
||||
now.tv_nsec = tickms * 1000;
|
||||
#elif defined(__mips__) || defined(VITA) || defined(_3DS)
|
||||
int tickms = ps2_clock();
|
||||
now.tv_sec = tickms / 1000;
|
||||
now.tv_nsec = tickms * 1000;
|
||||
#elif !defined(DINGUX_BETA) && (defined(__mips__) || defined(VITA) || defined(_3DS))
|
||||
struct timeval tm;
|
||||
|
||||
gettimeofday(&tm, NULL);
|
||||
now.tv_sec = tm.tv_sec;
|
||||
now.tv_nsec = tm.tv_usec * 1000;
|
||||
now.tv_sec = tm.tv_sec;
|
||||
now.tv_nsec = tm.tv_usec * 1000;
|
||||
#elif defined(RETRO_WIN32_USE_PTHREADS)
|
||||
_ftime64_s(&now);
|
||||
#elif !defined(GEKKO)
|
||||
/* timeout on libogc is duration, not end time. */
|
||||
#elif defined(GEKKO)
|
||||
/* Avoid gettimeofday due to it being reported to be broken */
|
||||
const uint64_t tickms = gettime() / TB_TIMER_CLOCK;
|
||||
now.tv_sec = tickms / 1000;
|
||||
now.tv_nsec = tickms * 1000;
|
||||
#else
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
#endif
|
||||
|
||||
seconds = timeout_us / INT64_C(1000000);
|
||||
remainder = timeout_us % INT64_C(1000000);
|
||||
seconds = timeout_us / INT64_C(1000000);
|
||||
remainder = timeout_us % INT64_C(1000000);
|
||||
|
||||
now.tv_sec += seconds;
|
||||
now.tv_nsec += remainder * INT64_C(1000);
|
||||
now.tv_sec += seconds;
|
||||
now.tv_nsec += remainder * INT64_C(1000);
|
||||
|
||||
if (now.tv_nsec > 1000000000)
|
||||
{
|
||||
now.tv_nsec -= 1000000000;
|
||||
now.tv_sec += 1;
|
||||
now.tv_nsec -= 1000000000;
|
||||
now.tv_sec += 1;
|
||||
}
|
||||
|
||||
ret = pthread_cond_timedwait(&cond->cond, &lock->lock, &now);
|
||||
return (ret == 0);
|
||||
return (pthread_cond_timedwait(&cond->cond, &lock->lock, &now) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -945,9 +918,9 @@ bool sthread_tls_set(sthread_tls_t *tls, const void *data)
|
|||
|
||||
uintptr_t sthread_get_thread_id(sthread_t *thread)
|
||||
{
|
||||
if (!thread)
|
||||
return 0;
|
||||
return (uintptr_t)thread->id;
|
||||
if (thread)
|
||||
return (uintptr_t)thread->id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uintptr_t sthread_get_current_thread_id(void)
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
|
@ -46,7 +45,6 @@ struct RFILE
|
|||
{
|
||||
struct retro_vfs_file_handle *hfile;
|
||||
bool error_flag;
|
||||
bool eof_flag;
|
||||
};
|
||||
|
||||
static retro_vfs_get_path_t filestream_get_path_cb = NULL;
|
||||
|
@ -109,18 +107,14 @@ bool filestream_exists(const char *path)
|
|||
|
||||
if (!path || !*path)
|
||||
return false;
|
||||
|
||||
dummy = filestream_open(
|
||||
if (!(dummy = filestream_open(
|
||||
path,
|
||||
RETRO_VFS_FILE_ACCESS_READ,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
|
||||
if (!dummy)
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE)))
|
||||
return false;
|
||||
|
||||
if (filestream_close(dummy) != 0)
|
||||
if (dummy)
|
||||
free(dummy);
|
||||
free(dummy);
|
||||
|
||||
dummy = NULL;
|
||||
return true;
|
||||
|
@ -165,7 +159,7 @@ int64_t filestream_truncate(RFILE *stream, int64_t length)
|
|||
* @hints :
|
||||
*
|
||||
* Opens a file for reading or writing, depending on the requested mode.
|
||||
* Returns a pointer to an RFILE if opened successfully, otherwise NULL.
|
||||
* @return A pointer to an RFILE if opened successfully, otherwise NULL.
|
||||
**/
|
||||
RFILE* filestream_open(const char *path, unsigned mode, unsigned hints)
|
||||
{
|
||||
|
@ -184,7 +178,6 @@ RFILE* filestream_open(const char *path, unsigned mode, unsigned hints)
|
|||
|
||||
output = (RFILE*)malloc(sizeof(RFILE));
|
||||
output->error_flag = false;
|
||||
output->eof_flag = false;
|
||||
output->hfile = fp;
|
||||
return output;
|
||||
}
|
||||
|
@ -221,12 +214,12 @@ int filestream_getc(RFILE *stream)
|
|||
return EOF;
|
||||
}
|
||||
|
||||
int filestream_scanf(RFILE *stream, const char* format, ...)
|
||||
int filestream_vscanf(RFILE *stream, const char* format, va_list *args)
|
||||
{
|
||||
char buf[4096];
|
||||
char subfmt[64];
|
||||
va_list args;
|
||||
const char * bufiter = buf;
|
||||
va_list args_copy;
|
||||
const char *bufiter = buf;
|
||||
int ret = 0;
|
||||
int64_t startpos = filestream_tell(stream);
|
||||
int64_t maxlen = filestream_read(stream, buf, sizeof(buf)-1);
|
||||
|
@ -236,7 +229,16 @@ int filestream_scanf(RFILE *stream, const char* format, ...)
|
|||
|
||||
buf[maxlen] = '\0';
|
||||
|
||||
va_start(args, format);
|
||||
/* Have to copy the input va_list here
|
||||
* > Calling va_arg() on 'args' directly would
|
||||
* cause the va_list to have an indeterminate value
|
||||
* in the function calling filestream_vscanf(),
|
||||
* leading to unexpected behaviour */
|
||||
#ifdef __va_copy
|
||||
__va_copy(args_copy, *args);
|
||||
#else
|
||||
va_copy(args_copy, *args);
|
||||
#endif
|
||||
|
||||
while (*format)
|
||||
{
|
||||
|
@ -302,7 +304,7 @@ int filestream_scanf(RFILE *stream, const char* format, ...)
|
|||
}
|
||||
else
|
||||
{
|
||||
int v = sscanf(bufiter, subfmt, va_arg(args, void*), &sublen);
|
||||
int v = sscanf(bufiter, subfmt, va_arg(args_copy, void*), &sublen);
|
||||
if (v == EOF)
|
||||
return EOF;
|
||||
if (v != 1)
|
||||
|
@ -327,13 +329,23 @@ int filestream_scanf(RFILE *stream, const char* format, ...)
|
|||
}
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
filestream_seek(stream, startpos+(bufiter-buf),
|
||||
va_end(args_copy);
|
||||
filestream_seek(stream, startpos + (bufiter - buf),
|
||||
RETRO_VFS_SEEK_POSITION_START);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int filestream_scanf(RFILE *stream, const char* format, ...)
|
||||
{
|
||||
int result;
|
||||
va_list vl;
|
||||
va_start(vl, format);
|
||||
result = filestream_vscanf(stream, format, &vl);
|
||||
va_end(vl);
|
||||
return result;
|
||||
}
|
||||
|
||||
int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position)
|
||||
{
|
||||
int64_t output;
|
||||
|
@ -348,14 +360,12 @@ int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position)
|
|||
if (output == VFS_ERROR_RETURN_VALUE)
|
||||
stream->error_flag = true;
|
||||
|
||||
stream->eof_flag = false;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
int filestream_eof(RFILE *stream)
|
||||
{
|
||||
return stream->eof_flag;
|
||||
return filestream_tell(stream) == filestream_get_size(stream) ? EOF : 0;
|
||||
}
|
||||
|
||||
int64_t filestream_tell(RFILE *stream)
|
||||
|
@ -380,7 +390,6 @@ void filestream_rewind(RFILE *stream)
|
|||
return;
|
||||
filestream_seek(stream, 0L, RETRO_VFS_SEEK_POSITION_START);
|
||||
stream->error_flag = false;
|
||||
stream->eof_flag = false;
|
||||
}
|
||||
|
||||
int64_t filestream_read(RFILE *stream, void *s, int64_t len)
|
||||
|
@ -395,8 +404,6 @@ int64_t filestream_read(RFILE *stream, void *s, int64_t len)
|
|||
|
||||
if (output == VFS_ERROR_RETURN_VALUE)
|
||||
stream->error_flag = true;
|
||||
if (output < len)
|
||||
stream->eof_flag = true;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
@ -494,9 +501,7 @@ int filestream_printf(RFILE *stream, const char* format, ...)
|
|||
|
||||
int filestream_error(RFILE *stream)
|
||||
{
|
||||
if (stream && stream->error_flag)
|
||||
return 1;
|
||||
return 0;
|
||||
return (stream && stream->error_flag);
|
||||
}
|
||||
|
||||
int filestream_close(RFILE *stream)
|
||||
|
@ -521,10 +526,11 @@ int filestream_close(RFILE *stream)
|
|||
* @path : path to file.
|
||||
* @buf : buffer to allocate and read the contents of the
|
||||
* file into. Needs to be freed manually.
|
||||
* @len : optional output integer containing bytes read.
|
||||
*
|
||||
* Read the contents of a file into @buf.
|
||||
*
|
||||
* Returns: number of items read, -1 on error.
|
||||
* @return Non-zero on success.
|
||||
*/
|
||||
int64_t filestream_read_file(const char *path, void **buf, int64_t *len)
|
||||
{
|
||||
|
@ -541,25 +547,20 @@ int64_t filestream_read_file(const char *path, void **buf, int64_t *len)
|
|||
return 0;
|
||||
}
|
||||
|
||||
content_buf_size = filestream_get_size(file);
|
||||
|
||||
if (content_buf_size < 0)
|
||||
if ((content_buf_size = filestream_get_size(file)) < 0)
|
||||
goto error;
|
||||
|
||||
content_buf = malloc((size_t)(content_buf_size + 1));
|
||||
|
||||
if (!content_buf)
|
||||
if (!(content_buf = malloc((size_t)(content_buf_size + 1))))
|
||||
goto error;
|
||||
if ((int64_t)(uint64_t)(content_buf_size + 1) != (content_buf_size + 1))
|
||||
goto error;
|
||||
|
||||
ret = filestream_read(file, content_buf, (int64_t)content_buf_size);
|
||||
if (ret < 0)
|
||||
if ((ret = filestream_read(file, content_buf, (int64_t)content_buf_size)) <
|
||||
0)
|
||||
goto error;
|
||||
|
||||
if (filestream_close(file) != 0)
|
||||
if (file)
|
||||
free(file);
|
||||
free(file);
|
||||
|
||||
*buf = content_buf;
|
||||
|
||||
|
@ -573,9 +574,8 @@ int64_t filestream_read_file(const char *path, void **buf, int64_t *len)
|
|||
return 1;
|
||||
|
||||
error:
|
||||
if (file)
|
||||
if (filestream_close(file) != 0)
|
||||
free(file);
|
||||
if (filestream_close(file) != 0)
|
||||
free(file);
|
||||
if (content_buf)
|
||||
free(content_buf);
|
||||
if (len)
|
||||
|
@ -592,8 +592,8 @@ error:
|
|||
*
|
||||
* Writes data to a file.
|
||||
*
|
||||
* Returns: true (1) on success, false (0) otherwise.
|
||||
*/
|
||||
* @return true on success, otherwise false.
|
||||
**/
|
||||
bool filestream_write_file(const char *path, const void *data, int64_t size)
|
||||
{
|
||||
int64_t ret = 0;
|
||||
|
@ -602,20 +602,18 @@ bool filestream_write_file(const char *path, const void *data, int64_t size)
|
|||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
ret = filestream_write(file, data, size);
|
||||
if (filestream_close(file) != 0)
|
||||
if (file)
|
||||
free(file);
|
||||
|
||||
if (ret != size)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
free(file);
|
||||
return (ret == size);
|
||||
}
|
||||
|
||||
/* Returned pointer must be freed by the caller. */
|
||||
char* filestream_getline(RFILE *stream)
|
||||
/**
|
||||
* filestream_getline:
|
||||
*
|
||||
* Returned pointer must be freed by the caller.
|
||||
**/
|
||||
char *filestream_getline(RFILE *stream)
|
||||
{
|
||||
char *newline_tmp = NULL;
|
||||
size_t cur_size = 8;
|
||||
|
@ -630,16 +628,15 @@ char* filestream_getline(RFILE *stream)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
in = filestream_getc(stream);
|
||||
in = filestream_getc(stream);
|
||||
|
||||
while (in != EOF && in != '\n')
|
||||
{
|
||||
if (idx == cur_size)
|
||||
{
|
||||
cur_size *= 2;
|
||||
newline_tmp = (char*)realloc(newline, cur_size + 1);
|
||||
|
||||
if (!newline_tmp)
|
||||
if (!(newline_tmp = (char*)realloc(newline, cur_size + 1)))
|
||||
{
|
||||
free(newline);
|
||||
return NULL;
|
||||
|
|
|
@ -69,17 +69,27 @@ RFILE* rfopen(const char *path, const char *mode)
|
|||
|
||||
int rfclose(RFILE* stream)
|
||||
{
|
||||
if (!stream)
|
||||
return EOF;
|
||||
|
||||
return filestream_close(stream);
|
||||
}
|
||||
|
||||
int64_t rftell(RFILE* stream)
|
||||
{
|
||||
if (!stream)
|
||||
return -1;
|
||||
|
||||
return filestream_tell(stream);
|
||||
}
|
||||
|
||||
int64_t rfseek(RFILE* stream, int64_t offset, int origin)
|
||||
{
|
||||
int seek_position = -1;
|
||||
|
||||
if (!stream)
|
||||
return -1;
|
||||
|
||||
switch (origin)
|
||||
{
|
||||
case SEEK_SET:
|
||||
|
@ -99,39 +109,61 @@ int64_t rfseek(RFILE* stream, int64_t offset, int origin)
|
|||
int64_t rfread(void* buffer,
|
||||
size_t elem_size, size_t elem_count, RFILE* stream)
|
||||
{
|
||||
if (!stream || (elem_size == 0) || (elem_count == 0))
|
||||
return 0;
|
||||
|
||||
return (filestream_read(stream, buffer, elem_size * elem_count) / elem_size);
|
||||
}
|
||||
|
||||
char *rfgets(char *buffer, int maxCount, RFILE* stream)
|
||||
{
|
||||
if (!stream)
|
||||
return NULL;
|
||||
|
||||
return filestream_gets(stream, buffer, maxCount);
|
||||
}
|
||||
|
||||
int rfgetc(RFILE* stream)
|
||||
{
|
||||
if (!stream)
|
||||
return EOF;
|
||||
|
||||
return filestream_getc(stream);
|
||||
}
|
||||
|
||||
int64_t rfwrite(void const* buffer,
|
||||
size_t elem_size, size_t elem_count, RFILE* stream)
|
||||
{
|
||||
return filestream_write(stream, buffer, elem_size * elem_count);
|
||||
if (!stream || (elem_size == 0) || (elem_count == 0))
|
||||
return 0;
|
||||
|
||||
return (filestream_write(stream, buffer, elem_size * elem_count) / elem_size);
|
||||
}
|
||||
|
||||
int rfputc(int character, RFILE * stream)
|
||||
{
|
||||
return filestream_putc(stream, character);
|
||||
if (!stream)
|
||||
return EOF;
|
||||
|
||||
return filestream_putc(stream, character);
|
||||
}
|
||||
|
||||
int64_t rfflush(RFILE * stream)
|
||||
{
|
||||
return filestream_flush(stream);
|
||||
if (!stream)
|
||||
return EOF;
|
||||
|
||||
return filestream_flush(stream);
|
||||
}
|
||||
|
||||
int rfprintf(RFILE * stream, const char * format, ...)
|
||||
{
|
||||
int result;
|
||||
va_list vl;
|
||||
|
||||
if (!stream)
|
||||
return -1;
|
||||
|
||||
va_start(vl, format);
|
||||
result = filestream_vprintf(stream, format, vl);
|
||||
va_end(vl);
|
||||
|
@ -152,8 +184,12 @@ int rfscanf(RFILE * stream, const char * format, ...)
|
|||
{
|
||||
int result;
|
||||
va_list vl;
|
||||
|
||||
if (!stream)
|
||||
return 0;
|
||||
|
||||
va_start(vl, format);
|
||||
result = filestream_scanf(stream, format, vl);
|
||||
result = filestream_vscanf(stream, format, &vl);
|
||||
va_end(vl);
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -22,7 +22,9 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <compat/strl.h>
|
||||
#include <string/stdstring.h>
|
||||
#include <encodings/utf.h>
|
||||
|
||||
|
@ -88,9 +90,11 @@ char *string_ucwords(char *s)
|
|||
}
|
||||
|
||||
char *string_replace_substring(const char *in,
|
||||
const char *pattern, const char *replacement)
|
||||
const char *pattern, size_t pattern_len,
|
||||
const char *replacement, size_t replacement_len)
|
||||
{
|
||||
size_t numhits, pattern_len, replacement_len, outlen;
|
||||
size_t outlen;
|
||||
size_t numhits = 0;
|
||||
const char *inat = NULL;
|
||||
const char *inprev = NULL;
|
||||
char *out = NULL;
|
||||
|
@ -101,9 +105,6 @@ char *string_replace_substring(const char *in,
|
|||
if (!pattern || !replacement)
|
||||
return strdup(in);
|
||||
|
||||
pattern_len = strlen(pattern);
|
||||
replacement_len = strlen(replacement);
|
||||
numhits = 0;
|
||||
inat = in;
|
||||
|
||||
while ((inat = strstr(inat, pattern)))
|
||||
|
@ -113,9 +114,8 @@ char *string_replace_substring(const char *in,
|
|||
}
|
||||
|
||||
outlen = strlen(in) - pattern_len*numhits + replacement_len*numhits;
|
||||
out = (char *)malloc(outlen+1);
|
||||
|
||||
if (!out)
|
||||
if (!(out = (char *)malloc(outlen+1)))
|
||||
return NULL;
|
||||
|
||||
outat = out;
|
||||
|
@ -128,7 +128,7 @@ char *string_replace_substring(const char *in,
|
|||
outat += inat-inprev;
|
||||
memcpy(outat, replacement, replacement_len);
|
||||
outat += replacement_len;
|
||||
inat += pattern_len;
|
||||
inat += pattern_len;
|
||||
inprev = inat;
|
||||
}
|
||||
strcpy(outat, inprev);
|
||||
|
@ -136,7 +136,11 @@ char *string_replace_substring(const char *in,
|
|||
return out;
|
||||
}
|
||||
|
||||
/* Remove leading whitespaces */
|
||||
/**
|
||||
* string_trim_whitespace_left:
|
||||
*
|
||||
* Remove leading whitespaces
|
||||
**/
|
||||
char *string_trim_whitespace_left(char *const s)
|
||||
{
|
||||
if (s && *s)
|
||||
|
@ -157,7 +161,11 @@ char *string_trim_whitespace_left(char *const s)
|
|||
return s;
|
||||
}
|
||||
|
||||
/* Remove trailing whitespaces */
|
||||
/**
|
||||
* string_trim_whitespace_right:
|
||||
*
|
||||
* Remove trailing whitespaces
|
||||
**/
|
||||
char *string_trim_whitespace_right(char *const s)
|
||||
{
|
||||
if (s && *s)
|
||||
|
@ -177,7 +185,11 @@ char *string_trim_whitespace_right(char *const s)
|
|||
return s;
|
||||
}
|
||||
|
||||
/* Remove leading and trailing whitespaces */
|
||||
/**
|
||||
* string_trim_whitespace:
|
||||
*
|
||||
* Remove leading and trailing whitespaces
|
||||
**/
|
||||
char *string_trim_whitespace(char *const s)
|
||||
{
|
||||
string_trim_whitespace_right(s); /* order matters */
|
||||
|
@ -186,94 +198,268 @@ char *string_trim_whitespace(char *const s)
|
|||
return s;
|
||||
}
|
||||
|
||||
char *word_wrap(char* buffer, const char *string, int line_width, bool unicode, unsigned max_lines)
|
||||
/**
|
||||
* word_wrap:
|
||||
* @dst : pointer to destination buffer.
|
||||
* @dst_size : size of destination buffer.
|
||||
* @src : pointer to input string.
|
||||
* @line_width : max number of characters per line.
|
||||
* @wideglyph_width : not used, but is necessary to keep
|
||||
* compatibility with word_wrap_wideglyph().
|
||||
* @max_lines : max lines of destination string.
|
||||
* 0 means no limit.
|
||||
*
|
||||
* Wraps string specified by 'src' to destination buffer
|
||||
* specified by 'dst' and 'dst_size'.
|
||||
* This function assumes that all glyphs in the string
|
||||
* have an on-screen pixel width similar to that of
|
||||
* regular Latin characters - i.e. it will not wrap
|
||||
* correctly any text containing so-called 'wide' Unicode
|
||||
* characters (e.g. CJK languages, emojis, etc.).
|
||||
**/
|
||||
void word_wrap(
|
||||
char *dst, size_t dst_size,
|
||||
const char *src, size_t src_len,
|
||||
int line_width, int wideglyph_width, unsigned max_lines)
|
||||
{
|
||||
unsigned i = 0;
|
||||
unsigned len = (unsigned)strlen(string);
|
||||
unsigned lines = 1;
|
||||
char *lastspace = NULL;
|
||||
unsigned counter = 0;
|
||||
unsigned lines = 1;
|
||||
const char *src_end = src + src_len;
|
||||
|
||||
while (i < len)
|
||||
/* Prevent buffer overflow */
|
||||
if (dst_size < src_len + 1)
|
||||
return;
|
||||
|
||||
/* Early return if src string length is less
|
||||
* than line width */
|
||||
if (src_len < (size_t)line_width)
|
||||
{
|
||||
unsigned counter;
|
||||
int pos = (int)(&buffer[i] - buffer);
|
||||
strlcpy(dst, src, dst_size);
|
||||
return;
|
||||
}
|
||||
|
||||
/* copy string until the end of the line is reached */
|
||||
for (counter = 1; counter <= (unsigned)line_width; counter++)
|
||||
while (*src != '\0')
|
||||
{
|
||||
unsigned char_len = (unsigned)(utf8skip(src, 1) - src);
|
||||
counter++;
|
||||
|
||||
if (*src == ' ')
|
||||
lastspace = dst; /* Remember the location of the whitespace */
|
||||
else if (*src == '\n')
|
||||
{
|
||||
const char *character;
|
||||
unsigned char_len;
|
||||
unsigned j = i;
|
||||
/* If newlines embedded in the input,
|
||||
* reset the index */
|
||||
lines++;
|
||||
counter = 0;
|
||||
|
||||
/* check if end of string reached */
|
||||
if (i == len)
|
||||
/* Early return if remaining src string
|
||||
* length is less than line width */
|
||||
if (src_end - src <= line_width)
|
||||
{
|
||||
buffer[i] = 0;
|
||||
return buffer;
|
||||
strlcpy(dst, src, dst_size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
character = utf8skip(&string[i], 1);
|
||||
char_len = (unsigned)(character - &string[i]);
|
||||
while (char_len--)
|
||||
*dst++ = *src++;
|
||||
|
||||
if (!unicode)
|
||||
counter += char_len - 1;
|
||||
|
||||
do
|
||||
{
|
||||
buffer[i] = string[i];
|
||||
char_len--;
|
||||
i++;
|
||||
} while (char_len);
|
||||
|
||||
/* check for newlines embedded in the original input
|
||||
* and reset the index */
|
||||
if (buffer[j] == '\n')
|
||||
{
|
||||
lines++;
|
||||
counter = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* check for whitespace */
|
||||
if (string[i] == ' ')
|
||||
if (counter >= (unsigned)line_width)
|
||||
{
|
||||
if ((max_lines == 0 || lines < max_lines))
|
||||
counter = 0;
|
||||
|
||||
if (lastspace && (max_lines == 0 || lines < max_lines))
|
||||
{
|
||||
buffer[i] = '\n';
|
||||
i++;
|
||||
/* Replace nearest (previous) whitespace
|
||||
* with newline character */
|
||||
*lastspace = '\n';
|
||||
lines++;
|
||||
|
||||
src -= dst - lastspace - 1;
|
||||
dst = lastspace + 1;
|
||||
lastspace = NULL;
|
||||
|
||||
/* Early return if remaining src string
|
||||
* length is less than line width */
|
||||
if (src_end - src < line_width)
|
||||
{
|
||||
strlcpy(dst, src, dst_size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int k;
|
||||
|
||||
/* check for nearest whitespace back in string */
|
||||
for (k = i; k > 0; k--)
|
||||
{
|
||||
if (string[k] != ' ' || (max_lines != 0 && lines >= max_lines))
|
||||
continue;
|
||||
|
||||
buffer[k] = '\n';
|
||||
/* set string index back to character after this one */
|
||||
i = k + 1;
|
||||
lines++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (&buffer[i] - buffer == pos)
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
buffer[i] = 0;
|
||||
|
||||
return buffer;
|
||||
*dst = '\0';
|
||||
}
|
||||
|
||||
/* Splits string into tokens seperated by 'delim'
|
||||
/**
|
||||
* word_wrap_wideglyph:
|
||||
* @dst : pointer to destination buffer.
|
||||
* @dst_size : size of destination buffer.
|
||||
* @src : pointer to input string.
|
||||
* @line_width : max number of characters per line.
|
||||
* @wideglyph_width : effective width of 'wide' Unicode glyphs.
|
||||
* the value here is normalised relative to the
|
||||
* typical on-screen pixel width of a regular
|
||||
* Latin character:
|
||||
* - a regular Latin character is defined to
|
||||
* have an effective width of 100
|
||||
* - wideglyph_width = 100 * (wide_character_pixel_width / latin_character_pixel_width)
|
||||
* - e.g. if 'wide' Unicode characters in 'src'
|
||||
* have an on-screen pixel width twice that of
|
||||
* regular Latin characters, wideglyph_width
|
||||
* would be 200
|
||||
* @max_lines : max lines of destination string.
|
||||
* 0 means no limit.
|
||||
*
|
||||
* Wraps string specified by @src to destination buffer
|
||||
* specified by @dst and @dst_size.
|
||||
* This function assumes that all glyphs in the string
|
||||
* are:
|
||||
* - EITHER 'non-wide' Unicode glyphs, with an on-screen
|
||||
* pixel width similar to that of regular Latin characters
|
||||
* - OR 'wide' Unicode glyphs (e.g. CJK languages, emojis, etc.)
|
||||
* with an on-screen pixel width defined by @wideglyph_width
|
||||
* Note that wrapping may occur in inappropriate locations
|
||||
* if @src string contains 'wide' Unicode characters whose
|
||||
* on-screen pixel width deviates greatly from the set
|
||||
* @wideglyph_width value.
|
||||
**/
|
||||
void word_wrap_wideglyph(char *dst, size_t dst_size,
|
||||
const char *src, size_t src_len, int line_width,
|
||||
int wideglyph_width, unsigned max_lines)
|
||||
{
|
||||
char *lastspace = NULL;
|
||||
char *lastwideglyph = NULL;
|
||||
const char *src_end = src + src_len;
|
||||
unsigned lines = 1;
|
||||
/* 'line_width' means max numbers of characters per line,
|
||||
* but this metric is only meaningful when dealing with
|
||||
* 'regular' glyphs that have an on-screen pixel width
|
||||
* similar to that of regular Latin characters.
|
||||
* When handing so-called 'wide' Unicode glyphs, it is
|
||||
* necessary to consider the actual on-screen pixel width
|
||||
* of each character.
|
||||
* In order to do this, we create a distinction between
|
||||
* regular Latin 'non-wide' glyphs and 'wide' glyphs, and
|
||||
* normalise all values relative to the on-screen pixel
|
||||
* width of regular Latin characters:
|
||||
* - Regular 'non-wide' glyphs have a normalised width of 100
|
||||
* - 'line_width' is therefore normalised to 100 * (width_in_characters)
|
||||
* - 'wide' glyphs have a normalised width of
|
||||
* 100 * (wide_character_pixel_width / latin_character_pixel_width)
|
||||
* - When a character is detected, the position in the current
|
||||
* line is incremented by the regular normalised width of 100
|
||||
* - If that character is then determined to be a 'wide'
|
||||
* glyph, the position in the current line is further incremented
|
||||
* by the difference between the normalised 'wide' and 'non-wide'
|
||||
* width values */
|
||||
unsigned counter_normalized = 0;
|
||||
int line_width_normalized = line_width * 100;
|
||||
int additional_counter_normalized = wideglyph_width - 100;
|
||||
|
||||
/* Early return if src string length is less
|
||||
* than line width */
|
||||
if (src_end - src < line_width)
|
||||
{
|
||||
strlcpy(dst, src, dst_size);
|
||||
return;
|
||||
}
|
||||
|
||||
while (*src != '\0')
|
||||
{
|
||||
unsigned char_len = (unsigned)(utf8skip(src, 1) - src);
|
||||
counter_normalized += 100;
|
||||
|
||||
/* Prevent buffer overflow */
|
||||
if (char_len >= dst_size)
|
||||
break;
|
||||
|
||||
if (*src == ' ')
|
||||
lastspace = dst; /* Remember the location of the whitespace */
|
||||
else if (*src == '\n')
|
||||
{
|
||||
/* If newlines embedded in the input,
|
||||
* reset the index */
|
||||
lines++;
|
||||
counter_normalized = 0;
|
||||
|
||||
/* Early return if remaining src string
|
||||
* length is less than line width */
|
||||
if (src_end - src <= line_width)
|
||||
{
|
||||
strlcpy(dst, src, dst_size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (char_len >= 3)
|
||||
{
|
||||
/* Remember the location of the first byte
|
||||
* whose length as UTF-8 >= 3*/
|
||||
lastwideglyph = dst;
|
||||
counter_normalized += additional_counter_normalized;
|
||||
}
|
||||
|
||||
dst_size -= char_len;
|
||||
while (char_len--)
|
||||
*dst++ = *src++;
|
||||
|
||||
if (counter_normalized >= (unsigned)line_width_normalized)
|
||||
{
|
||||
counter_normalized = 0;
|
||||
|
||||
if (max_lines != 0 && lines >= max_lines)
|
||||
continue;
|
||||
else if (lastwideglyph && (!lastspace || lastwideglyph > lastspace))
|
||||
{
|
||||
/* Insert newline character */
|
||||
*lastwideglyph = '\n';
|
||||
lines++;
|
||||
src -= dst - lastwideglyph;
|
||||
dst = lastwideglyph + 1;
|
||||
lastwideglyph = NULL;
|
||||
|
||||
/* Early return if remaining src string
|
||||
* length is less than line width */
|
||||
if (src_end - src <= line_width)
|
||||
{
|
||||
strlcpy(dst, src, dst_size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (lastspace)
|
||||
{
|
||||
/* Replace nearest (previous) whitespace
|
||||
* with newline character */
|
||||
*lastspace = '\n';
|
||||
lines++;
|
||||
src -= dst - lastspace - 1;
|
||||
dst = lastspace + 1;
|
||||
lastspace = NULL;
|
||||
|
||||
/* Early return if remaining src string
|
||||
* length is less than line width */
|
||||
if (src_end - src < line_width)
|
||||
{
|
||||
strlcpy(dst, src, dst_size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*dst = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* string_tokenize:
|
||||
*
|
||||
* 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
|
||||
* > After each call, @str is set to the position after the
|
||||
* last found token
|
||||
* > Tokens *include* empty strings
|
||||
* Usage example:
|
||||
|
@ -286,7 +472,7 @@ char *word_wrap(char* buffer, const char *string, int line_width, bool unicode,
|
|||
* 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# */
|
||||
|
@ -299,25 +485,20 @@ char* string_tokenize(char **str, const char *delim)
|
|||
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)
|
||||
if (!(str_ptr = *str))
|
||||
return NULL;
|
||||
|
||||
/* Search for delimiter */
|
||||
delim_ptr = strstr(str_ptr, delim);
|
||||
|
||||
if (delim_ptr)
|
||||
if ((delim_ptr = strstr(str_ptr, delim)))
|
||||
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)
|
||||
if (!(token = (char *)malloc((token_len + 1) * sizeof(char))))
|
||||
return NULL;
|
||||
|
||||
/* Copy token */
|
||||
|
@ -330,42 +511,53 @@ char* string_tokenize(char **str, const char *delim)
|
|||
return token;
|
||||
}
|
||||
|
||||
/* Removes every instance of character 'c' from 'str' */
|
||||
/**
|
||||
* string_remove_all_chars:
|
||||
* @str : input string (must be non-NULL, otherwise UB)
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* 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;
|
||||
char *read_ptr = str;
|
||||
char *write_ptr = str;
|
||||
|
||||
while (*read_ptr != '\0')
|
||||
{
|
||||
*write_ptr = *read_ptr++;
|
||||
write_ptr += (*write_ptr != c) ? 1 : 0;
|
||||
if (*write_ptr != c)
|
||||
write_ptr++;
|
||||
}
|
||||
|
||||
*write_ptr = '\0';
|
||||
}
|
||||
|
||||
/* Replaces every instance of character 'find' in 'str'
|
||||
* with character 'replace' */
|
||||
/**
|
||||
* string_replace_all_chars:
|
||||
* @str : input string (must be non-NULL, otherwise UB)
|
||||
* @find : character to find
|
||||
* @replace : character to replace @find with
|
||||
*
|
||||
* 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)))
|
||||
*str_ptr++ = replace;
|
||||
}
|
||||
|
||||
/* Converts string to unsigned integer.
|
||||
* Returns 0 if string is invalid */
|
||||
/**
|
||||
* string_to_unsigned:
|
||||
* @str : input string
|
||||
*
|
||||
* Converts string to unsigned integer.
|
||||
*
|
||||
* @return 0 if string is invalid, otherwise > 0
|
||||
**/
|
||||
unsigned string_to_unsigned(const char *str)
|
||||
{
|
||||
const char *ptr = NULL;
|
||||
|
@ -382,27 +574,33 @@ unsigned string_to_unsigned(const char *str)
|
|||
return (unsigned)strtoul(str, NULL, 10);
|
||||
}
|
||||
|
||||
/* Converts hexadecimal string to unsigned integer.
|
||||
/**
|
||||
* string_hex_to_unsigned:
|
||||
* @str : input string (must be non-NULL, otherwise UB)
|
||||
*
|
||||
* Converts hexadecimal string to unsigned integer.
|
||||
* Handles optional leading '0x'.
|
||||
* Returns 0 if string is invalid */
|
||||
*
|
||||
* @return 0 if string is invalid, otherwise > 0
|
||||
**/
|
||||
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')))
|
||||
if (str[0] != '\0' && str[1] != '\0')
|
||||
{
|
||||
if ( (str[0] == '0') &&
|
||||
((str[1] == 'x') ||
|
||||
(str[1] == 'X')))
|
||||
{
|
||||
hex_str = str + 2;
|
||||
|
||||
if (string_is_empty(hex_str))
|
||||
if (string_is_empty(hex_str))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
/* Check for valid characters */
|
||||
|
@ -414,3 +612,117 @@ unsigned string_hex_to_unsigned(const char *str)
|
|||
|
||||
return (unsigned)strtoul(hex_str, NULL, 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* string_count_occurrences_single_character:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Get the total number of occurrences of character @c in @str.
|
||||
*
|
||||
* @return Total number of occurrences of character @c
|
||||
*/
|
||||
int string_count_occurrences_single_character(const char *str, char c)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (; *str; str++)
|
||||
if (*str == c)
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* string_replace_whitespace_with_single_character:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Replaces all spaces with given character @c.
|
||||
**/
|
||||
void string_replace_whitespace_with_single_character(char *str, char c)
|
||||
{
|
||||
for (; *str; str++)
|
||||
if (ISSPACE(*str))
|
||||
*str = c;
|
||||
}
|
||||
|
||||
/**
|
||||
* string_replace_multi_space_with_single_space:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Replaces multiple spaces with a single space in a string.
|
||||
**/
|
||||
void string_replace_multi_space_with_single_space(char *str)
|
||||
{
|
||||
char *str_trimmed = str;
|
||||
bool prev_is_space = false;
|
||||
bool curr_is_space = false;
|
||||
|
||||
for (; *str; str++)
|
||||
{
|
||||
curr_is_space = ISSPACE(*str);
|
||||
if (prev_is_space && curr_is_space)
|
||||
continue;
|
||||
*str_trimmed++ = *str;
|
||||
prev_is_space = curr_is_space;
|
||||
}
|
||||
*str_trimmed = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* string_remove_all_whitespace:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Remove all spaces from the given string.
|
||||
**/
|
||||
void string_remove_all_whitespace(char *str_trimmed, const char *str)
|
||||
{
|
||||
for (; *str; str++)
|
||||
if (!ISSPACE(*str))
|
||||
*str_trimmed++ = *str;
|
||||
*str_trimmed = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the last occurance of the given character in a string.
|
||||
*/
|
||||
int string_index_last_occurance(const char *str, char c)
|
||||
{
|
||||
const char *pos = strrchr(str, c);
|
||||
if (pos)
|
||||
return (int)(pos - str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* string_find_index_substring_string:
|
||||
* @str : input string (must be non-NULL, otherwise UB)
|
||||
* @substr : substring to find in @str
|
||||
*
|
||||
* Find the position of substring @substr in string @str.
|
||||
**/
|
||||
int string_find_index_substring_string(const char *str, const char *substr)
|
||||
{
|
||||
const char *pos = strstr(str, substr);
|
||||
if (pos)
|
||||
return (int)(pos - str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* string_copy_only_ascii:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* Strips non-ASCII characters from a string.
|
||||
**/
|
||||
void string_copy_only_ascii(char *str_stripped, const char *str)
|
||||
{
|
||||
for (; *str; str++)
|
||||
if (*str > 0x1F && *str < 0x7F)
|
||||
*str_stripped++ = *str;
|
||||
*str_stripped = '\0';
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
|
||||
#ifdef HAVE_THREADS
|
||||
#include <rthreads/rthreads.h>
|
||||
#include <retro_assert.h>
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
|
@ -41,8 +40,6 @@ void rtime_init(void)
|
|||
#ifdef HAVE_THREADS
|
||||
if (!rtime_localtime_lock)
|
||||
rtime_localtime_lock = slock_new();
|
||||
|
||||
retro_assert(rtime_localtime_lock);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string/stdstring.h>
|
||||
#include <string/stdstring.h> /* string_is_empty */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
|
@ -57,34 +57,18 @@
|
|||
# include <dirent.h>
|
||||
# endif
|
||||
# include <unistd.h>
|
||||
# if defined(ORBIS)
|
||||
# include <sys/fcntl.h>
|
||||
# include <sys/dirent.h>
|
||||
# include <orbisFile.h>
|
||||
# if defined(WIIU)
|
||||
# include <malloc.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined (__CELLOS_LV2__) && !defined(__PSL1GHT__)
|
||||
#include <cell/cell_fs.h>
|
||||
#define O_RDONLY CELL_FS_O_RDONLY
|
||||
#define O_WRONLY CELL_FS_O_WRONLY
|
||||
#define O_CREAT CELL_FS_O_CREAT
|
||||
#define O_TRUNC CELL_FS_O_TRUNC
|
||||
#define O_RDWR CELL_FS_O_RDWR
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
/* TODO: Some things are duplicated but I'm really afraid of breaking other platforms by touching this */
|
||||
#if defined(VITA)
|
||||
# include <psp2/io/fcntl.h>
|
||||
# include <psp2/io/dirent.h>
|
||||
# include <psp2/io/stat.h>
|
||||
#elif defined(ORBIS)
|
||||
# include <orbisFile.h>
|
||||
# include <ps4link.h>
|
||||
# include <sys/dirent.h>
|
||||
# include <sys/fcntl.h>
|
||||
#elif !defined(_WIN32)
|
||||
# if defined(PSP)
|
||||
# include <pspiofilemgr.h>
|
||||
|
@ -95,7 +79,7 @@
|
|||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(__QNX__) || defined(PSP)
|
||||
#if defined(__QNX__) || defined(PSP)
|
||||
#include <unistd.h> /* stat() is defined here */
|
||||
#endif
|
||||
|
||||
|
@ -130,24 +114,74 @@
|
|||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(ORBIS)
|
||||
#include <orbisFile.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/dirent.h>
|
||||
#endif
|
||||
|
||||
#if defined(PSP)
|
||||
#include <pspkernel.h>
|
||||
#endif
|
||||
|
||||
#if defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
|
||||
#if defined(__PS3__) || defined(__PSL1GHT__)
|
||||
#define FS_SUCCEEDED 0
|
||||
#define FS_TYPE_DIR 1
|
||||
#ifdef __PSL1GHT__
|
||||
#include <lv2/sysfs.h>
|
||||
#ifndef O_RDONLY
|
||||
#define O_RDONLY SYS_O_RDONLY
|
||||
#endif
|
||||
#ifndef O_WRONLY
|
||||
#define O_WRONLY SYS_O_WRONLY
|
||||
#endif
|
||||
#ifndef O_CREAT
|
||||
#define O_CREAT SYS_O_CREAT
|
||||
#endif
|
||||
#ifndef O_TRUNC
|
||||
#define O_TRUNC SYS_O_TRUNC
|
||||
#endif
|
||||
#ifndef O_RDWR
|
||||
#define O_RDWR SYS_O_RDWR
|
||||
#endif
|
||||
#else
|
||||
#include <cell/cell_fs.h>
|
||||
#ifndef O_RDONLY
|
||||
#define O_RDONLY CELL_FS_O_RDONLY
|
||||
#endif
|
||||
#ifndef O_WRONLY
|
||||
#define O_WRONLY CELL_FS_O_WRONLY
|
||||
#endif
|
||||
#ifndef O_CREAT
|
||||
#define O_CREAT CELL_FS_O_CREAT
|
||||
#endif
|
||||
#ifndef O_TRUNC
|
||||
#define O_TRUNC CELL_FS_O_TRUNC
|
||||
#endif
|
||||
#ifndef O_RDWR
|
||||
#define O_RDWR CELL_FS_O_RDWR
|
||||
#endif
|
||||
#ifndef sysFsStat
|
||||
#define sysFsStat cellFsStat
|
||||
#endif
|
||||
#ifndef sysFSDirent
|
||||
#define sysFSDirent CellFsDirent
|
||||
#endif
|
||||
#ifndef sysFsOpendir
|
||||
#define sysFsOpendir cellFsOpendir
|
||||
#endif
|
||||
#ifndef sysFsReaddir
|
||||
#define sysFsReaddir cellFsReaddir
|
||||
#endif
|
||||
#ifndef sysFSDirent
|
||||
#define sysFSDirent CellFsDirent
|
||||
#endif
|
||||
#ifndef sysFsClosedir
|
||||
#define sysFsClosedir cellFsClosedir
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(VITA)
|
||||
#define FIO_S_ISDIR SCE_S_ISDIR
|
||||
#endif
|
||||
|
||||
#if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(__QNX__) || defined(PSP)
|
||||
#if defined(__QNX__) || defined(PSP)
|
||||
#include <unistd.h> /* stat() is defined here */
|
||||
#endif
|
||||
|
||||
|
@ -168,7 +202,9 @@
|
|||
|
||||
#include <vfs/vfs_implementation.h>
|
||||
#include <libretro.h>
|
||||
#if defined(HAVE_MMAP)
|
||||
#include <memmap.h>
|
||||
#endif
|
||||
#include <encodings/utf.h>
|
||||
#include <compat/fopen_utf8.h>
|
||||
#include <file/file_path.h>
|
||||
|
@ -201,13 +237,6 @@ int64_t retro_vfs_file_seek_internal(
|
|||
#ifdef ATLEAST_VC2005
|
||||
/* VC2005 and up have a special 64-bit fseek */
|
||||
return _fseeki64(stream->fp, offset, whence);
|
||||
#elif defined(ORBIS)
|
||||
{
|
||||
int ret = orbisLseek(stream->fd, offset, whence);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
#elif defined(HAVE_64BIT_OFFSETS)
|
||||
return fseeko(stream->fp, (off_t)offset, whence);
|
||||
#else
|
||||
|
@ -270,19 +299,6 @@ int64_t retro_vfs_file_seek_internal(
|
|||
libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
||||
const char *path, unsigned mode, unsigned hints)
|
||||
{
|
||||
#if defined(VFS_FRONTEND) || defined(HAVE_CDROM)
|
||||
int path_len = (int)strlen(path);
|
||||
#endif
|
||||
#ifdef VFS_FRONTEND
|
||||
const char *dumb_prefix = "vfsonly://";
|
||||
size_t dumb_prefix_siz = STRLEN_CONST("vfsonly://");
|
||||
int dumb_prefix_len = (int)dumb_prefix_siz;
|
||||
#endif
|
||||
#ifdef HAVE_CDROM
|
||||
const char *cdrom_prefix = "cdrom://";
|
||||
size_t cdrom_prefix_siz = STRLEN_CONST("cdrom://");
|
||||
int cdrom_prefix_len = (int)cdrom_prefix_siz;
|
||||
#endif
|
||||
int flags = 0;
|
||||
const char *mode_str = NULL;
|
||||
libretro_vfs_implementation_file *stream =
|
||||
|
@ -307,9 +323,18 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
|||
stream->scheme = VFS_SCHEME_NONE;
|
||||
|
||||
#ifdef VFS_FRONTEND
|
||||
if (path_len >= dumb_prefix_len)
|
||||
if (!memcmp(path, dumb_prefix, dumb_prefix_len))
|
||||
path += dumb_prefix_siz;
|
||||
if ( path
|
||||
&& path[0] == 'v'
|
||||
&& path[1] == 'f'
|
||||
&& path[2] == 's'
|
||||
&& path[3] == 'o'
|
||||
&& path[4] == 'n'
|
||||
&& path[5] == 'l'
|
||||
&& path[6] == 'y'
|
||||
&& path[7] == ':'
|
||||
&& path[8] == '/'
|
||||
&& path[9] == '/')
|
||||
path += sizeof("vfsonly://")-1;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CDROM
|
||||
|
@ -326,13 +351,19 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
|||
stream->cdrom.last_frame[0] = '\0';
|
||||
stream->cdrom.last_frame_valid = false;
|
||||
|
||||
if (path_len > cdrom_prefix_len)
|
||||
if ( path
|
||||
&& path[0] == 'c'
|
||||
&& path[1] == 'd'
|
||||
&& path[2] == 'r'
|
||||
&& path[3] == 'o'
|
||||
&& path[4] == 'm'
|
||||
&& path[5] == ':'
|
||||
&& path[6] == '/'
|
||||
&& path[7] == '/'
|
||||
&& path[8] != '\0')
|
||||
{
|
||||
if (!memcmp(path, cdrom_prefix, cdrom_prefix_len))
|
||||
{
|
||||
path += cdrom_prefix_siz;
|
||||
stream->scheme = VFS_SCHEME_CDROM;
|
||||
}
|
||||
path += sizeof("cdrom://")-1;
|
||||
stream->scheme = VFS_SCHEME_CDROM;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -360,24 +391,20 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
|||
mode_str = "wb";
|
||||
|
||||
flags = O_WRONLY | O_CREAT | O_TRUNC;
|
||||
#if !defined(ORBIS)
|
||||
#if !defined(_WIN32)
|
||||
flags |= S_IRUSR | S_IWUSR;
|
||||
#else
|
||||
flags |= O_BINARY;
|
||||
#endif
|
||||
#endif
|
||||
break;
|
||||
|
||||
case RETRO_VFS_FILE_ACCESS_READ_WRITE:
|
||||
mode_str = "w+b";
|
||||
flags = O_RDWR | O_CREAT | O_TRUNC;
|
||||
#if !defined(ORBIS)
|
||||
#if !defined(_WIN32)
|
||||
flags |= S_IRUSR | S_IWUSR;
|
||||
#else
|
||||
flags |= O_BINARY;
|
||||
#endif
|
||||
#endif
|
||||
break;
|
||||
|
||||
|
@ -386,12 +413,10 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
|||
mode_str = "r+b";
|
||||
|
||||
flags = O_RDWR;
|
||||
#if !defined(ORBIS)
|
||||
#if !defined(_WIN32)
|
||||
flags |= S_IRUSR | S_IWUSR;
|
||||
#else
|
||||
flags |= O_BINARY;
|
||||
#endif
|
||||
#endif
|
||||
break;
|
||||
|
||||
|
@ -401,15 +426,6 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
|||
|
||||
if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
|
||||
{
|
||||
#ifdef ORBIS
|
||||
int fd = orbisOpen(path, flags, 0644);
|
||||
if (fd < 0)
|
||||
{
|
||||
stream->fd = -1;
|
||||
goto error;
|
||||
}
|
||||
stream->fd = fd;
|
||||
#else
|
||||
FILE *fp;
|
||||
#ifdef HAVE_CDROM
|
||||
if (stream->scheme == VFS_SCHEME_CDROM)
|
||||
|
@ -426,13 +442,12 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
|||
else
|
||||
#endif
|
||||
{
|
||||
fp = (FILE*)fopen_utf8(path, mode_str);
|
||||
|
||||
if (!fp)
|
||||
if (!(fp = (FILE*)fopen_utf8(path, mode_str)))
|
||||
goto error;
|
||||
|
||||
stream->fp = fp;
|
||||
}
|
||||
|
||||
/* Regarding setvbuf:
|
||||
*
|
||||
* https://www.freebsd.org/cgi/man.cgi?query=setvbuf&apropos=0&sektion=0&manpath=FreeBSD+11.1-RELEASE&arch=default&format=html
|
||||
|
@ -453,14 +468,17 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
|||
if (stream->fp)
|
||||
setvbuf(stream->fp, stream->buf, _IOFBF, 0x10000);
|
||||
}
|
||||
#elif !defined(PSP)
|
||||
#elif defined(WIIU)
|
||||
if (stream->scheme != VFS_SCHEME_CDROM)
|
||||
{
|
||||
const int bufsize = 128 * 1024;
|
||||
stream->buf = (char*)memalign(0x40, bufsize);
|
||||
if (stream->fp)
|
||||
setvbuf(stream->fp, stream->buf, _IOFBF, bufsize);
|
||||
stream->buf = (char*)calloc(1, 0x4000);
|
||||
if (stream->fp)
|
||||
setvbuf(stream->fp, stream->buf, _IOFBF, 0x4000);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
else
|
||||
|
@ -496,18 +514,12 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
|||
|
||||
retro_vfs_file_seek_internal(stream, 0, SEEK_SET);
|
||||
|
||||
stream->mapped = (uint8_t*)mmap((void*)0,
|
||||
stream->mapsize, PROT_READ, MAP_SHARED, stream->fd, 0);
|
||||
|
||||
if (stream->mapped == MAP_FAILED)
|
||||
if ((stream->mapped = (uint8_t*)mmap((void*)0,
|
||||
stream->mapsize, PROT_READ, MAP_SHARED, stream->fd, 0)) == MAP_FAILED)
|
||||
stream->hints &= ~RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifdef ORBIS
|
||||
stream->size = orbisLseek(stream->fd, 0, SEEK_END);
|
||||
orbisLseek(stream->fd, 0, SEEK_SET);
|
||||
#else
|
||||
#ifdef HAVE_CDROM
|
||||
if (stream->scheme == VFS_SCHEME_CDROM)
|
||||
{
|
||||
|
@ -528,7 +540,6 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
|
|||
|
||||
retro_vfs_file_seek_internal(stream, 0, SEEK_SET);
|
||||
}
|
||||
#endif
|
||||
return stream;
|
||||
|
||||
error:
|
||||
|
@ -563,14 +574,7 @@ int retro_vfs_file_close_impl(libretro_vfs_implementation_file *stream)
|
|||
}
|
||||
|
||||
if (stream->fd > 0)
|
||||
{
|
||||
#ifdef ORBIS
|
||||
orbisClose(stream->fd);
|
||||
stream->fd = -1;
|
||||
#else
|
||||
close(stream->fd);
|
||||
#endif
|
||||
}
|
||||
#ifdef HAVE_CDROM
|
||||
end:
|
||||
if (stream->cdrom.cue_buf)
|
||||
|
@ -593,12 +597,7 @@ int retro_vfs_file_error_impl(libretro_vfs_implementation_file *stream)
|
|||
if (stream->scheme == VFS_SCHEME_CDROM)
|
||||
return retro_vfs_file_error_cdrom(stream);
|
||||
#endif
|
||||
#ifdef ORBIS
|
||||
/* TODO/FIXME - implement this? */
|
||||
return 0;
|
||||
#else
|
||||
return ferror(stream->fp);
|
||||
#endif
|
||||
}
|
||||
|
||||
int64_t retro_vfs_file_size_impl(libretro_vfs_implementation_file *stream)
|
||||
|
@ -610,18 +609,20 @@ int64_t retro_vfs_file_size_impl(libretro_vfs_implementation_file *stream)
|
|||
|
||||
int64_t retro_vfs_file_truncate_impl(libretro_vfs_implementation_file *stream, int64_t length)
|
||||
{
|
||||
if (!stream)
|
||||
return -1;
|
||||
|
||||
#ifdef _WIN32
|
||||
if (_chsize(_fileno(stream->fp), length) != 0)
|
||||
return -1;
|
||||
if (stream && _chsize(_fileno(stream->fp), length) == 0)
|
||||
{
|
||||
stream->size = length;
|
||||
return 0;
|
||||
}
|
||||
#elif !defined(VITA) && !defined(PSP) && !defined(PS2) && !defined(ORBIS) && (!defined(SWITCH) || defined(HAVE_LIBNX))
|
||||
if (ftruncate(fileno(stream->fp), (off_t)length) != 0)
|
||||
return -1;
|
||||
if (stream && ftruncate(fileno(stream->fp), (off_t)length) == 0)
|
||||
{
|
||||
stream->size = length;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream)
|
||||
|
@ -635,14 +636,6 @@ int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream)
|
|||
if (stream->scheme == VFS_SCHEME_CDROM)
|
||||
return retro_vfs_file_tell_cdrom(stream);
|
||||
#endif
|
||||
#ifdef ORBIS
|
||||
{
|
||||
int64_t ret = orbisLseek(stream->fd, 0, SEEK_CUR);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
#ifdef ATLEAST_VC2005
|
||||
/* VC2005 and up have a special 64-bit ftell */
|
||||
return _ftelli64(stream->fp);
|
||||
|
@ -650,7 +643,6 @@ int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream)
|
|||
return ftello(stream->fp);
|
||||
#else
|
||||
return ftell(stream->fp);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#ifdef HAVE_MMAP
|
||||
|
@ -669,21 +661,7 @@ int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream)
|
|||
int64_t retro_vfs_file_seek_impl(libretro_vfs_implementation_file *stream,
|
||||
int64_t offset, int seek_position)
|
||||
{
|
||||
int whence = -1;
|
||||
switch (seek_position)
|
||||
{
|
||||
case RETRO_VFS_SEEK_POSITION_START:
|
||||
whence = SEEK_SET;
|
||||
break;
|
||||
case RETRO_VFS_SEEK_POSITION_CURRENT:
|
||||
whence = SEEK_CUR;
|
||||
break;
|
||||
case RETRO_VFS_SEEK_POSITION_END:
|
||||
whence = SEEK_END;
|
||||
break;
|
||||
}
|
||||
|
||||
return retro_vfs_file_seek_internal(stream, offset, whence);
|
||||
return retro_vfs_file_seek_internal(stream, offset, seek_position);
|
||||
}
|
||||
|
||||
int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream,
|
||||
|
@ -698,13 +676,7 @@ int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream,
|
|||
if (stream->scheme == VFS_SCHEME_CDROM)
|
||||
return retro_vfs_file_read_cdrom(stream, s, len);
|
||||
#endif
|
||||
#ifdef ORBIS
|
||||
if (orbisRead(stream->fd, s, (size_t)len) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
#else
|
||||
return fread(s, 1, (size_t)len, stream->fp);
|
||||
#endif
|
||||
}
|
||||
#ifdef HAVE_MMAP
|
||||
if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
|
||||
|
@ -727,36 +699,41 @@ int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream,
|
|||
|
||||
int64_t retro_vfs_file_write_impl(libretro_vfs_implementation_file *stream, const void *s, uint64_t len)
|
||||
{
|
||||
int64_t pos = 0;
|
||||
size_t result = -1;
|
||||
|
||||
if (!stream)
|
||||
return -1;
|
||||
|
||||
if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
|
||||
{
|
||||
#ifdef ORBIS
|
||||
if (orbisWrite(stream->fd, s, (size_t)len) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
#else
|
||||
return fwrite(s, 1, (size_t)len, stream->fp);
|
||||
#endif
|
||||
}
|
||||
pos = retro_vfs_file_tell_impl(stream);
|
||||
result = fwrite(s, 1, (size_t)len, stream->fp);
|
||||
|
||||
if (result != -1 && pos + result > stream->size)
|
||||
stream->size = pos + result;
|
||||
|
||||
return result;
|
||||
}
|
||||
#ifdef HAVE_MMAP
|
||||
if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
|
||||
return -1;
|
||||
#endif
|
||||
return write(stream->fd, s, (size_t)len);
|
||||
|
||||
pos = retro_vfs_file_tell_impl(stream);
|
||||
result = write(stream->fd, s, (size_t)len);
|
||||
|
||||
if (result != -1 && pos + result > stream->size)
|
||||
stream->size = pos + result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int retro_vfs_file_flush_impl(libretro_vfs_implementation_file *stream)
|
||||
{
|
||||
if (!stream)
|
||||
return -1;
|
||||
#ifdef ORBIS
|
||||
return 0;
|
||||
#else
|
||||
return fflush(stream->fp) == 0 ? 0 : -1;
|
||||
#endif
|
||||
if (stream && fflush(stream->fp) == 0)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int retro_vfs_file_remove_impl(const char *path)
|
||||
|
@ -772,9 +749,7 @@ int retro_vfs_file_remove_impl(const char *path)
|
|||
if (!path || !*path)
|
||||
return -1;
|
||||
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500
|
||||
path_local = utf8_to_local_string_alloc(path);
|
||||
|
||||
if (path_local)
|
||||
if ((path_local = utf8_to_local_string_alloc(path)))
|
||||
{
|
||||
int ret = remove(path_local);
|
||||
free(path_local);
|
||||
|
@ -783,9 +758,7 @@ int retro_vfs_file_remove_impl(const char *path)
|
|||
return 0;
|
||||
}
|
||||
#else
|
||||
path_wide = utf8_to_utf16_string_alloc(path);
|
||||
|
||||
if (path_wide)
|
||||
if ((path_wide = utf8_to_utf16_string_alloc(path)))
|
||||
{
|
||||
int ret = _wremove(path_wide);
|
||||
free(path_wide);
|
||||
|
@ -794,16 +767,11 @@ int retro_vfs_file_remove_impl(const char *path)
|
|||
return 0;
|
||||
}
|
||||
#endif
|
||||
return -1;
|
||||
#elif defined(ORBIS)
|
||||
/* Orbis
|
||||
* TODO/FIXME - stub for now */
|
||||
return 0;
|
||||
#else
|
||||
if (remove(path) == 0)
|
||||
return 0;
|
||||
return -1;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
int retro_vfs_file_rename_impl(const char *old_path, const char *new_path)
|
||||
|
@ -855,13 +823,6 @@ int retro_vfs_file_rename_impl(const char *old_path, const char *new_path)
|
|||
#endif
|
||||
return ret;
|
||||
|
||||
#elif defined(ORBIS)
|
||||
/* Orbis */
|
||||
/* TODO/FIXME - Stub for now */
|
||||
if (!old_path || !*old_path || !new_path || !*new_path)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
#else
|
||||
/* Every other platform */
|
||||
if (!old_path || !*old_path || !new_path || !*new_path)
|
||||
|
@ -883,7 +844,7 @@ int retro_vfs_stat_impl(const char *path, int32_t *size)
|
|||
{
|
||||
bool is_dir = false;
|
||||
bool is_character_special = false;
|
||||
#if defined(VITA) || defined(PSP)
|
||||
#if defined(VITA)
|
||||
/* Vita / PSP */
|
||||
SceIoStat buf;
|
||||
int dir_ret;
|
||||
|
@ -896,7 +857,7 @@ int retro_vfs_stat_impl(const char *path, int32_t *size)
|
|||
tmp = strdup(path);
|
||||
len = strlen(tmp);
|
||||
if (tmp[len-1] == '/')
|
||||
tmp[len-1] = '\0';
|
||||
tmp[len-1] = '\0';
|
||||
|
||||
dir_ret = sceIoGetstat(tmp, &buf);
|
||||
free(tmp);
|
||||
|
@ -907,35 +868,19 @@ int retro_vfs_stat_impl(const char *path, int32_t *size)
|
|||
*size = (int32_t)buf.st_size;
|
||||
|
||||
is_dir = FIO_S_ISDIR(buf.st_mode);
|
||||
#elif defined(ORBIS)
|
||||
/* Orbis */
|
||||
int dir_ret = 0;
|
||||
#elif defined(__PSL1GHT__) || defined(__PS3__)
|
||||
/* Lowlevel Lv2 */
|
||||
sysFSStat buf;
|
||||
|
||||
if (!path || !*path)
|
||||
return 0;
|
||||
|
||||
if (size)
|
||||
*size = (int32_t)buf.st_size;
|
||||
|
||||
dir_ret = orbisDopen(path);
|
||||
is_dir = dir_ret > 0;
|
||||
orbisDclose(dir_ret);
|
||||
|
||||
is_character_special = S_ISCHR(buf.st_mode);
|
||||
#elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
|
||||
/* CellOS Lv2 */
|
||||
CellFsStat buf;
|
||||
|
||||
if (!path || !*path)
|
||||
return 0;
|
||||
if (cellFsStat(path, &buf) < 0)
|
||||
if (sysFsStat(path, &buf) < 0)
|
||||
return 0;
|
||||
|
||||
if (size)
|
||||
*size = (int32_t)buf.st_size;
|
||||
|
||||
is_dir = ((buf.st_mode & S_IFMT) == S_IFDIR);
|
||||
|
||||
#elif defined(_WIN32)
|
||||
/* Windows */
|
||||
DWORD file_info;
|
||||
|
@ -988,12 +933,10 @@ int retro_vfs_stat_impl(const char *path, int32_t *size)
|
|||
if (string_is_empty(path))
|
||||
return 0;
|
||||
|
||||
path_buf = strdup(path);
|
||||
if (!path_buf)
|
||||
if (!(path_buf = strdup(path)))
|
||||
return 0;
|
||||
|
||||
len = strlen(path_buf);
|
||||
if (len > 0)
|
||||
if ((len = strlen(path_buf)) > 0)
|
||||
if (path_buf[len - 1] == '/')
|
||||
path_buf[len - 1] = '\0';
|
||||
|
||||
|
@ -1029,7 +972,7 @@ int retro_vfs_stat_impl(const char *path, int32_t *size)
|
|||
|
||||
#if defined(VITA)
|
||||
#define path_mkdir_error(ret) (((ret) == SCE_ERROR_ERRNO_EEXIST))
|
||||
#elif defined(PSP) || defined(PS2) || defined(_3DS) || defined(WIIU) || defined(SWITCH) || defined(ORBIS)
|
||||
#elif defined(PSP) || defined(PS2) || defined(_3DS) || defined(WIIU) || defined(SWITCH)
|
||||
#define path_mkdir_error(ret) ((ret) == -1)
|
||||
#else
|
||||
#define path_mkdir_error(ret) ((ret) < 0 && errno == EEXIST)
|
||||
|
@ -1052,13 +995,11 @@ int retro_vfs_mkdir_impl(const char *dir)
|
|||
#endif
|
||||
#elif defined(IOS)
|
||||
int ret = mkdir(dir, 0755);
|
||||
#elif defined(VITA) || defined(PSP)
|
||||
#elif defined(VITA)
|
||||
int ret = sceIoMkdir(dir, 0777);
|
||||
#elif defined(ORBIS)
|
||||
int ret = orbisMkdir(dir, 0755);
|
||||
#elif defined(__QNX__)
|
||||
int ret = mkdir(dir, 0777);
|
||||
#elif defined(GEKKO)
|
||||
#elif defined(GEKKO) || defined(WIIU)
|
||||
/* On GEKKO platforms, mkdir() fails if
|
||||
* the path has a trailing slash. We must
|
||||
* therefore remove it. */
|
||||
|
@ -1105,16 +1046,13 @@ struct libretro_vfs_implementation_dir
|
|||
HANDLE directory;
|
||||
bool next;
|
||||
char path[PATH_MAX_LENGTH];
|
||||
#elif defined(VITA) || defined(PSP)
|
||||
#elif defined(VITA)
|
||||
SceUID directory;
|
||||
SceIoDirent entry;
|
||||
#elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
|
||||
CellFsErrno error;
|
||||
#elif defined(__PSL1GHT__) || defined(__PS3__)
|
||||
int error;
|
||||
int directory;
|
||||
CellFsDirent entry;
|
||||
#elif defined(ORBIS)
|
||||
int directory;
|
||||
struct dirent entry;
|
||||
sysFSDirent entry;
|
||||
#else
|
||||
DIR *directory;
|
||||
const struct dirent *entry;
|
||||
|
@ -1125,10 +1063,10 @@ static bool dirent_check_error(libretro_vfs_implementation_dir *rdir)
|
|||
{
|
||||
#if defined(_WIN32)
|
||||
return (rdir->directory == INVALID_HANDLE_VALUE);
|
||||
#elif defined(VITA) || defined(PSP) || defined(ORBIS)
|
||||
#elif defined(VITA) || defined(ORBIS)
|
||||
return (rdir->directory < 0);
|
||||
#elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
|
||||
return (rdir->error != CELL_FS_SUCCEEDED);
|
||||
#elif defined(__PSL1GHT__) || defined(__PS3__)
|
||||
return (rdir->error != FS_SUCCEEDED);
|
||||
#else
|
||||
return !(rdir->directory);
|
||||
#endif
|
||||
|
@ -1138,7 +1076,6 @@ libretro_vfs_implementation_dir *retro_vfs_opendir_impl(
|
|||
const char *name, bool include_hidden)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
unsigned path_len;
|
||||
char path_buf[1024];
|
||||
size_t copied = 0;
|
||||
#if defined(LEGACY_WIN32)
|
||||
|
@ -1149,28 +1086,25 @@ libretro_vfs_implementation_dir *retro_vfs_opendir_impl(
|
|||
#endif
|
||||
libretro_vfs_implementation_dir *rdir;
|
||||
|
||||
/*Reject null or empty string paths*/
|
||||
/* Reject NULL or empty string paths*/
|
||||
if (!name || (*name == 0))
|
||||
return NULL;
|
||||
|
||||
/*Allocate RDIR struct. Tidied later with retro_closedir*/
|
||||
rdir = (libretro_vfs_implementation_dir*)calloc(1, sizeof(*rdir));
|
||||
if (!rdir)
|
||||
if (!(rdir = (libretro_vfs_implementation_dir*)
|
||||
calloc(1, sizeof(*rdir))))
|
||||
return NULL;
|
||||
|
||||
rdir->orig_path = strdup(name);
|
||||
|
||||
#if defined(_WIN32)
|
||||
path_buf[0] = '\0';
|
||||
path_len = strlen(name);
|
||||
|
||||
copied = strlcpy(path_buf, name, sizeof(path_buf));
|
||||
|
||||
/* Non-NT platforms don't like extra slashes in the path */
|
||||
if (name[path_len - 1] != '\\')
|
||||
path_buf[copied++] = '\\';
|
||||
if (path_buf[copied - 1] != '\\')
|
||||
path_buf [copied++] = '\\';
|
||||
|
||||
path_buf[copied] = '*';
|
||||
path_buf[copied ] = '*';
|
||||
path_buf[copied+1] = '\0';
|
||||
|
||||
#if defined(LEGACY_WIN32)
|
||||
|
@ -1187,15 +1121,13 @@ libretro_vfs_implementation_dir *retro_vfs_opendir_impl(
|
|||
free(path_wide);
|
||||
#endif
|
||||
|
||||
#elif defined(VITA) || defined(PSP)
|
||||
#elif defined(VITA)
|
||||
rdir->directory = sceIoDopen(name);
|
||||
#elif defined(_3DS)
|
||||
rdir->directory = !string_is_empty(name) ? opendir(name) : NULL;
|
||||
rdir->entry = NULL;
|
||||
#elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
|
||||
rdir->error = cellFsOpendir(name, &rdir->directory);
|
||||
#elif defined(ORBIS)
|
||||
rdir->directory = orbisDopen(name);
|
||||
#elif defined(__PSL1GHT__) || defined(__PS3__)
|
||||
rdir->error = sysFsOpendir(name, &rdir->directory);
|
||||
#else
|
||||
rdir->directory = opendir(name);
|
||||
rdir->entry = NULL;
|
||||
|
@ -1227,14 +1159,12 @@ bool retro_vfs_readdir_impl(libretro_vfs_implementation_dir *rdir)
|
|||
|
||||
rdir->next = true;
|
||||
return (rdir->directory != INVALID_HANDLE_VALUE);
|
||||
#elif defined(VITA) || defined(PSP)
|
||||
#elif defined(VITA)
|
||||
return (sceIoDread(rdir->directory, &rdir->entry) > 0);
|
||||
#elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
|
||||
#elif defined(__PSL1GHT__) || defined(__PS3__)
|
||||
uint64_t nread;
|
||||
rdir->error = cellFsReaddir(rdir->directory, &rdir->entry, &nread);
|
||||
rdir->error = sysFsReaddir(rdir->directory, &rdir->entry, &nread);
|
||||
return (nread != 0);
|
||||
#elif defined(ORBIS)
|
||||
return (orbisDread(rdir->directory, &rdir->entry) > 0);
|
||||
#else
|
||||
return ((rdir->entry = readdir(rdir->directory)) != NULL);
|
||||
#endif
|
||||
|
@ -1253,7 +1183,7 @@ const char *retro_vfs_dirent_get_name_impl(libretro_vfs_implementation_dir *rdir
|
|||
if (name)
|
||||
free(name);
|
||||
return (char*)rdir->entry.cFileName;
|
||||
#elif defined(VITA) || defined(PSP) || defined(__CELLOS_LV2__) && !defined(__PSL1GHT__) || defined(ORBIS)
|
||||
#elif defined(VITA) || defined(__PSL1GHT__) || defined(__PS3__)
|
||||
return rdir->entry.d_name;
|
||||
#else
|
||||
if (!rdir || !rdir->entry)
|
||||
|
@ -1267,22 +1197,12 @@ bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *rdir)
|
|||
#if defined(_WIN32)
|
||||
const WIN32_FIND_DATA *entry = (const WIN32_FIND_DATA*)&rdir->entry;
|
||||
return entry->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
|
||||
#elif defined(PSP) || defined(VITA)
|
||||
const SceIoDirent *entry = (const SceIoDirent*)&rdir->entry;
|
||||
#if defined(PSP)
|
||||
return (entry->d_stat.st_attr & FIO_SO_IFDIR) == FIO_SO_IFDIR;
|
||||
#elif defined(VITA)
|
||||
const SceIoDirent *entry = (const SceIoDirent*)&rdir->entry;
|
||||
return SCE_S_ISDIR(entry->d_stat.st_mode);
|
||||
#endif
|
||||
#elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
|
||||
CellFsDirent *entry = (CellFsDirent*)&rdir->entry;
|
||||
return (entry->d_type == CELL_FS_TYPE_DIRECTORY);
|
||||
#elif defined(ORBIS)
|
||||
const struct dirent *entry = &rdir->entry;
|
||||
if (entry->d_type == DT_DIR)
|
||||
return true;
|
||||
if (!(entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK))
|
||||
return false;
|
||||
#elif defined(__PSL1GHT__) || defined(__PS3__)
|
||||
sysFSDirent *entry = (sysFSDirent*)&rdir->entry;
|
||||
return (entry->d_type == FS_TYPE_DIR);
|
||||
#else
|
||||
struct stat buf;
|
||||
char path[PATH_MAX_LENGTH];
|
||||
|
@ -1295,8 +1215,7 @@ bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *rdir)
|
|||
return false;
|
||||
#endif
|
||||
/* dirent struct doesn't have d_type, do it the slow way ... */
|
||||
path[0] = '\0';
|
||||
fill_pathname_join(path, rdir->orig_path, retro_vfs_dirent_get_name_impl(rdir), sizeof(path));
|
||||
fill_pathname_join_special(path, rdir->orig_path, retro_vfs_dirent_get_name_impl(rdir), sizeof(path));
|
||||
if (stat(path, &buf) < 0)
|
||||
return false;
|
||||
return S_ISDIR(buf.st_mode);
|
||||
|
@ -1311,12 +1230,10 @@ int retro_vfs_closedir_impl(libretro_vfs_implementation_dir *rdir)
|
|||
#if defined(_WIN32)
|
||||
if (rdir->directory != INVALID_HANDLE_VALUE)
|
||||
FindClose(rdir->directory);
|
||||
#elif defined(VITA) || defined(PSP)
|
||||
#elif defined(VITA)
|
||||
sceIoDclose(rdir->directory);
|
||||
#elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
|
||||
rdir->error = cellFsClosedir(rdir->directory);
|
||||
#elif defined(ORBIS)
|
||||
orbisDclose(rdir->directory);
|
||||
#elif defined(__PSL1GHT__) || defined(__PS3__)
|
||||
rdir->error = sysFsClosedir(rdir->directory);
|
||||
#else
|
||||
if (rdir->directory)
|
||||
closedir(rdir->directory);
|
||||
|
|
Loading…
Reference in New Issue