Revert "Updated to current libretro-common (#269)" (#271)

This reverts commit 9481e11d50.
This commit is contained in:
LibretroAdmin 2024-06-28 20:37:41 -05:00 committed by GitHub
parent fe131fb544
commit 5d47507d3e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 1197 additions and 2237 deletions

View File

@ -60,3 +60,10 @@ size_t strlcat(char *dest, const char *source, size_t size)
return len + strlcpy(dest, source, size); return len + strlcpy(dest, source, size);
} }
#endif #endif
char *strldup(const char *s, size_t n)
{
char *dst = (char*)malloc(sizeof(char) * (n + 1));
strlcpy(dst, s, n);
return dst;
}

View File

@ -37,28 +37,27 @@
void *fopen_utf8(const char * filename, const char * mode) void *fopen_utf8(const char * filename, const char * mode)
{ {
#if defined(LEGACY_WIN32) #if defined(LEGACY_WIN32)
FILE *ret = NULL;
char * filename_local = utf8_to_local_string_alloc(filename); char * filename_local = utf8_to_local_string_alloc(filename);
if (!filename_local)
return NULL;
ret = fopen(filename_local, mode);
if (filename_local) if (filename_local)
{
FILE *ret = fopen(filename_local, mode);
free(filename_local); free(filename_local);
return ret; return ret;
}
#else #else
wchar_t * filename_w = utf8_to_utf16_string_alloc(filename); wchar_t * filename_w = utf8_to_utf16_string_alloc(filename);
wchar_t * mode_w = utf8_to_utf16_string_alloc(mode);
FILE* ret = NULL;
if (filename_w && mode_w)
ret = _wfopen(filename_w, mode_w);
if (filename_w) 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); free(filename_w);
return ret; if (mode_w)
} free(mode_w);
return ret;
#endif #endif
return NULL;
} }
#endif #endif

View File

@ -51,12 +51,9 @@ static unsigned leading_ones(uint8_t c)
return ones; return ones;
} }
/** /* Simple implementation. Assumes the sequence is
* utf8_conv_utf32: * properly synchronized and terminated. */
*
* Simple implementation. Assumes the sequence is
* properly synchronized and terminated.
**/
size_t utf8_conv_utf32(uint32_t *out, size_t out_chars, size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
const char *in, size_t in_size) const char *in, size_t in_size)
{ {
@ -82,7 +79,7 @@ size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
for (i = 0; i < extra; i++, in++, shift -= 6) for (i = 0; i < extra; i++, in++, shift -= 6)
c |= (*in & 0x3f) << shift; c |= (*in & 0x3f) << shift;
*out++ = c; *out++ = c;
in_size -= 1 + extra; in_size -= 1 + extra;
out_chars--; out_chars--;
ret++; ret++;
@ -91,11 +88,6 @@ size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
return ret; return ret;
} }
/**
* utf16_conv_utf8:
*
* Leaf function.
**/
bool utf16_conv_utf8(uint8_t *out, size_t *out_chars, bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
const uint16_t *in, size_t in_size) const uint16_t *in, size_t in_size)
{ {
@ -156,20 +148,16 @@ bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
return false; return false;
} }
/** /* Acts mostly like strlcpy.
* utf8cpy:
*
* Acts mostly like strlcpy.
* *
* Copies the given number of UTF-8 characters, * 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.
* @s is assumed valid UTF-8. * Does not copy half a character.
* Use only if @chars is considerably less than @d_len.
* *
* @return Number of bytes. * Returns number of bytes. 's' is assumed valid UTF-8.
**/ * Use only if 'chars' is considerably less than 'd_len'. */
size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars) size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars)
{ {
const uint8_t *sb = (const uint8_t*)s; const uint8_t *sb = (const uint8_t*)s;
@ -198,11 +186,6 @@ size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars)
return sb-sb_org; return sb-sb_org;
} }
/**
* utf8skip:
*
* Leaf function
**/
const char *utf8skip(const char *str, size_t chars) const char *utf8skip(const char *str, size_t chars)
{ {
const uint8_t *strb = (const uint8_t*)str; const uint8_t *strb = (const uint8_t*)str;
@ -221,11 +204,6 @@ const char *utf8skip(const char *str, size_t chars)
return (const char*)strb; return (const char*)strb;
} }
/**
* utf8len:
*
* Leaf function.
**/
size_t utf8len(const char *string) size_t utf8len(const char *string)
{ {
size_t ret = 0; size_t ret = 0;
@ -242,15 +220,7 @@ size_t utf8len(const char *string)
return ret; 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) uint32_t utf8_walk(const char **string)
{ {
uint8_t first = UTF8_WALKBYTE(string); uint8_t first = UTF8_WALKBYTE(string);
@ -278,23 +248,24 @@ static bool utf16_to_char(uint8_t **utf_data,
size_t *dest_len, const uint16_t *in) size_t *dest_len, const uint16_t *in)
{ {
unsigned len = 0; unsigned len = 0;
while (in[len] != '\0') while (in[len] != '\0')
len++; len++;
utf16_conv_utf8(NULL, dest_len, in, len); utf16_conv_utf8(NULL, dest_len, in, len);
*dest_len += 1; *dest_len += 1;
if ((*utf_data = (uint8_t*)malloc(*dest_len)) != 0) *utf_data = (uint8_t*)malloc(*dest_len);
return utf16_conv_utf8(*utf_data, dest_len, in, len); if (*utf_data == 0)
return false; return false;
return utf16_conv_utf8(*utf_data, dest_len, in, len);
} }
/**
* utf16_to_char_string:
**/
bool utf16_to_char_string(const uint16_t *in, char *s, size_t len) bool utf16_to_char_string(const uint16_t *in, char *s, size_t len)
{ {
size_t dest_len = 0; size_t dest_len = 0;
uint8_t *utf16_data = NULL; uint8_t *utf16_data = NULL;
bool ret = utf16_to_char(&utf16_data, &dest_len, in); bool ret = utf16_to_char(&utf16_data, &dest_len, in);
if (ret) if (ret)
{ {
@ -303,17 +274,13 @@ bool utf16_to_char_string(const uint16_t *in, char *s, size_t len)
} }
free(utf16_data); free(utf16_data);
utf16_data = NULL; utf16_data = NULL;
return ret; return ret;
} }
#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE) #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, static char *mb_to_mb_string_alloc(const char *str,
enum CodePage cp_in, enum CodePage cp_out) enum CodePage cp_in, enum CodePage cp_out)
{ {
@ -333,8 +300,10 @@ static char *mb_to_mb_string_alloc(const char *str,
if (!path_buf_wide_len) if (!path_buf_wide_len)
return strdup(str); return strdup(str);
if ((path_buf_wide = (wchar_t*) path_buf_wide = (wchar_t*)
calloc(path_buf_wide_len + sizeof(wchar_t), sizeof(wchar_t)))) calloc(path_buf_wide_len + sizeof(wchar_t), sizeof(wchar_t));
if (path_buf_wide)
{ {
MultiByteToWideChar(cp_in, 0, MultiByteToWideChar(cp_in, 0,
str, -1, path_buf_wide, path_buf_wide_len); str, -1, path_buf_wide, path_buf_wide_len);
@ -378,49 +347,45 @@ static char *mb_to_mb_string_alloc(const char *str,
} }
#endif #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) char* utf8_to_local_string_alloc(const char *str)
{ {
if (str && *str) if (str && *str)
{
#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE) #if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE)
return mb_to_mb_string_alloc(str, CODEPAGE_UTF8, CODEPAGE_LOCAL); return mb_to_mb_string_alloc(str, CODEPAGE_UTF8, CODEPAGE_LOCAL);
#else #else
return strdup(str); /* Assume string needs no modification if not on Windows */ /* assume string needs no modification if not on Windows */
return strdup(str);
#endif #endif
}
return NULL; return NULL;
} }
/** /* Returned pointer MUST be freed by the caller if non-NULL. */
* local_to_utf8_string_alloc: char* local_to_utf8_string_alloc(const char *str)
*
* @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) #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 #else
return strdup(str); /* Assume string needs no modification if not on Windows */ /* assume string needs no modification if not on Windows */
return strdup(str);
#endif #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) wchar_t* utf8_to_utf16_string_alloc(const char *str)
{ {
#ifdef _WIN32 #ifdef _WIN32
int len = 0; int len = 0;
int out_len = 0;
#else #else
size_t len = 0; size_t len = 0;
size_t out_len = 0;
#endif #endif
wchar_t *buf = NULL; wchar_t *buf = NULL;
@ -428,55 +393,63 @@ wchar_t* utf8_to_utf16_string_alloc(const char *str)
return NULL; return NULL;
#ifdef _WIN32 #ifdef _WIN32
if ((len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0))) len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
if (len)
{ {
if (!(buf = (wchar_t*)calloc(len, sizeof(wchar_t)))) buf = (wchar_t*)calloc(len, sizeof(wchar_t));
if (!buf)
return NULL; return NULL;
if ((MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, len)) < 0) out_len = MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, len);
{
free(buf);
return NULL;
}
} }
else else
{ {
/* Fallback to ANSI codepage instead */ /* fallback to ANSI codepage instead */
if ((len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0))) len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
if (len)
{ {
if (!(buf = (wchar_t*)calloc(len, sizeof(wchar_t)))) buf = (wchar_t*)calloc(len, sizeof(wchar_t));
if (!buf)
return NULL; return NULL;
if ((MultiByteToWideChar(CP_ACP, 0, str, -1, buf, len)) < 0) out_len = MultiByteToWideChar(CP_ACP, 0, str, -1, buf, len);
{
free(buf);
return NULL;
}
} }
} }
if (out_len < 0)
{
free(buf);
return NULL;
}
#else #else
/* NOTE: For now, assume non-Windows platforms' locale is already UTF-8. */ /* NOTE: For now, assume non-Windows platforms' locale is already UTF-8. */
if ((len = mbstowcs(NULL, str, 0) + 1)) len = mbstowcs(NULL, str, 0) + 1;
if (len)
{ {
if (!(buf = (wchar_t*)calloc(len, sizeof(wchar_t)))) buf = (wchar_t*)calloc(len, sizeof(wchar_t));
if (!buf)
return NULL; return NULL;
if ((mbstowcs(buf, str, len)) == (size_t)-1) out_len = mbstowcs(buf, str, len);
{ }
free(buf);
return NULL; if (out_len == (size_t)-1)
} {
free(buf);
return NULL;
} }
#endif #endif
return buf; 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) char* utf16_to_utf8_string_alloc(const wchar_t *str)
{ {
#ifdef _WIN32 #ifdef _WIN32
@ -492,17 +465,20 @@ char* utf16_to_utf8_string_alloc(const wchar_t *str)
#ifdef _WIN32 #ifdef _WIN32
{ {
UINT code_page = CP_UTF8; UINT code_page = CP_UTF8;
len = WideCharToMultiByte(code_page,
0, str, -1, NULL, 0, NULL, NULL);
/* fallback to ANSI codepage instead */ /* fallback to ANSI codepage instead */
if (!(len = WideCharToMultiByte(code_page, if (!len)
0, str, -1, NULL, 0, NULL, NULL)))
{ {
code_page = CP_ACP; code_page = CP_ACP;
len = WideCharToMultiByte(code_page, len = WideCharToMultiByte(code_page,
0, str, -1, NULL, 0, NULL, NULL); 0, str, -1, NULL, 0, NULL, NULL);
} }
if (!(buf = (char*)calloc(len, sizeof(char)))) buf = (char*)calloc(len, sizeof(char));
if (!buf)
return NULL; return NULL;
if (WideCharToMultiByte(code_page, if (WideCharToMultiByte(code_page,
@ -515,9 +491,13 @@ char* utf16_to_utf8_string_alloc(const wchar_t *str)
#else #else
/* NOTE: For now, assume non-Windows platforms' /* NOTE: For now, assume non-Windows platforms'
* locale is already UTF-8. */ * locale is already UTF-8. */
if ((len = wcstombs(NULL, str, 0) + 1)) len = wcstombs(NULL, str, 0) + 1;
if (len)
{ {
if (!(buf = (char*)calloc(len, sizeof(char)))) buf = (char*)calloc(len, sizeof(char));
if (!buf)
return NULL; return NULL;
if (wcstombs(buf, str, len) == (size_t)-1) if (wcstombs(buf, str, len) == (size_t)-1)

File diff suppressed because it is too large Load Diff

View File

@ -24,11 +24,13 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <errno.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <boolean.h> #include <boolean.h>
#include <file/file_path.h> #include <file/file_path.h>
#include <retro_assert.h>
#include <compat/strl.h> #include <compat/strl.h>
#include <compat/posix_string.h> #include <compat/posix_string.h>
#include <retro_miscellaneous.h> #include <retro_miscellaneous.h>
@ -72,7 +74,7 @@ int path_stat(const char *path)
* *
* Checks if path is a directory. * Checks if path is a directory.
* *
* @return true if path is a directory, otherwise false. * Returns: true (1) if path is a directory, otherwise false (0).
*/ */
bool path_is_directory(const char *path) bool path_is_directory(const char *path)
{ {
@ -103,10 +105,8 @@ int32_t path_get_size(const char *path)
* @dir : directory * @dir : directory
* *
* Create directory on filesystem. * Create directory on filesystem.
*
* Recursive function.
* *
* @return true if directory could be created, otherwise false. * Returns: true (1) if directory could be created, otherwise false (0).
**/ **/
bool path_mkdir(const char *dir) bool path_mkdir(const char *dir)
{ {
@ -118,10 +118,12 @@ bool path_mkdir(const char *dir)
/* Use heap. Real chance of stack /* Use heap. Real chance of stack
* overflow if we recurse too hard. */ * overflow if we recurse too hard. */
if (!(basedir = strdup(dir))) basedir = strdup(dir);
return false;
path_parent_dir(basedir, strlen(basedir)); if (!basedir)
return false;
path_parent_dir(basedir);
if (!*basedir || !strcmp(basedir, dir)) if (!*basedir || !strcmp(basedir, dir))
{ {

View File

@ -229,6 +229,18 @@ RHMAP__UNUSED static void* rhmap__grow(void* old_ptr, size_t elem_size, size_t r
return new_vals; return new_vals;
} }
RHMAP__UNUSED static char* rhmap__strdup(const char * str)
{
size_t full_len = strlen(str) + 1;
char *dup_str = (char *)malloc(full_len);
if (!dup_str)
return NULL;
memcpy(dup_str, str, full_len);
return dup_str;
}
RHMAP__UNUSED static ptrdiff_t rhmap__idx(struct rhmap__hdr* hdr, uint32_t key, const char * str, int add, size_t del) RHMAP__UNUSED static ptrdiff_t rhmap__idx(struct rhmap__hdr* hdr, uint32_t key, const char * str, int add, size_t del)
{ {
uint32_t i; uint32_t i;
@ -261,7 +273,7 @@ RHMAP__UNUSED static ptrdiff_t rhmap__idx(struct rhmap__hdr* hdr, uint32_t key,
} }
if (!hdr->keys[i]) if (!hdr->keys[i])
{ {
if (add) { hdr->len++; hdr->keys[i] = key; if (str) hdr->key_strs[i] = strdup(str); return (ptrdiff_t)i; } if (add) { hdr->len++; hdr->keys[i] = key; if (str) hdr->key_strs[i] = rhmap__strdup(str); return (ptrdiff_t)i; }
return (ptrdiff_t)-1; return (ptrdiff_t)-1;
} }
} }

View File

@ -38,99 +38,29 @@ enum CodePage
CODEPAGE_UTF8 = 65001 /* CP_UTF8 */ 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, size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
const char *in, size_t in_size); const char *in, size_t in_size);
/**
* utf16_conv_utf8:
*
* Leaf function.
**/
bool utf16_conv_utf8(uint8_t *out, size_t *out_chars, bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
const uint16_t *in, size_t in_size); const uint16_t *in, size_t in_size);
/**
* utf8len:
*
* Leaf function.
**/
size_t utf8len(const char *string); 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); 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); 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); 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); 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 RETRO_END_DECLS

View File

@ -51,28 +51,6 @@ enum
RARCH_FILE_UNSUPPORTED 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_is_compressed_file:
* @path : path * @path : path
@ -103,12 +81,12 @@ bool path_is_compressed_file(const char *path);
* path_get_archive_delim: * path_get_archive_delim:
* @path : path * @path : path
* *
* Find delimiter of an archive file. Only the first '#' * Gets delimiter of an archive file. Only the first '#'
* after a compression extension is considered. * after a compression extension is considered.
* *
* @return pointer to the delimiter in the path if it contains * Returns: pointer to the delimiter in the path if it contains
* a path inside a compressed file, otherwise NULL. * a compressed file, otherwise NULL.
**/ */
const char *path_get_archive_delim(const char *path); const char *path_get_archive_delim(const char *path);
/** /**
@ -118,28 +96,10 @@ const char *path_get_archive_delim(const char *path);
* Gets extension of file. Only '.'s * Gets extension of file. Only '.'s
* after the last slash are considered. * after the last slash are considered.
* *
* Hidden non-leaf function cost: * Returns: extension part from the path.
* - calls string_is_empty() */
* - calls strrchr
*
* @return extension part from the path.
**/
const char *path_get_extension(const char *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_remove_extension:
* @path : path * @path : path
@ -148,10 +108,7 @@ char *path_get_extension_mutable(const char *path);
* text after and including the last '.'. * text after and including the last '.'.
* Only '.'s after the last slash are considered. * Only '.'s after the last slash are considered.
* *
* Hidden non-leaf function cost: * Returns:
* - calls strrchr
*
* @return
* 1) If path has an extension, returns path with the * 1) If path has an extension, returns path with the
* extension removed. * extension removed.
* 2) If there is no extension, returns NULL. * 2) If there is no extension, returns NULL.
@ -165,26 +122,9 @@ char *path_remove_extension(char *path);
* *
* Get basename from @path. * Get basename from @path.
* *
* Hidden non-leaf function cost: * Returns: basename from path.
* - 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); 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); const char *path_basename_nocompression(const char *path);
/** /**
@ -199,13 +139,12 @@ void path_basedir(char *path);
/** /**
* path_parent_dir: * path_parent_dir:
* @path : path * @path : path
* @len : length of @path
* *
* Extracts parent directory by mutating path. * Extracts parent directory by mutating path.
* Assumes that path is a directory. Keeps trailing '/'. * Assumes that path is a directory. Keeps trailing '/'.
* If the path was already at the root directory, returns empty string * If the path was already at the root directory, returns empty string
**/ **/
void path_parent_dir(char *path, size_t len); void path_parent_dir(char *path);
/** /**
* path_resolve_realpath: * path_resolve_realpath:
@ -217,7 +156,7 @@ void path_parent_dir(char *path, size_t len);
* *
* Relative paths are rebased on the current working dir. * Relative paths are rebased on the current working dir.
* *
* @return @buf if successful, NULL otherwise. * Returns: @buf if successful, NULL otherwise.
* Note: Not implemented on consoles * Note: Not implemented on consoles
* Note: Symlinks are only resolved on Unix-likes * Note: Symlinks are only resolved on Unix-likes
* Note: The current working dir might not be what you expect, * Note: The current working dir might not be what you expect,
@ -239,11 +178,8 @@ char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks);
* Both @path and @base are assumed to be absolute paths without "." or "..". * 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 * 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 path_relative_to(char *out, const char *path, const char *base, size_t size);
size_t size);
/** /**
* path_is_absolute: * path_is_absolute:
@ -251,7 +187,7 @@ size_t path_relative_to(char *out, const char *path, const char *base,
* *
* Checks if @path is an absolute path or a relative path. * Checks if @path is an absolute path or a relative path.
* *
* @return true if path is absolute, false if path is relative. * Returns: true if path is absolute, false if path is relative.
**/ **/
bool path_is_absolute(const char *path); bool path_is_absolute(const char *path);
@ -275,15 +211,8 @@ bool path_is_absolute(const char *path);
* out_path = "/foo/bar/baz/boo.asm" * out_path = "/foo/bar/baz/boo.asm"
* E.g.: in_path = "/foo/bar/baz/boo.c", replace = "" => * E.g.: in_path = "/foo/bar/baz/boo.c", replace = "" =>
* out_path = "/foo/bar/baz/boo" * 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
*/ */
size_t fill_pathname(char *out_path, const char *in_path, void fill_pathname(char *out_path, const char *in_path,
const char *replace, size_t size); const char *replace, size_t size);
/** /**
@ -297,12 +226,6 @@ size_t fill_pathname(char *out_path, const char *in_path,
* *
* E.g.: * E.g.:
* out_filename = "RetroArch-{month}{day}-{Hours}{Minutes}.{@ext}" * 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, size_t fill_dated_filename(char *out_filename,
const char *ext, size_t size); const char *ext, size_t size);
@ -319,33 +242,34 @@ size_t fill_dated_filename(char *out_filename,
* *
* E.g.: * E.g.:
* out_filename = "RetroArch-{year}{month}{day}-{Hour}{Minute}{Second}.{@ext}" * out_filename = "RetroArch-{year}{month}{day}-{Hour}{Minute}{Second}.{@ext}"
*
* Hidden non-leaf function cost:
* - Calls time
* - Calls rtime_localtime()
* - Calls strlcpy 2x
* - Calls string_is_empty()
* - Calls strftime
* - Calls strlcat
*
* @return Length of the string copied into @out_path
**/ **/
size_t fill_str_dated_filename(char *out_filename, void fill_str_dated_filename(char *out_filename,
const char *in_str, const char *ext, size_t size); const char *in_str, const char *ext, size_t size);
/** /**
* find_last_slash: * fill_pathname_noext:
* @str : path * @out_path : output path
* @size : size of path * @in_path : input path
* @replace : what to replace
* @size : buffer size of output path
* *
* Find last slash in path. Tries to find * Appends a filename extension 'replace' to 'in_path', and outputs
* a backslash on Windows too which takes precedence * result in 'out_path'.
* over regular slash. *
* 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);
* Hidden non-leaf function cost: /**
* - calls strrchr * find_last_slash:
* @str : input path
* *
* @return pointer to last slash/backslash found in @str. * Gets a pointer to the last slash in the input path.
*
* Returns: a pointer to the last slash in the input path.
**/ **/
char *find_last_slash(const char *str); char *find_last_slash(const char *str);
@ -365,11 +289,6 @@ char *find_last_slash(const char *str);
* *
* E.g..: in_dir = "/tmp/some_dir", in_basename = "/some_content/foo.c", * E.g..: in_dir = "/tmp/some_dir", in_basename = "/some_content/foo.c",
* replace = ".asm" => in_dir = "/tmp/some_dir/foo.c.asm" * replace = ".asm" => in_dir = "/tmp/some_dir/foo.c.asm"
*
* Hidden non-leaf function cost:
* - Calls fill_pathname_slash()
* - Calls path_basename()
* - Calls strlcpy 2x
**/ **/
size_t fill_pathname_dir(char *in_dir, const char *in_basename, size_t fill_pathname_dir(char *in_dir, const char *in_basename,
const char *replace, size_t size); const char *replace, size_t size);
@ -381,15 +300,16 @@ size_t fill_pathname_dir(char *in_dir, const char *in_basename,
* @size : size of output path * @size : size of output path
* *
* Copies basename of @in_path into @out_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); 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: * fill_pathname_basedir:
* @out_dir : output directory * @out_dir : output directory
@ -399,13 +319,12 @@ size_t fill_pathname_base(char *out_path, const char *in_path, size_t size);
* Copies base directory of @in_path into @out_path. * Copies base directory of @in_path into @out_path.
* If in_path is a path without any slashes (relative current directory), * If in_path is a path without any slashes (relative current directory),
* @out_path will get path "./". * @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(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: * fill_pathname_parent_dir_name:
* @out_dir : output directory * @out_dir : output directory
@ -414,13 +333,7 @@ void fill_pathname_basedir(char *out_path, const char *in_path, size_t size);
* *
* Copies only the parent directory name of @in_dir into @out_dir. * Copies only the parent directory name of @in_dir into @out_dir.
* The two buffers must not overlap. Removes trailing '/'. * 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, bool fill_pathname_parent_dir_name(char *out_dir,
const char *in_dir, size_t size); const char *in_dir, size_t size);
@ -434,11 +347,6 @@ bool fill_pathname_parent_dir_name(char *out_dir,
* Copies parent directory of @in_dir into @out_dir. * Copies parent directory of @in_dir into @out_dir.
* Assumes @in_dir is a directory. Keeps trailing '/'. * Assumes @in_dir is a directory. Keeps trailing '/'.
* If the path was already at the root directory, @out_dir will be an empty string. * 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, void fill_pathname_parent_dir(char *out_dir,
const char *in_dir, size_t size); const char *in_dir, size_t size);
@ -466,51 +374,30 @@ void fill_pathname_resolve_relative(char *out_path, const char *in_refpath,
* @size : size of output path * @size : size of output path
* *
* Joins a directory (@dir) and path (@path) together. * 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. * between directory and path.
*
* Hidden non-leaf function cost:
* - calls strlcpy at least once
* - calls fill_pathname_slash()
*
* 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, size_t fill_pathname_join(char *out_path, const char *dir,
const char *path, size_t size); 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 2x
* - calls find_last_slash()
*
* @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, size_t fill_pathname_join_special_ext(char *out_path,
const char *dir, const char *path, const char *dir, const char *path,
const char *last, const char *ext, const char *last, const char *ext,
size_t size); 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: * fill_pathname_join_delim:
* @out_path : output path * @out_path : output path
@ -521,57 +408,45 @@ size_t fill_pathname_join_special_ext(char *out_path,
* *
* Joins a directory (@dir) and path (@path) together * Joins a directory (@dir) and path (@path) together
* using the given delimiter (@delim). * 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, size_t fill_pathname_join_delim(char *out_path, const char *dir,
const char *path, const char delim, size_t size); const char *path, const char delim, size_t size);
size_t fill_pathname_expand_special(char *out_path, size_t fill_pathname_join_delim_concat(char *out_path, const char *dir,
const char *in_path, size_t size); const char *path, const char delim, const char *concat,
size_t size);
size_t fill_pathname_abbreviate_special(char *out_path,
const char *in_path, size_t size);
/** /**
* fill_pathname_abbreviated_or_relative: * fill_short_pathname_representation:
* @out_rep : output representation
* @in_path : input path
* @size : size of output representation
* *
* Fills the supplied path with either the abbreviated path or * Generates a short representation of path. It should only
* the relative path, which ever one has less depth / number of slashes * 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)
* If lengths of abbreviated and relative paths are the same, * In case of more complex URLs, this should cut everything except for
* the relative path will be used * the main image file.
* @in_path can be an absolute, relative or abbreviated path
* *
* @return Length of the string copied into @out_path * E.g.: "/path/to/game.img" -> game.img
**/ * "/path/to/myarchive.7z#folder/to/game.img" -> game.img
size_t fill_pathname_abbreviated_or_relative(char *out_path, */
const char *in_refpath, const char *in_path, size_t size); size_t fill_short_pathname_representation(char* out_rep,
const char *in_path, size_t size);
void fill_short_pathname_representation_noext(char* out_rep,
const char *in_path, size_t size);
void fill_pathname_expand_special(char *out_path,
const char *in_path, size_t size);
void fill_pathname_abbreviate_special(char *out_path,
const char *in_path, size_t size);
void fill_pathname_abbreviated_or_relative(char *out_path, const char *in_refpath, 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 pathname_conform_slashes_to_os(char *path);
/**
* 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); void pathname_make_slashes_portable(char *path);
/** /**
@ -589,8 +464,8 @@ void path_basedir_wrapper(char *path);
* *
* Checks if character (@c) is a slash. * Checks if character (@c) is a slash.
* *
* @return true if character is a slash, otherwise false. * Returns: true (1) if character is a slash, otherwise false (0).
**/ */
#ifdef _WIN32 #ifdef _WIN32
#define PATH_CHAR_IS_SLASH(c) (((c) == '/') || ((c) == '\\')) #define PATH_CHAR_IS_SLASH(c) (((c) == '/') || ((c) == '\\'))
#else #else
@ -602,8 +477,8 @@ void path_basedir_wrapper(char *path);
* *
* Gets the default slash separator. * Gets the default slash separator.
* *
* @return default slash separator. * Returns: default slash separator.
**/ */
#ifdef _WIN32 #ifdef _WIN32
#define PATH_DEFAULT_SLASH() "\\" #define PATH_DEFAULT_SLASH() "\\"
#define PATH_DEFAULT_SLASH_C() '\\' #define PATH_DEFAULT_SLASH_C() '\\'
@ -619,13 +494,8 @@ void path_basedir_wrapper(char *path);
* *
* Assumes path is a directory. Appends a slash * Assumes path is a directory. Appends a slash
* if not already there. * if not already there.
* Hidden non-leaf function cost:
* - calls find_last_slash()
* - can call strlcat once if it returns false
* - calls strlen
**/ **/
size_t fill_pathname_slash(char *path, size_t size); void fill_pathname_slash(char *path, size_t size);
#if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL) #if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
void fill_pathname_application_path(char *buf, size_t size); void fill_pathname_application_path(char *buf, size_t size);
@ -639,16 +509,7 @@ void fill_pathname_home_dir(char *buf, size_t size);
* *
* Create directory on filesystem. * Create directory on filesystem.
* *
* Recursive function. * Returns: true (1) if directory could be created, otherwise false (0).
*
* 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); bool path_mkdir(const char *dir);
@ -658,15 +519,10 @@ bool path_mkdir(const char *dir);
* *
* Checks if path is a directory. * Checks if path is a directory.
* *
* @return true if path is a directory, otherwise false. * Returns: true (1) if path is a directory, otherwise false (0).
*/ */
bool path_is_directory(const char *path); bool path_is_directory(const char *path);
/* Time format strings with AM-PM designation require special
* handling due to platform dependence */
void strftime_am_pm(char *s, size_t len, const char* format,
const void* timeptr);
bool path_is_character_special(const char *path); bool path_is_character_special(const char *path);
int path_stat(const char *path); int path_stat(const char *path);

View File

@ -291,7 +291,6 @@ enum retro_language
RETRO_LANGUAGE_CATALAN = 29, RETRO_LANGUAGE_CATALAN = 29,
RETRO_LANGUAGE_BRITISH_ENGLISH = 30, RETRO_LANGUAGE_BRITISH_ENGLISH = 30,
RETRO_LANGUAGE_HUNGARIAN = 31, RETRO_LANGUAGE_HUNGARIAN = 31,
RETRO_LANGUAGE_BELARUSIAN = 32,
RETRO_LANGUAGE_LAST, RETRO_LANGUAGE_LAST,
/* Ensure sizeof(enum) == sizeof(int) */ /* Ensure sizeof(enum) == sizeof(int) */
@ -929,6 +928,8 @@ enum retro_mod
* anything else. * anything else.
* It is recommended to expose all relevant pointers through * It is recommended to expose all relevant pointers through
* retro_get_memory_* as well. * retro_get_memory_* as well.
*
* Can be called from retro_init and retro_load_game.
*/ */
#define RETRO_ENVIRONMENT_SET_GEOMETRY 37 #define RETRO_ENVIRONMENT_SET_GEOMETRY 37
/* const struct retro_game_geometry * -- /* const struct retro_game_geometry * --
@ -1766,90 +1767,6 @@ enum retro_mod
* (see enum retro_savestate_context) * (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.
*/
#define RETRO_ENVIRONMENT_GET_JIT_CAPABLE 74
/* bool * --
* Result is set to true if the frontend has already verified JIT can be
* used, mainly for use iOS/tvOS. On other platforms the result is true.
*/
#define RETRO_ENVIRONMENT_GET_MICROPHONE_INTERFACE (75 | RETRO_ENVIRONMENT_EXPERIMENTAL)
/* struct retro_microphone_interface * --
* Returns an interface that can be used to receive input from the microphone driver.
*
* Returns true if microphone support is available,
* even if no microphones are plugged in.
* Returns false if mic support is disabled or unavailable.
*
* This callback can be invoked at any time,
* even before the microphone driver is ready.
*/
#define RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE 76
/* const struct retro_netpacket_callback * --
* When set, a core gains control over network packets sent and
* received during a multiplayer session. This can be used to
* emulate multiplayer games that were originally played on two
* or more separate consoles or computers connected together.
*
* The frontend will take care of connecting players together,
* and the core only needs to send the actual data as needed for
* the emulation, while handshake and connection management happen
* in the background.
*
* When two or more players are connected and this interface has
* been set, time manipulation features (such as pausing, slow motion,
* fast forward, rewinding, save state loading, etc.) are disabled to
* avoid interrupting communication.
*
* Should be set in either retro_init or retro_load_game, but not both.
*
* When not set, a frontend may use state serialization-based
* multiplayer, where a deterministic core supporting multiple
* input devices does not need to take any action on its own.
*/
#define RETRO_ENVIRONMENT_GET_DEVICE_POWER (77 | RETRO_ENVIRONMENT_EXPERIMENTAL)
/* struct retro_device_power * --
* Returns the device's current power state as reported by the frontend.
* This is useful for emulating the battery level in handheld consoles,
* or for reducing power consumption when on battery power.
*
* The return value indicates whether the frontend can provide this information,
* even if the parameter is NULL.
*
* If the frontend does not support this functionality,
* then the provided argument will remain unchanged.
*
* Note that this environment call describes the power state for the entire device,
* not for individual peripherals like controllers.
*/
/* VFS functionality */ /* VFS functionality */
/* File paths: /* File paths:
@ -2024,13 +1941,13 @@ struct retro_vfs_interface_info
enum retro_hw_render_interface_type enum retro_hw_render_interface_type
{ {
RETRO_HW_RENDER_INTERFACE_VULKAN = 0, RETRO_HW_RENDER_INTERFACE_VULKAN = 0,
RETRO_HW_RENDER_INTERFACE_D3D9 = 1, RETRO_HW_RENDER_INTERFACE_D3D9 = 1,
RETRO_HW_RENDER_INTERFACE_D3D10 = 2, RETRO_HW_RENDER_INTERFACE_D3D10 = 2,
RETRO_HW_RENDER_INTERFACE_D3D11 = 3, RETRO_HW_RENDER_INTERFACE_D3D11 = 3,
RETRO_HW_RENDER_INTERFACE_D3D12 = 4, RETRO_HW_RENDER_INTERFACE_D3D12 = 4,
RETRO_HW_RENDER_INTERFACE_GSKIT_PS2 = 5, RETRO_HW_RENDER_INTERFACE_GSKIT_PS2 = 5,
RETRO_HW_RENDER_INTERFACE_DUMMY = INT_MAX RETRO_HW_RENDER_INTERFACE_DUMMY = INT_MAX
}; };
/* Base struct. All retro_hw_render_interface_* types /* Base struct. All retro_hw_render_interface_* types
@ -2806,17 +2723,9 @@ enum retro_hw_context_type
/* Vulkan, see RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE. */ /* Vulkan, see RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE. */
RETRO_HW_CONTEXT_VULKAN = 6, RETRO_HW_CONTEXT_VULKAN = 6,
/* Direct3D11, see RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE */ /* Direct3D, set version_major to select the type of interface
RETRO_HW_CONTEXT_D3D11 = 7, * returned by RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE */
RETRO_HW_CONTEXT_DIRECT3D = 7,
/* Direct3D10, see RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE */
RETRO_HW_CONTEXT_D3D10 = 8,
/* Direct3D12, see RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE */
RETRO_HW_CONTEXT_D3D12 = 9,
/* Direct3D9, see RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE */
RETRO_HW_CONTEXT_D3D9 = 10,
RETRO_HW_CONTEXT_DUMMY = INT_MAX RETRO_HW_CONTEXT_DUMMY = INT_MAX
}; };
@ -3071,100 +2980,6 @@ struct retro_disk_control_ext_callback
retro_get_image_label_t get_image_label; /* Optional - may be NULL */ retro_get_image_label_t get_image_label; /* Optional - may be NULL */
}; };
/* Definitions for RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE.
* A core can set it if sending and receiving custom network packets
* during a multiplayer session is desired.
*/
/* Netpacket flags for retro_netpacket_send_t */
#define RETRO_NETPACKET_UNRELIABLE 0 /* Packet to be sent unreliable, depending on network quality it might not arrive. */
#define RETRO_NETPACKET_RELIABLE (1 << 0) /* Reliable packets are guaranteed to arrive at the target in the order they were send. */
#define RETRO_NETPACKET_UNSEQUENCED (1 << 1) /* Packet will not be sequenced with other packets and may arrive out of order. Cannot be set on reliable packets. */
/* Used by the core to send a packet to one or more connected players.
* A single packet sent via this interface can contain up to 64 KB of data.
*
* The broadcast flag can be set to true to send to multiple connected clients.
* In a broadcast, the client_id argument indicates 1 client NOT to send the
* packet to (pass 0xFFFF to send to everyone). Otherwise, the client_id
* argument indicates a single client to send the packet to.
*
* A frontend must support sending reliable packets (RETRO_NETPACKET_RELIABLE).
* Unreliable packets might not be supported by the frontend, but the flags can
* still be specified. Reliable transmission will be used instead.
*
* If this function is called passing NULL for buf, it will instead flush all
* previously buffered outgoing packets and instantly read any incoming packets.
* During such a call, retro_netpacket_receive_t and retro_netpacket_stop_t can
* be called. The core can perform this in a loop to do a blocking read, i.e.,
* wait for incoming data, but needs to handle stop getting called and also
* give up after a short while to avoid freezing on a connection problem.
*
* This function is not guaranteed to be thread-safe and must be called during
* retro_run or any of the netpacket callbacks passed with this interface.
*/
typedef void (RETRO_CALLCONV *retro_netpacket_send_t)(int flags, const void* buf, size_t len, uint16_t client_id, bool broadcast);
/* Called by the frontend to signify that a multiplayer session has started.
* If client_id is 0 the local player is the host of the session and at this
* point no other player has connected yet.
*
* If client_id is > 0 the local player is a client connected to a host and
* at this point is already fully connected to the host.
*
* The core must store the retro_netpacket_send_t function pointer provided
* here and use it whenever it wants to send a packet. This function pointer
* remains valid until the frontend calls retro_netpacket_stop_t.
*/
typedef void (RETRO_CALLCONV *retro_netpacket_start_t)(uint16_t client_id, retro_netpacket_send_t send_fn);
/* Called by the frontend when a new packet arrives which has been sent from
* another player with retro_netpacket_send_t. The client_id argument indicates
* who has sent the packet.
*/
typedef void (RETRO_CALLCONV *retro_netpacket_receive_t)(const void* buf, size_t len, uint16_t client_id);
/* Called by the frontend when the multiplayer session has ended.
* Once this gets called the retro_netpacket_send_t function pointer passed
* to retro_netpacket_start_t will not be valid anymore.
*/
typedef void (RETRO_CALLCONV *retro_netpacket_stop_t)(void);
/* Called by the frontend every frame (between calls to retro_run while
* updating the state of the multiplayer session.
* This is a good place for the core to call retro_netpacket_send_t from.
*/
typedef void (RETRO_CALLCONV *retro_netpacket_poll_t)(void);
/* Called by the frontend when a new player connects to the hosted session.
* This is only called on the host side, not for clients connected to the host.
* If this function returns false, the newly connected player gets dropped.
* This can be used for example to limit the number of players.
*/
typedef bool (RETRO_CALLCONV *retro_netpacket_connected_t)(uint16_t client_id);
/* Called by the frontend when a player leaves or disconnects from the hosted session.
* This is only called on the host side, not for clients connected to the host.
*/
typedef void (RETRO_CALLCONV *retro_netpacket_disconnected_t)(uint16_t client_id);
/**
* A callback interface for giving a core the ability to send and receive custom
* network packets during a multiplayer session between two or more instances
* of a libretro frontend.
*
* @see RETRO_ENVIRONMENT_SET_NETPACKET_INTERFACE
*/
struct retro_netpacket_callback
{
retro_netpacket_start_t start;
retro_netpacket_receive_t receive;
retro_netpacket_stop_t stop; /* Optional - may be NULL */
retro_netpacket_poll_t poll; /* Optional - may be NULL */
retro_netpacket_connected_t connected; /* Optional - may be NULL */
retro_netpacket_disconnected_t disconnected; /* Optional - may be NULL */
};
enum retro_pixel_format enum retro_pixel_format
{ {
/* 0RGB1555, native endian. /* 0RGB1555, native endian.
@ -3967,289 +3782,6 @@ struct retro_throttle_state
float rate; float rate;
}; };
/**
* Opaque handle to a microphone that's been opened for use.
* The underlying object is accessed or created with \c retro_microphone_interface_t.
*/
typedef struct retro_microphone retro_microphone_t;
/**
* Parameters for configuring a microphone.
* Some of these might not be honored,
* depending on the available hardware and driver configuration.
*/
typedef struct retro_microphone_params
{
/**
* The desired sample rate of the microphone's input, in Hz.
* The microphone's input will be resampled,
* so cores can ask for whichever frequency they need.
*
* If zero, some reasonable default will be provided by the frontend
* (usually from its config file).
*
* @see retro_get_mic_rate_t
*/
unsigned rate;
} retro_microphone_params_t;
/**
* @copydoc retro_microphone_interface::open_mic
*/
typedef retro_microphone_t *(RETRO_CALLCONV *retro_open_mic_t)(const retro_microphone_params_t *params);
/**
* @copydoc retro_microphone_interface::close_mic
*/
typedef void (RETRO_CALLCONV *retro_close_mic_t)(retro_microphone_t *microphone);
/**
* @copydoc retro_microphone_interface::get_params
*/
typedef bool (RETRO_CALLCONV *retro_get_mic_params_t)(const retro_microphone_t *microphone, retro_microphone_params_t *params);
/**
* @copydoc retro_microphone_interface::set_mic_state
*/
typedef bool (RETRO_CALLCONV *retro_set_mic_state_t)(retro_microphone_t *microphone, bool state);
/**
* @copydoc retro_microphone_interface::get_mic_state
*/
typedef bool (RETRO_CALLCONV *retro_get_mic_state_t)(const retro_microphone_t *microphone);
/**
* @copydoc retro_microphone_interface::read_mic
*/
typedef int (RETRO_CALLCONV *retro_read_mic_t)(retro_microphone_t *microphone, int16_t* samples, size_t num_samples);
/**
* The current version of the microphone interface.
* Will be incremented whenever \c retro_microphone_interface or \c retro_microphone_params_t
* receive new fields.
*
* Frontends using cores built against older mic interface versions
* should not access fields introduced in newer versions.
*/
#define RETRO_MICROPHONE_INTERFACE_VERSION 1
/**
* An interface for querying the microphone and accessing data read from it.
*
* @see RETRO_ENVIRONMENT_GET_MICROPHONE_INTERFACE
*/
struct retro_microphone_interface
{
/**
* The version of this microphone interface.
* Set by the core to request a particular version,
* and set by the frontend to indicate the returned version.
* 0 indicates that the interface is invalid or uninitialized.
*/
unsigned interface_version;
/**
* Initializes a new microphone.
* Assuming that microphone support is enabled and provided by the frontend,
* cores may call this function whenever necessary.
* A microphone could be opened throughout a core's lifetime,
* or it could wait until a microphone is plugged in to the emulated device.
*
* The returned handle will be valid until it's freed,
* even if the audio driver is reinitialized.
*
* This function is not guaranteed to be thread-safe.
*
* @param args[in] Parameters used to create the microphone.
* May be \c NULL, in which case the default value of each parameter will be used.
*
* @returns Pointer to the newly-opened microphone,
* or \c NULL if one couldn't be opened.
* This likely means that no microphone is plugged in and recognized,
* or the maximum number of supported microphones has been reached.
*
* @note Microphones are \em inactive by default;
* to begin capturing audio, call \c set_mic_state.
* @see retro_microphone_params_t
*/
retro_open_mic_t open_mic;
/**
* Closes a microphone that was initialized with \c open_mic.
* Calling this function will stop all microphone activity
* and free up the resources that it allocated.
* Afterwards, the handle is invalid and must not be used.
*
* A frontend may close opened microphones when unloading content,
* but this behavior is not guaranteed.
* Cores should close their microphones when exiting, just to be safe.
*
* @param microphone Pointer to the microphone that was allocated by \c open_mic.
* If \c NULL, this function does nothing.
*
* @note The handle might be reused if another microphone is opened later.
*/
retro_close_mic_t close_mic;
/**
* Returns the configured parameters of this microphone.
* These may differ from what was requested depending on
* the driver and device configuration.
*
* Cores should check these values before they start fetching samples.
*
* Will not change after the mic was opened.
*
* @param microphone[in] Opaque handle to the microphone
* whose parameters will be retrieved.
* @param params[out] The parameters object that the
* microphone's parameters will be copied to.
*
* @return \c true if the parameters were retrieved,
* \c false if there was an error.
*/
retro_get_mic_params_t get_params;
/**
* Enables or disables the given microphone.
* Microphones are disabled by default
* and must be explicitly enabled before they can be used.
* Disabled microphones will not process incoming audio samples,
* and will therefore have minimal impact on overall performance.
* Cores may enable microphones throughout their lifetime,
* or only for periods where they're needed.
*
* Cores that accept microphone input should be able to operate without it;
* we suggest substituting silence in this case.
*
* @param microphone Opaque handle to the microphone
* whose state will be adjusted.
* This will have been provided by \c open_mic.
* @param state \c true if the microphone should receive audio input,
* \c false if it should be idle.
* @returns \c true if the microphone's state was successfully set,
* \c false if \c microphone is invalid
* or if there was an error.
*/
retro_set_mic_state_t set_mic_state;
/**
* Queries the active state of a microphone at the given index.
* Will return whether the microphone is enabled,
* even if the driver is paused.
*
* @param microphone Opaque handle to the microphone
* whose state will be queried.
* @return \c true if the provided \c microphone is valid and active,
* \c false if not or if there was an error.
*/
retro_get_mic_state_t get_mic_state;
/**
* Retrieves the input processed by the microphone since the last call.
* \em Must be called every frame unless \c microphone is disabled,
* similar to how \c retro_audio_sample_batch_t works.
*
* @param[in] microphone Opaque handle to the microphone
* whose recent input will be retrieved.
* @param[out] samples The buffer that will be used to store the microphone's data.
* Microphone input is in mono (i.e. one number per sample).
* Should be large enough to accommodate the expected number of samples per frame;
* for example, a 44.1kHz sample rate at 60 FPS would require space for 735 samples.
* @param[in] num_samples The size of the data buffer in samples (\em not bytes).
* Microphone input is in mono, so a "frame" and a "sample" are equivalent in length here.
*
* @return The number of samples that were copied into \c samples.
* If \c microphone is pending driver initialization,
* this function will copy silence of the requested length into \c samples.
*
* Will return -1 if the microphone is disabled,
* the audio driver is paused,
* or there was an error.
*/
retro_read_mic_t read_mic;
};
/**
* Describes how a device is being powered.
* @see RETRO_ENVIRONMENT_GET_DEVICE_POWER
*/
enum retro_power_state
{
/**
* Indicates that the frontend cannot report its power state at this time,
* most likely due to a lack of support.
*
* \c RETRO_ENVIRONMENT_GET_DEVICE_POWER will not return this value;
* instead, the environment callback will return \c false.
*/
RETRO_POWERSTATE_UNKNOWN = 0,
/**
* Indicates that the device is running on its battery.
* Usually applies to portable devices such as handhelds, laptops, and smartphones.
*/
RETRO_POWERSTATE_DISCHARGING,
/**
* Indicates that the device's battery is currently charging.
*/
RETRO_POWERSTATE_CHARGING,
/**
* Indicates that the device is connected to a power source
* and that its battery has finished charging.
*/
RETRO_POWERSTATE_CHARGED,
/**
* Indicates that the device is connected to a power source
* and that it does not have a battery.
* This usually suggests a desktop computer or a non-portable game console.
*/
RETRO_POWERSTATE_PLUGGED_IN
};
/**
* Indicates that an estimate is not available for the battery level or time remaining,
* even if the actual power state is known.
*/
#define RETRO_POWERSTATE_NO_ESTIMATE (-1)
/**
* Describes the power state of the device running the frontend.
* @see RETRO_ENVIRONMENT_GET_DEVICE_POWER
*/
struct retro_device_power
{
/**
* The current state of the frontend's power usage.
*/
enum retro_power_state state;
/**
* A rough estimate of the amount of time remaining (in seconds)
* before the device powers off.
* This value depends on a variety of factors,
* so it is not guaranteed to be accurate.
*
* Will be set to \c RETRO_POWERSTATE_NO_ESTIMATE if \c state does not equal \c RETRO_POWERSTATE_DISCHARGING.
* May still be set to \c RETRO_POWERSTATE_NO_ESTIMATE if the frontend is unable to provide an estimate.
*/
int seconds;
/**
* The approximate percentage of battery charge,
* ranging from 0 to 100 (inclusive).
* The device may power off before this reaches 0.
*
* The user might have configured their device
* to stop charging before the battery is full,
* so do not assume that this will be 100 in the \c RETRO_POWERSTATE_CHARGED state.
*/
int8_t percent;
};
/* Callbacks */ /* Callbacks */
/* Environment callback. Gives implementations a way of performing /* Environment callback. Gives implementations a way of performing

View File

@ -101,26 +101,6 @@ typedef int ssize_t;
#define STRING_REP_UINT64 "%" PRIu64 #define STRING_REP_UINT64 "%" PRIu64
#define STRING_REP_USIZE "%" PRIuPTR #define STRING_REP_USIZE "%" PRIuPTR
/* Wrap a declaration in RETRO_DEPRECATED() to produce a compiler warning when
it's used. This is intended for developer machines, so it won't work on ancient
or obscure compilers */
#if defined(_MSC_VER)
#if _MSC_VER >= 1400 /* Visual C 2005 or later */
#define RETRO_DEPRECATED(decl) __declspec(deprecated) decl
#endif
#elif defined(__GNUC__)
#if __GNUC__ >= 3 /* GCC 3 or later */
#define RETRO_DEPRECATED(decl) decl __attribute__((deprecated))
#endif
#elif defined(__clang__)
#if __clang_major__ >= 3 /* clang 3 or later */
#define RETRO_DEPRECATED(decl) decl __attribute__((deprecated))
#endif
#endif
#ifndef RETRO_DEPRECATED /* Unsupported compilers */
#define RETRO_DEPRECATED(decl) decl
#endif
/* /*
I would like to see retro_inline.h moved in here; possibly boolean too. I would like to see retro_inline.h moved in here; possibly boolean too.

View File

@ -49,10 +49,6 @@
#include <compat/msvc.h> #include <compat/msvc.h>
#endif #endif
#ifdef IOS
#include <sys/param.h>
#endif
static INLINE void bits_or_bits(uint32_t *a, uint32_t *b, uint32_t count) static INLINE void bits_or_bits(uint32_t *a, uint32_t *b, uint32_t count)
{ {
uint32_t i; uint32_t i;
@ -78,29 +74,14 @@ static INLINE bool bits_any_set(uint32_t* ptr, uint32_t count)
return false; 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 #ifndef PATH_MAX_LENGTH
#if defined(_XBOX1) || defined(_3DS) || defined(PSP) || defined(PS2) || defined(GEKKO)|| defined(WIIU) || defined(__PSL1GHT__) || defined(__PS3__) #if defined(_XBOX1) || defined(_3DS) || defined(PSP) || defined(PS2) || defined(GEKKO)|| defined(WIIU) || defined(ORBIS) || defined(__PSL1GHT__) || defined(__PS3__)
#define PATH_MAX_LENGTH 512 #define PATH_MAX_LENGTH 512
#else #else
#define PATH_MAX_LENGTH 4096 #define PATH_MAX_LENGTH 4096
#endif #endif
#endif #endif
#ifndef NAME_MAX_LENGTH
#define NAME_MAX_LENGTH 256
#endif
#ifndef MAX #ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif #endif

View File

@ -59,7 +59,7 @@ int64_t filestream_truncate(RFILE *stream, int64_t length);
* @bufsize : optional buffer size (-1 or 0 to use default) * @bufsize : optional buffer size (-1 or 0 to use default)
* *
* Opens a file for reading or writing, depending on the requested mode. * Opens a file for reading or writing, depending on the requested mode.
* @return A pointer to an RFILE if opened successfully, otherwise NULL. * Returns a pointer to an RFILE if opened successfully, otherwise NULL.
**/ **/
RFILE* filestream_open(const char *path, unsigned mode, unsigned hints); RFILE* filestream_open(const char *path, unsigned mode, unsigned hints);
@ -75,17 +75,6 @@ void filestream_rewind(RFILE *stream);
int filestream_close(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); int64_t filestream_read_file(const char *path, void **buf, int64_t *len);
char* filestream_gets(RFILE *stream, char *s, size_t len); char* filestream_gets(RFILE *stream, char *s, size_t len);
@ -98,16 +87,6 @@ int filestream_scanf(RFILE *stream, const char* format, ...);
int filestream_eof(RFILE *stream); 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); bool filestream_write_file(const char *path, const void *data, int64_t size);
int filestream_putc(RFILE *stream, int c); int filestream_putc(RFILE *stream, int c);
@ -128,11 +107,7 @@ const char* filestream_get_path(RFILE *stream);
bool filestream_exists(const char *path); 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); char* filestream_getline(RFILE *stream);
libretro_vfs_implementation_file* filestream_get_vfs_handle(RFILE *stream); libretro_vfs_implementation_file* filestream_get_vfs_handle(RFILE *stream);

View File

@ -48,7 +48,7 @@ RETRO_BEGIN_DECLS
#define TOUPPER(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 */ /* C standard says \f \v are space, but this one disagrees */
#define ISSPACE(c) (lr_char_props[(unsigned char)(c)] & 0x80) #define ISSPACE(c) (lr_char_props[(unsigned char)(c)] & 0x80)
#define ISDIGIT(c) (lr_char_props[(unsigned char)(c)] & 0x40) #define ISDIGIT(c) (lr_char_props[(unsigned char)(c)] & 0x40)
#define ISALPHA(c) (lr_char_props[(unsigned char)(c)] & 0x20) #define ISALPHA(c) (lr_char_props[(unsigned char)(c)] & 0x20)
@ -92,20 +92,16 @@ 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) static INLINE bool string_ends_with(const char *str, const char *suffix)
{ {
return str && suffix && string_ends_with_size(str, suffix, strlen(str), strlen(suffix)); if (!str || !suffix)
return false;
return 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 * checks the first 'size' characters
* - If 'str' is NULL, returns 0 * - If 'str' is NULL, returns 0
* - If 'str' is not NULL and no '\0' character is found * - 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) static INLINE size_t strlen_size(const char *str, size_t size)
{ {
size_t i = 0; size_t i = 0;
@ -134,68 +130,25 @@ static INLINE bool string_is_equal_case_insensitive(const char *a,
return (result == 0); 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_upper(char *s);
char *string_to_lower(char *s); char *string_to_lower(char *s);
char *string_ucwords(char *s); char *string_ucwords(char *s);
char *string_replace_substring(const char *in, char *string_replace_substring(const char *in, const char *pattern,
const char *pattern, size_t pattern_len, const char *by);
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); 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); 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); char *string_trim_whitespace(char *const s);
/** /*
* 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 * Wraps string specified by 'src' to destination buffer
* specified by 'dst' and 'dst_size'. * specified by 'dst' and 'dst_size'.
* This function assumes that all glyphs in the string * This function assumes that all glyphs in the string
@ -203,57 +156,58 @@ char *string_trim_whitespace(char *const s);
* regular Latin characters - i.e. it will not wrap * regular Latin characters - i.e. it will not wrap
* correctly any text containing so-called 'wide' Unicode * correctly any text containing so-called 'wide' Unicode
* characters (e.g. CJK languages, emojis, etc.). * characters (e.g. CJK languages, emojis, etc.).
**/ *
size_t word_wrap(char *dst, size_t dst_size, const char *src, size_t src_len, * @param dst pointer to destination buffer.
* @param dst_size size of destination buffer.
* @param src pointer to input string.
* @param line_width max number of characters per line.
* @param wideglyph_width not used, but is necessary to keep
* compatibility with word_wrap_wideglyph().
* @param max_lines max lines of destination string.
* 0 means no limit.
*/
void word_wrap(char *dst, size_t dst_size, const char *src,
int line_width, int wideglyph_width, unsigned max_lines); int line_width, int wideglyph_width, unsigned max_lines);
/** /*
* word_wrap_wideglyph: * Wraps string specified by 'src' to destination buffer
* @dst : pointer to destination buffer. * specified by 'dst' and 'dst_size'.
* @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 * This function assumes that all glyphs in the string
* are: * are:
* - EITHER 'non-wide' Unicode glyphs, with an on-screen * - EITHER 'non-wide' Unicode glyphs, with an on-screen
* pixel width similar to that of regular Latin characters * pixel width similar to that of regular Latin characters
* - OR 'wide' Unicode glyphs (e.g. CJK languages, emojis, etc.) * - OR 'wide' Unicode glyphs (e.g. CJK languages, emojis, etc.)
* with an on-screen pixel width defined by @wideglyph_width * with an on-screen pixel width defined by 'wideglyph_width'
* Note that wrapping may occur in inappropriate locations * Note that wrapping may occur in inappropriate locations
* if @src string contains 'wide' Unicode characters whose * if 'src' string contains 'wide' Unicode characters whose
* on-screen pixel width deviates greatly from the set * on-screen pixel width deviates greatly from the set
* @wideglyph_width value. * 'wideglyph_width' value.
**/
size_t 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 * @param dst pointer to destination buffer.
* @param dst_size size of destination buffer.
* @param src pointer to input string.
* @param line_width max number of characters per line.
* @param wideglyph_width effective width of 'wide' Unicode glyphs.
* the value here is normalised relative to the
* typical on-screen pixel width of a regular
* Latin character:
* - a regular Latin character is defined to
* have an effective width of 100
* - wideglyph_width = 100 * (wide_character_pixel_width / latin_character_pixel_width)
* - e.g. if 'wide' Unicode characters in 'src'
* have an on-screen pixel width twice that of
* regular Latin characters, wideglyph_width
* would be 200
* @param max_lines max lines of destination string.
* 0 means no limit.
*/
void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src,
int line_width, int wideglyph_width, unsigned max_lines);
/* Splits string into tokens seperated by 'delim'
* > Returned token string must be free()'d * > Returned token string must be free()'d
* > Returns NULL if token is not found * > 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 * last found token
* > Tokens *include* empty strings * > Tokens *include* empty strings
* Usage example: * Usage example:
@ -266,120 +220,29 @@ size_t word_wrap_wideglyph(
* free(token); * free(token);
* token = NULL; * token = NULL;
* } * }
**/ */
char* string_tokenize(char **str, const char *delim); 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); void string_remove_all_chars(char *str, char c);
/** /* Replaces every instance of character 'find' in 'str'
* string_replace_all_chars: * with character 'replace' */
* @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); void string_replace_all_chars(char *str, char find, char replace);
/** /* Converts string to unsigned integer.
* string_to_unsigned: * Returns 0 if string is invalid */
* @str : input string
*
* Converts string to unsigned integer.
*
* @return 0 if string is invalid, otherwise > 0
**/
unsigned string_to_unsigned(const char *str); 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'. * 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); unsigned string_hex_to_unsigned(const char *str);
char *string_init(const char *src); char *string_init(const char *src);
void string_set(char **string, 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]; extern const unsigned char lr_char_props[256];
RETRO_END_DECLS RETRO_END_DECLS

View File

@ -71,12 +71,6 @@ bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *dirstream);
int retro_vfs_closedir_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 RETRO_END_DECLS
#endif #endif

View File

@ -25,6 +25,7 @@
#include <string.h> #include <string.h>
#include <stdarg.h> #include <stdarg.h>
#include <ctype.h> #include <ctype.h>
#include <errno.h>
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" #include "config.h"
@ -45,6 +46,7 @@ struct RFILE
{ {
struct retro_vfs_file_handle *hfile; struct retro_vfs_file_handle *hfile;
bool error_flag; bool error_flag;
bool eof_flag;
}; };
static retro_vfs_get_path_t filestream_get_path_cb = NULL; static retro_vfs_get_path_t filestream_get_path_cb = NULL;
@ -107,14 +109,18 @@ bool filestream_exists(const char *path)
if (!path || !*path) if (!path || !*path)
return false; return false;
if (!(dummy = filestream_open(
dummy = filestream_open(
path, path,
RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_READ,
RETRO_VFS_FILE_ACCESS_HINT_NONE))) RETRO_VFS_FILE_ACCESS_HINT_NONE);
if (!dummy)
return false; return false;
if (filestream_close(dummy) != 0) if (filestream_close(dummy) != 0)
free(dummy); if (dummy)
free(dummy);
dummy = NULL; dummy = NULL;
return true; return true;
@ -159,7 +165,7 @@ int64_t filestream_truncate(RFILE *stream, int64_t length)
* @hints : * @hints :
* *
* Opens a file for reading or writing, depending on the requested mode. * Opens a file for reading or writing, depending on the requested mode.
* @return A pointer to an RFILE if opened successfully, otherwise NULL. * Returns a pointer to an RFILE if opened successfully, otherwise NULL.
**/ **/
RFILE* filestream_open(const char *path, unsigned mode, unsigned hints) RFILE* filestream_open(const char *path, unsigned mode, unsigned hints)
{ {
@ -178,6 +184,7 @@ RFILE* filestream_open(const char *path, unsigned mode, unsigned hints)
output = (RFILE*)malloc(sizeof(RFILE)); output = (RFILE*)malloc(sizeof(RFILE));
output->error_flag = false; output->error_flag = false;
output->eof_flag = false;
output->hfile = fp; output->hfile = fp;
return output; return output;
} }
@ -219,7 +226,7 @@ int filestream_vscanf(RFILE *stream, const char* format, va_list *args)
char buf[4096]; char buf[4096];
char subfmt[64]; char subfmt[64];
va_list args_copy; va_list args_copy;
const char *bufiter = buf; const char * bufiter = buf;
int ret = 0; int ret = 0;
int64_t startpos = filestream_tell(stream); int64_t startpos = filestream_tell(stream);
int64_t maxlen = filestream_read(stream, buf, sizeof(buf)-1); int64_t maxlen = filestream_read(stream, buf, sizeof(buf)-1);
@ -234,11 +241,7 @@ int filestream_vscanf(RFILE *stream, const char* format, va_list *args)
* cause the va_list to have an indeterminate value * cause the va_list to have an indeterminate value
* in the function calling filestream_vscanf(), * in the function calling filestream_vscanf(),
* leading to unexpected behaviour */ * leading to unexpected behaviour */
#ifdef __va_copy
__va_copy(args_copy, *args);
#else
va_copy(args_copy, *args); va_copy(args_copy, *args);
#endif
while (*format) while (*format)
{ {
@ -330,7 +333,7 @@ int filestream_vscanf(RFILE *stream, const char* format, va_list *args)
} }
va_end(args_copy); va_end(args_copy);
filestream_seek(stream, startpos + (bufiter - buf), filestream_seek(stream, startpos+(bufiter-buf),
RETRO_VFS_SEEK_POSITION_START); RETRO_VFS_SEEK_POSITION_START);
return ret; return ret;
@ -360,12 +363,14 @@ int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position)
if (output == VFS_ERROR_RETURN_VALUE) if (output == VFS_ERROR_RETURN_VALUE)
stream->error_flag = true; stream->error_flag = true;
stream->eof_flag = false;
return output; return output;
} }
int filestream_eof(RFILE *stream) int filestream_eof(RFILE *stream)
{ {
return filestream_tell(stream) == filestream_get_size(stream) ? EOF : 0; return stream->eof_flag;
} }
int64_t filestream_tell(RFILE *stream) int64_t filestream_tell(RFILE *stream)
@ -390,6 +395,7 @@ void filestream_rewind(RFILE *stream)
return; return;
filestream_seek(stream, 0L, RETRO_VFS_SEEK_POSITION_START); filestream_seek(stream, 0L, RETRO_VFS_SEEK_POSITION_START);
stream->error_flag = false; stream->error_flag = false;
stream->eof_flag = false;
} }
int64_t filestream_read(RFILE *stream, void *s, int64_t len) int64_t filestream_read(RFILE *stream, void *s, int64_t len)
@ -404,6 +410,8 @@ int64_t filestream_read(RFILE *stream, void *s, int64_t len)
if (output == VFS_ERROR_RETURN_VALUE) if (output == VFS_ERROR_RETURN_VALUE)
stream->error_flag = true; stream->error_flag = true;
if (output < len)
stream->eof_flag = true;
return output; return output;
} }
@ -501,7 +509,9 @@ int filestream_printf(RFILE *stream, const char* format, ...)
int filestream_error(RFILE *stream) int filestream_error(RFILE *stream)
{ {
return (stream && stream->error_flag); if (stream && stream->error_flag)
return 1;
return 0;
} }
int filestream_close(RFILE *stream) int filestream_close(RFILE *stream)
@ -530,7 +540,7 @@ int filestream_close(RFILE *stream)
* *
* Read the contents of a file into @buf. * Read the contents of a file into @buf.
* *
* @return Non-zero on success. * Returns: non zero on success.
*/ */
int64_t filestream_read_file(const char *path, void **buf, int64_t *len) int64_t filestream_read_file(const char *path, void **buf, int64_t *len)
{ {
@ -547,20 +557,25 @@ int64_t filestream_read_file(const char *path, void **buf, int64_t *len)
return 0; return 0;
} }
if ((content_buf_size = filestream_get_size(file)) < 0) content_buf_size = filestream_get_size(file);
if (content_buf_size < 0)
goto error; goto error;
if (!(content_buf = malloc((size_t)(content_buf_size + 1)))) content_buf = malloc((size_t)(content_buf_size + 1));
if (!content_buf)
goto error; goto error;
if ((int64_t)(uint64_t)(content_buf_size + 1) != (content_buf_size + 1)) if ((int64_t)(uint64_t)(content_buf_size + 1) != (content_buf_size + 1))
goto error; goto error;
if ((ret = filestream_read(file, content_buf, (int64_t)content_buf_size)) < ret = filestream_read(file, content_buf, (int64_t)content_buf_size);
0) if (ret < 0)
goto error; goto error;
if (filestream_close(file) != 0) if (filestream_close(file) != 0)
free(file); if (file)
free(file);
*buf = content_buf; *buf = content_buf;
@ -574,8 +589,9 @@ int64_t filestream_read_file(const char *path, void **buf, int64_t *len)
return 1; return 1;
error: error:
if (filestream_close(file) != 0) if (file)
free(file); if (filestream_close(file) != 0)
free(file);
if (content_buf) if (content_buf)
free(content_buf); free(content_buf);
if (len) if (len)
@ -592,8 +608,8 @@ error:
* *
* Writes data to a file. * Writes data to a file.
* *
* @return true on success, otherwise false. * Returns: true (1) on success, false (0) otherwise.
**/ */
bool filestream_write_file(const char *path, const void *data, int64_t size) bool filestream_write_file(const char *path, const void *data, int64_t size)
{ {
int64_t ret = 0; int64_t ret = 0;
@ -602,18 +618,20 @@ bool filestream_write_file(const char *path, const void *data, int64_t size)
RETRO_VFS_FILE_ACCESS_HINT_NONE); RETRO_VFS_FILE_ACCESS_HINT_NONE);
if (!file) if (!file)
return false; return false;
ret = filestream_write(file, data, size); ret = filestream_write(file, data, size);
if (filestream_close(file) != 0) if (filestream_close(file) != 0)
free(file); if (file)
return (ret == size); free(file);
if (ret != size)
return false;
return true;
} }
/** /* Returned pointer must be freed by the caller. */
* filestream_getline: char* filestream_getline(RFILE *stream)
*
* Returned pointer must be freed by the caller.
**/
char *filestream_getline(RFILE *stream)
{ {
char *newline_tmp = NULL; char *newline_tmp = NULL;
size_t cur_size = 8; size_t cur_size = 8;
@ -628,15 +646,16 @@ char *filestream_getline(RFILE *stream)
return NULL; return NULL;
} }
in = filestream_getc(stream); in = filestream_getc(stream);
while (in != EOF && in != '\n') while (in != EOF && in != '\n')
{ {
if (idx == cur_size) if (idx == cur_size)
{ {
cur_size *= 2; cur_size *= 2;
newline_tmp = (char*)realloc(newline, cur_size + 1);
if (!(newline_tmp = (char*)realloc(newline, cur_size + 1))) if (!newline_tmp)
{ {
free(newline); free(newline);
return NULL; return NULL;

View File

@ -69,27 +69,17 @@ RFILE* rfopen(const char *path, const char *mode)
int rfclose(RFILE* stream) int rfclose(RFILE* stream)
{ {
if (!stream)
return EOF;
return filestream_close(stream); return filestream_close(stream);
} }
int64_t rftell(RFILE* stream) int64_t rftell(RFILE* stream)
{ {
if (!stream)
return -1;
return filestream_tell(stream); return filestream_tell(stream);
} }
int64_t rfseek(RFILE* stream, int64_t offset, int origin) int64_t rfseek(RFILE* stream, int64_t offset, int origin)
{ {
int seek_position = -1; int seek_position = -1;
if (!stream)
return -1;
switch (origin) switch (origin)
{ {
case SEEK_SET: case SEEK_SET:
@ -109,61 +99,39 @@ int64_t rfseek(RFILE* stream, int64_t offset, int origin)
int64_t rfread(void* buffer, int64_t rfread(void* buffer,
size_t elem_size, size_t elem_count, RFILE* stream) 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); return (filestream_read(stream, buffer, elem_size * elem_count) / elem_size);
} }
char *rfgets(char *buffer, int maxCount, RFILE* stream) char *rfgets(char *buffer, int maxCount, RFILE* stream)
{ {
if (!stream)
return NULL;
return filestream_gets(stream, buffer, maxCount); return filestream_gets(stream, buffer, maxCount);
} }
int rfgetc(RFILE* stream) int rfgetc(RFILE* stream)
{ {
if (!stream)
return EOF;
return filestream_getc(stream); return filestream_getc(stream);
} }
int64_t rfwrite(void const* buffer, int64_t rfwrite(void const* buffer,
size_t elem_size, size_t elem_count, RFILE* stream) size_t elem_size, size_t elem_count, RFILE* stream)
{ {
if (!stream || (elem_size == 0) || (elem_count == 0)) return filestream_write(stream, buffer, elem_size * elem_count);
return 0;
return (filestream_write(stream, buffer, elem_size * elem_count) / elem_size);
} }
int rfputc(int character, RFILE * stream) int rfputc(int character, RFILE * stream)
{ {
if (!stream) return filestream_putc(stream, character);
return EOF;
return filestream_putc(stream, character);
} }
int64_t rfflush(RFILE * stream) int64_t rfflush(RFILE * stream)
{ {
if (!stream) return filestream_flush(stream);
return EOF;
return filestream_flush(stream);
} }
int rfprintf(RFILE * stream, const char * format, ...) int rfprintf(RFILE * stream, const char * format, ...)
{ {
int result; int result;
va_list vl; va_list vl;
if (!stream)
return -1;
va_start(vl, format); va_start(vl, format);
result = filestream_vprintf(stream, format, vl); result = filestream_vprintf(stream, format, vl);
va_end(vl); va_end(vl);
@ -184,10 +152,6 @@ int rfscanf(RFILE * stream, const char * format, ...)
{ {
int result; int result;
va_list vl; va_list vl;
if (!stream)
return 0;
va_start(vl, format); va_start(vl, format);
result = filestream_vscanf(stream, format, &vl); result = filestream_vscanf(stream, format, &vl);
va_end(vl); va_end(vl);

View File

@ -24,7 +24,6 @@
#include <ctype.h> #include <ctype.h>
#include <string.h> #include <string.h>
#include <compat/strl.h>
#include <string/stdstring.h> #include <string/stdstring.h>
#include <encodings/utf.h> #include <encodings/utf.h>
@ -90,11 +89,9 @@ char *string_ucwords(char *s)
} }
char *string_replace_substring(const char *in, char *string_replace_substring(const char *in,
const char *pattern, size_t pattern_len, const char *pattern, const char *replacement)
const char *replacement, size_t replacement_len)
{ {
size_t outlen; size_t numhits, pattern_len, replacement_len, outlen;
size_t numhits = 0;
const char *inat = NULL; const char *inat = NULL;
const char *inprev = NULL; const char *inprev = NULL;
char *out = NULL; char *out = NULL;
@ -105,6 +102,9 @@ char *string_replace_substring(const char *in,
if (!pattern || !replacement) if (!pattern || !replacement)
return strdup(in); return strdup(in);
pattern_len = strlen(pattern);
replacement_len = strlen(replacement);
numhits = 0;
inat = in; inat = in;
while ((inat = strstr(inat, pattern))) while ((inat = strstr(inat, pattern)))
@ -114,8 +114,9 @@ char *string_replace_substring(const char *in,
} }
outlen = strlen(in) - pattern_len*numhits + replacement_len*numhits; outlen = strlen(in) - pattern_len*numhits + replacement_len*numhits;
out = (char *)malloc(outlen+1);
if (!(out = (char *)malloc(outlen+1))) if (!out)
return NULL; return NULL;
outat = out; outat = out;
@ -128,7 +129,7 @@ char *string_replace_substring(const char *in,
outat += inat-inprev; outat += inat-inprev;
memcpy(outat, replacement, replacement_len); memcpy(outat, replacement, replacement_len);
outat += replacement_len; outat += replacement_len;
inat += pattern_len; inat += pattern_len;
inprev = inat; inprev = inat;
} }
strcpy(outat, inprev); strcpy(outat, inprev);
@ -136,11 +137,7 @@ char *string_replace_substring(const char *in,
return out; return out;
} }
/** /* Remove leading whitespaces */
* string_trim_whitespace_left:
*
* Remove leading whitespaces
**/
char *string_trim_whitespace_left(char *const s) char *string_trim_whitespace_left(char *const s)
{ {
if (s && *s) if (s && *s)
@ -161,11 +158,7 @@ char *string_trim_whitespace_left(char *const s)
return s; return s;
} }
/** /* Remove trailing whitespaces */
* string_trim_whitespace_right:
*
* Remove trailing whitespaces
**/
char *string_trim_whitespace_right(char *const s) char *string_trim_whitespace_right(char *const s)
{ {
if (s && *s) if (s && *s)
@ -185,11 +178,7 @@ char *string_trim_whitespace_right(char *const s)
return s; return s;
} }
/** /* Remove leading and trailing whitespaces */
* string_trim_whitespace:
*
* Remove leading and trailing whitespaces
**/
char *string_trim_whitespace(char *const s) char *string_trim_whitespace(char *const s)
{ {
string_trim_whitespace_right(s); /* order matters */ string_trim_whitespace_right(s); /* order matters */
@ -198,62 +187,49 @@ char *string_trim_whitespace(char *const s)
return s; return s;
} }
/** void word_wrap(char *dst, size_t dst_size, const char *src, int line_width, int wideglyph_width, 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.).
**/
size_t word_wrap(
char *dst, size_t dst_size,
const char *src, size_t src_len,
int line_width, int wideglyph_width, unsigned max_lines)
{ {
char *last_space = NULL; char *lastspace = NULL;
unsigned counter = 0; unsigned counter = 0;
unsigned lines = 1; unsigned lines = 1;
size_t src_len = strlen(src);
const char *src_end = src + src_len; const char *src_end = src + src_len;
/* Prevent buffer overflow */ /* Prevent buffer overflow */
if (dst_size < src_len + 1) if (dst_size < src_len + 1)
return 0; return;
/* Early return if src string length is less /* Early return if src string length is less
* than line width */ * than line width */
if (src_len < (size_t)line_width) if (src_len < line_width)
return strlcpy(dst, src, dst_size); {
strcpy(dst, src);
return;
}
while (*src != '\0') while (*src != '\0')
{ {
unsigned char_len = (unsigned)(utf8skip(src, 1) - src); unsigned char_len;
char_len = (unsigned)(utf8skip(src, 1) - src);
counter++; counter++;
if (*src == ' ') if (*src == ' ')
last_space = dst; /* Remember the location of the whitespace */ lastspace = dst; /* Remember the location of the whitespace */
else if (*src == '\n') else if (*src == '\n')
{ {
/* If newlines embedded in the input, /* If newlines embedded in the input,
* reset the index */ * reset the index */
lines++; lines++;
counter = 0; counter = 0;
/* Early return if remaining src string /* Early return if remaining src string
* length is less than line width */ * length is less than line width */
if (src_end - src <= line_width) if (src_end - src <= line_width)
return strlcpy(dst, src, dst_size); {
strcpy(dst, src);
return;
}
} }
while (char_len--) while (char_len--)
@ -263,69 +239,36 @@ size_t word_wrap(
{ {
counter = 0; counter = 0;
if (last_space && (max_lines == 0 || lines < max_lines)) if (lastspace && (max_lines == 0 || lines < max_lines))
{ {
/* Replace nearest (previous) whitespace /* Replace nearest (previous) whitespace
* with newline character */ * with newline character */
*last_space = '\n'; *lastspace = '\n';
lines++; lines++;
src -= dst - last_space - 1; src -= dst - lastspace - 1;
dst = last_space + 1; dst = lastspace + 1;
last_space = NULL; lastspace = NULL;
/* Early return if remaining src string /* Early return if remaining src string
* length is less than line width */ * length is less than line width */
if (src_end - src < line_width) if (src_end - src < line_width)
return strlcpy(dst, src, dst_size); {
strcpy(dst, src);
return;
}
} }
} }
} }
*dst = '\0'; *dst = '\0';
return 0;
} }
/** void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src, int line_width, int wideglyph_width, unsigned max_lines)
* 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.
**/
size_t 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 *lastspace = NULL;
char *lastwideglyph = NULL; char *lastwideglyph = NULL;
const char *src_end = src + src_len; const char *src_end = src + strlen(src);
unsigned lines = 1; unsigned lines = 1;
/* 'line_width' means max numbers of characters per line, /* 'line_width' means max numbers of characters per line,
* but this metric is only meaningful when dealing with * but this metric is only meaningful when dealing with
@ -351,15 +294,20 @@ size_t word_wrap_wideglyph(char *dst, size_t dst_size,
unsigned counter_normalized = 0; unsigned counter_normalized = 0;
int line_width_normalized = line_width * 100; int line_width_normalized = line_width * 100;
int additional_counter_normalized = wideglyph_width - 100; int additional_counter_normalized = wideglyph_width - 100;
/* Early return if src string length is less /* Early return if src string length is less
* than line width */ * than line width */
if (src_end - src < line_width) if (src_end - src < line_width)
return strlcpy(dst, src, dst_size); {
strlcpy(dst, src, dst_size);
return;
}
while (*src != '\0') while (*src != '\0')
{ {
unsigned char_len = (unsigned)(utf8skip(src, 1) - src); unsigned char_len;
char_len = (unsigned)(utf8skip(src, 1) - src);
counter_normalized += 100; counter_normalized += 100;
/* Prevent buffer overflow */ /* Prevent buffer overflow */
@ -367,7 +315,7 @@ size_t word_wrap_wideglyph(char *dst, size_t dst_size,
break; break;
if (*src == ' ') if (*src == ' ')
lastspace = dst; /* Remember the location of the whitespace */ lastspace = dst; /* Remember the location of the whitespace */
else if (*src == '\n') else if (*src == '\n')
{ {
/* If newlines embedded in the input, /* If newlines embedded in the input,
@ -378,13 +326,16 @@ size_t word_wrap_wideglyph(char *dst, size_t dst_size,
/* Early return if remaining src string /* Early return if remaining src string
* length is less than line width */ * length is less than line width */
if (src_end - src <= line_width) if (src_end - src <= line_width)
return strlcpy(dst, src, dst_size); {
strlcpy(dst, src, dst_size);
return;
}
} }
else if (char_len >= 3) else if (char_len >= 3)
{ {
/* Remember the location of the first byte /* Remember the location of the first byte
* whose length as UTF-8 >= 3*/ * whose length as UTF-8 >= 3*/
lastwideglyph = dst; lastwideglyph = dst;
counter_normalized += additional_counter_normalized; counter_normalized += additional_counter_normalized;
} }
@ -403,14 +354,17 @@ size_t word_wrap_wideglyph(char *dst, size_t dst_size,
/* Insert newline character */ /* Insert newline character */
*lastwideglyph = '\n'; *lastwideglyph = '\n';
lines++; lines++;
src -= dst - lastwideglyph; src -= dst - lastwideglyph;
dst = lastwideglyph + 1; dst = lastwideglyph + 1;
lastwideglyph = NULL; lastwideglyph = NULL;
/* Early return if remaining src string /* Early return if remaining src string
* length is less than line width */ * length is less than line width */
if (src_end - src <= line_width) if (src_end - src <= line_width)
return strlcpy(dst, src, dst_size); {
strlcpy(dst, src, dst_size);
return;
}
} }
else if (lastspace) else if (lastspace)
{ {
@ -418,29 +372,28 @@ size_t word_wrap_wideglyph(char *dst, size_t dst_size,
* with newline character */ * with newline character */
*lastspace = '\n'; *lastspace = '\n';
lines++; lines++;
src -= dst - lastspace - 1; src -= dst - lastspace - 1;
dst = lastspace + 1; dst = lastspace + 1;
lastspace = NULL; lastspace = NULL;
/* Early return if remaining src string /* Early return if remaining src string
* length is less than line width */ * length is less than line width */
if (src_end - src < line_width) if (src_end - src < line_width)
return strlcpy(dst, src, dst_size); {
strlcpy(dst, src, dst_size);
return;
}
} }
} }
} }
*dst = '\0'; *dst = '\0';
return 0;
} }
/** /* Splits string into tokens seperated by 'delim'
* string_tokenize:
*
* Splits string into tokens seperated by @delim
* > Returned token string must be free()'d * > Returned token string must be free()'d
* > Returns NULL if token is not found * > 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 * last found token
* > Tokens *include* empty strings * > Tokens *include* empty strings
* Usage example: * Usage example:
@ -453,7 +406,7 @@ size_t word_wrap_wideglyph(char *dst, size_t dst_size,
* free(token); * free(token);
* token = NULL; * token = NULL;
* } * }
**/ */
char* string_tokenize(char **str, const char *delim) 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# */ /* Taken from https://codereview.stackexchange.com/questions/216956/strtok-function-thread-safe-supports-empty-tokens-doesnt-change-string# */
@ -466,20 +419,25 @@ char* string_tokenize(char **str, const char *delim)
if (!str || string_is_empty(delim)) if (!str || string_is_empty(delim))
return NULL; return NULL;
str_ptr = *str;
/* Note: we don't check string_is_empty() here, /* Note: we don't check string_is_empty() here,
* empty strings are valid */ * empty strings are valid */
if (!(str_ptr = *str)) if (!str_ptr)
return NULL; return NULL;
/* Search for delimiter */ /* Search for delimiter */
if ((delim_ptr = strstr(str_ptr, delim))) delim_ptr = strstr(str_ptr, delim);
if (delim_ptr)
token_len = delim_ptr - str_ptr; token_len = delim_ptr - str_ptr;
else else
token_len = strlen(str_ptr); token_len = strlen(str_ptr);
/* Allocate token string */ /* Allocate token string */
if (!(token = (char *)malloc((token_len + 1) * sizeof(char)))) token = (char *)malloc((token_len + 1) * sizeof(char));
if (!token)
return NULL; return NULL;
/* Copy token */ /* Copy token */
@ -492,53 +450,42 @@ char* string_tokenize(char **str, const char *delim)
return token; 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) void string_remove_all_chars(char *str, char c)
{ {
char *read_ptr = str; char *read_ptr = NULL;
char *write_ptr = str; char *write_ptr = NULL;
if (string_is_empty(str))
return;
read_ptr = str;
write_ptr = str;
while (*read_ptr != '\0') while (*read_ptr != '\0')
{ {
*write_ptr = *read_ptr++; *write_ptr = *read_ptr++;
if (*write_ptr != c) write_ptr += (*write_ptr != c) ? 1 : 0;
write_ptr++;
} }
*write_ptr = '\0'; *write_ptr = '\0';
} }
/** /* Replaces every instance of character 'find' in 'str'
* string_replace_all_chars: * with character 'replace' */
* @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) void string_replace_all_chars(char *str, char find, char replace)
{ {
char *str_ptr = str; char *str_ptr = str;
if (string_is_empty(str))
return;
while ((str_ptr = strchr(str_ptr, find))) while ((str_ptr = strchr(str_ptr, find)))
*str_ptr++ = replace; *str_ptr++ = replace;
} }
/** /* Converts string to unsigned integer.
* string_to_unsigned: * Returns 0 if string is invalid */
* @str : input string
*
* Converts string to unsigned integer.
*
* @return 0 if string is invalid, otherwise > 0
**/
unsigned string_to_unsigned(const char *str) unsigned string_to_unsigned(const char *str)
{ {
const char *ptr = NULL; const char *ptr = NULL;
@ -555,33 +502,27 @@ unsigned string_to_unsigned(const char *str)
return (unsigned)strtoul(str, NULL, 10); 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'. * 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) unsigned string_hex_to_unsigned(const char *str)
{ {
const char *hex_str = str; const char *hex_str = str;
const char *ptr = NULL; const char *ptr = NULL;
size_t len;
if (string_is_empty(str))
return 0;
/* Remove leading '0x', if required */ /* Remove leading '0x', if required */
if (str[0] != '\0' && str[1] != '\0') len = strlen(str);
{
if ( (str[0] == '0') && if (len >= 2)
((str[1] == 'x') || if ((str[0] == '0') &&
(str[1] == 'X'))) ((str[1] == 'x') || (str[1] == 'X')))
{
hex_str = str + 2; hex_str = str + 2;
if (string_is_empty(hex_str))
return 0; if (string_is_empty(hex_str))
}
}
else
return 0; return 0;
/* Check for valid characters */ /* Check for valid characters */
@ -593,117 +534,3 @@ unsigned string_hex_to_unsigned(const char *str)
return (unsigned)strtoul(hex_str, NULL, 16); 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';
}

View File

@ -22,6 +22,7 @@
#ifdef HAVE_THREADS #ifdef HAVE_THREADS
#include <rthreads/rthreads.h> #include <rthreads/rthreads.h>
#include <retro_assert.h>
#include <stdlib.h> #include <stdlib.h>
#endif #endif
@ -40,6 +41,8 @@ void rtime_init(void)
#ifdef HAVE_THREADS #ifdef HAVE_THREADS
if (!rtime_localtime_lock) if (!rtime_localtime_lock)
rtime_localtime_lock = slock_new(); rtime_localtime_lock = slock_new();
retro_assert(rtime_localtime_lock);
#endif #endif
} }

View File

@ -26,7 +26,7 @@
#include <errno.h> #include <errno.h>
#include <sys/types.h> #include <sys/types.h>
#include <string/stdstring.h> /* string_is_empty */ #include <string/stdstring.h>
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" #include "config.h"
@ -57,6 +57,11 @@
# include <dirent.h> # include <dirent.h>
# endif # endif
# include <unistd.h> # include <unistd.h>
# if defined(ORBIS)
# include <sys/fcntl.h>
# include <sys/dirent.h>
# include <orbisFile.h>
# endif
# if defined(WIIU) # if defined(WIIU)
# include <malloc.h> # include <malloc.h>
# endif # endif
@ -69,6 +74,11 @@
# include <psp2/io/fcntl.h> # include <psp2/io/fcntl.h>
# include <psp2/io/dirent.h> # include <psp2/io/dirent.h>
# include <psp2/io/stat.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) #elif !defined(_WIN32)
# if defined(PSP) # if defined(PSP)
# include <pspiofilemgr.h> # include <pspiofilemgr.h>
@ -114,66 +124,19 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
#if defined(ORBIS)
#include <orbisFile.h>
#include <sys/fcntl.h>
#include <sys/dirent.h>
#endif
#if defined(PSP) #if defined(PSP)
#include <pspkernel.h> #include <pspkernel.h>
#endif #endif
#if defined(__PS3__) || defined(__PSL1GHT__) #if defined(__PS3__) || defined(__PSL1GHT__)
#define FS_SUCCEEDED 0 #include <defines/ps3_defines.h>
#define FS_TYPE_DIR 1 #if defined(__PSL1GHT__)
#ifdef __PSL1GHT__
#include <lv2/sysfs.h> #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
#endif #endif
@ -237,6 +200,13 @@ int64_t retro_vfs_file_seek_internal(
#ifdef ATLEAST_VC2005 #ifdef ATLEAST_VC2005
/* VC2005 and up have a special 64-bit fseek */ /* VC2005 and up have a special 64-bit fseek */
return _fseeki64(stream->fp, offset, whence); 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) #elif defined(HAVE_64BIT_OFFSETS)
return fseeko(stream->fp, (off_t)offset, whence); return fseeko(stream->fp, (off_t)offset, whence);
#else #else
@ -299,9 +269,22 @@ int64_t retro_vfs_file_seek_internal(
libretro_vfs_implementation_file *retro_vfs_file_open_impl( libretro_vfs_implementation_file *retro_vfs_file_open_impl(
const char *path, unsigned mode, unsigned hints) 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; int flags = 0;
const char *mode_str = NULL; const char *mode_str = NULL;
libretro_vfs_implementation_file *stream = libretro_vfs_implementation_file *stream =
(libretro_vfs_implementation_file*) (libretro_vfs_implementation_file*)
malloc(sizeof(*stream)); malloc(sizeof(*stream));
@ -323,18 +306,9 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
stream->scheme = VFS_SCHEME_NONE; stream->scheme = VFS_SCHEME_NONE;
#ifdef VFS_FRONTEND #ifdef VFS_FRONTEND
if ( path if (path_len >= dumb_prefix_len)
&& path[0] == 'v' if (!memcmp(path, dumb_prefix, dumb_prefix_len))
&& path[1] == 'f' path += dumb_prefix_siz;
&& 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 #endif
#ifdef HAVE_CDROM #ifdef HAVE_CDROM
@ -351,19 +325,13 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
stream->cdrom.last_frame[0] = '\0'; stream->cdrom.last_frame[0] = '\0';
stream->cdrom.last_frame_valid = false; stream->cdrom.last_frame_valid = false;
if ( path if (path_len > cdrom_prefix_len)
&& path[0] == 'c'
&& path[1] == 'd'
&& path[2] == 'r'
&& path[3] == 'o'
&& path[4] == 'm'
&& path[5] == ':'
&& path[6] == '/'
&& path[7] == '/'
&& path[8] != '\0')
{ {
path += sizeof("cdrom://")-1; if (!memcmp(path, cdrom_prefix, cdrom_prefix_len))
stream->scheme = VFS_SCHEME_CDROM; {
path += cdrom_prefix_siz;
stream->scheme = VFS_SCHEME_CDROM;
}
} }
#endif #endif
@ -391,20 +359,24 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
mode_str = "wb"; mode_str = "wb";
flags = O_WRONLY | O_CREAT | O_TRUNC; flags = O_WRONLY | O_CREAT | O_TRUNC;
#if !defined(ORBIS)
#if !defined(_WIN32) #if !defined(_WIN32)
flags |= S_IRUSR | S_IWUSR; flags |= S_IRUSR | S_IWUSR;
#else #else
flags |= O_BINARY; flags |= O_BINARY;
#endif
#endif #endif
break; break;
case RETRO_VFS_FILE_ACCESS_READ_WRITE: case RETRO_VFS_FILE_ACCESS_READ_WRITE:
mode_str = "w+b"; mode_str = "w+b";
flags = O_RDWR | O_CREAT | O_TRUNC; flags = O_RDWR | O_CREAT | O_TRUNC;
#if !defined(ORBIS)
#if !defined(_WIN32) #if !defined(_WIN32)
flags |= S_IRUSR | S_IWUSR; flags |= S_IRUSR | S_IWUSR;
#else #else
flags |= O_BINARY; flags |= O_BINARY;
#endif
#endif #endif
break; break;
@ -413,10 +385,12 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
mode_str = "r+b"; mode_str = "r+b";
flags = O_RDWR; flags = O_RDWR;
#if !defined(ORBIS)
#if !defined(_WIN32) #if !defined(_WIN32)
flags |= S_IRUSR | S_IWUSR; flags |= S_IRUSR | S_IWUSR;
#else #else
flags |= O_BINARY; flags |= O_BINARY;
#endif
#endif #endif
break; break;
@ -426,6 +400,15 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) 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; FILE *fp;
#ifdef HAVE_CDROM #ifdef HAVE_CDROM
if (stream->scheme == VFS_SCHEME_CDROM) if (stream->scheme == VFS_SCHEME_CDROM)
@ -442,24 +425,25 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
else else
#endif #endif
{ {
if (!(fp = (FILE*)fopen_utf8(path, mode_str))) fp = (FILE*)fopen_utf8(path, mode_str);
if (!fp)
goto error; goto error;
stream->fp = fp; stream->fp = fp;
} }
/* Regarding setvbuf: /* Regarding setvbuf:
* *
* https://www.freebsd.org/cgi/man.cgi?query=setvbuf&apropos=0&sektion=0&manpath=FreeBSD+11.1-RELEASE&arch=default&format=html * https://www.freebsd.org/cgi/man.cgi?query=setvbuf&apropos=0&sektion=0&manpath=FreeBSD+11.1-RELEASE&arch=default&format=html
* *
* If the size argument is not zero but buf is NULL, * If the size argument is not zero but buf is NULL,
* a buffer of the given size will be allocated immediately, and * a buffer of the given size will be allocated immediately, and
* released on close. This is an extension to ANSI C. * released on close. This is an extension to ANSI C.
* *
* Since C89 does not support specifying a NULL buffer * Since C89 does not support specifying a NULL buffer
* with a non-zero size, we create and track our own buffer for it. * with a non-zero size, we create and track our own buffer for it.
*/ */
/* TODO: this is only useful for a few platforms, /* TODO: this is only useful for a few platforms,
* find which and add ifdef */ * find which and add ifdef */
#if defined(_3DS) #if defined(_3DS)
if (stream->scheme != VFS_SCHEME_CDROM) if (stream->scheme != VFS_SCHEME_CDROM)
@ -471,14 +455,19 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
#elif defined(WIIU) #elif defined(WIIU)
if (stream->scheme != VFS_SCHEME_CDROM) if (stream->scheme != VFS_SCHEME_CDROM)
{ {
const int bufsize = 128 * 1024; const int bufsize = 128*1024;
stream->buf = (char*)memalign(0x40, bufsize); stream->buf = (char*)memalign(0x40, bufsize);
if (stream->fp) if (stream->fp)
setvbuf(stream->fp, stream->buf, _IOFBF, bufsize); setvbuf(stream->fp, stream->buf, _IOFBF, bufsize);
}
#elif !defined(PSP)
if (stream->scheme != VFS_SCHEME_CDROM)
{
stream->buf = (char*)calloc(1, 0x4000); stream->buf = (char*)calloc(1, 0x4000);
if (stream->fp) if (stream->fp)
setvbuf(stream->fp, stream->buf, _IOFBF, 0x4000); setvbuf(stream->fp, stream->buf, _IOFBF, 0x4000);
} }
#endif
#endif #endif
} }
else else
@ -514,12 +503,18 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
retro_vfs_file_seek_internal(stream, 0, SEEK_SET); retro_vfs_file_seek_internal(stream, 0, SEEK_SET);
if ((stream->mapped = (uint8_t*)mmap((void*)0, stream->mapped = (uint8_t*)mmap((void*)0,
stream->mapsize, PROT_READ, MAP_SHARED, stream->fd, 0)) == MAP_FAILED) stream->mapsize, PROT_READ, MAP_SHARED, stream->fd, 0);
if (stream->mapped == MAP_FAILED)
stream->hints &= ~RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS; stream->hints &= ~RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS;
} }
#endif #endif
} }
#ifdef ORBIS
stream->size = orbisLseek(stream->fd, 0, SEEK_END);
orbisLseek(stream->fd, 0, SEEK_SET);
#else
#ifdef HAVE_CDROM #ifdef HAVE_CDROM
if (stream->scheme == VFS_SCHEME_CDROM) if (stream->scheme == VFS_SCHEME_CDROM)
{ {
@ -540,6 +535,7 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
retro_vfs_file_seek_internal(stream, 0, SEEK_SET); retro_vfs_file_seek_internal(stream, 0, SEEK_SET);
} }
#endif
return stream; return stream;
error: error:
@ -574,7 +570,14 @@ int retro_vfs_file_close_impl(libretro_vfs_implementation_file *stream)
} }
if (stream->fd > 0) if (stream->fd > 0)
{
#ifdef ORBIS
orbisClose(stream->fd);
stream->fd = -1;
#else
close(stream->fd); close(stream->fd);
#endif
}
#ifdef HAVE_CDROM #ifdef HAVE_CDROM
end: end:
if (stream->cdrom.cue_buf) if (stream->cdrom.cue_buf)
@ -597,7 +600,12 @@ int retro_vfs_file_error_impl(libretro_vfs_implementation_file *stream)
if (stream->scheme == VFS_SCHEME_CDROM) if (stream->scheme == VFS_SCHEME_CDROM)
return retro_vfs_file_error_cdrom(stream); return retro_vfs_file_error_cdrom(stream);
#endif #endif
#ifdef ORBIS
/* TODO/FIXME - implement this? */
return 0;
#else
return ferror(stream->fp); return ferror(stream->fp);
#endif
} }
int64_t retro_vfs_file_size_impl(libretro_vfs_implementation_file *stream) int64_t retro_vfs_file_size_impl(libretro_vfs_implementation_file *stream)
@ -609,20 +617,18 @@ int64_t retro_vfs_file_size_impl(libretro_vfs_implementation_file *stream)
int64_t retro_vfs_file_truncate_impl(libretro_vfs_implementation_file *stream, int64_t length) int64_t retro_vfs_file_truncate_impl(libretro_vfs_implementation_file *stream, int64_t length)
{ {
if (!stream)
return -1;
#ifdef _WIN32 #ifdef _WIN32
if (stream && _chsize(_fileno(stream->fp), length) == 0) if (_chsize(_fileno(stream->fp), length) != 0)
{ return -1;
stream->size = length;
return 0;
}
#elif !defined(VITA) && !defined(PSP) && !defined(PS2) && !defined(ORBIS) && (!defined(SWITCH) || defined(HAVE_LIBNX)) #elif !defined(VITA) && !defined(PSP) && !defined(PS2) && !defined(ORBIS) && (!defined(SWITCH) || defined(HAVE_LIBNX))
if (stream && ftruncate(fileno(stream->fp), (off_t)length) == 0) if (ftruncate(fileno(stream->fp), (off_t)length) != 0)
{ return -1;
stream->size = length;
return 0;
}
#endif #endif
return -1;
return 0;
} }
int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream) int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream)
@ -636,6 +642,14 @@ int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream)
if (stream->scheme == VFS_SCHEME_CDROM) if (stream->scheme == VFS_SCHEME_CDROM)
return retro_vfs_file_tell_cdrom(stream); return retro_vfs_file_tell_cdrom(stream);
#endif #endif
#ifdef ORBIS
{
int64_t ret = orbisLseek(stream->fd, 0, SEEK_CUR);
if (ret < 0)
return -1;
return ret;
}
#else
#ifdef ATLEAST_VC2005 #ifdef ATLEAST_VC2005
/* VC2005 and up have a special 64-bit ftell */ /* VC2005 and up have a special 64-bit ftell */
return _ftelli64(stream->fp); return _ftelli64(stream->fp);
@ -643,12 +657,13 @@ int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream)
return ftello(stream->fp); return ftello(stream->fp);
#else #else
return ftell(stream->fp); return ftell(stream->fp);
#endif
#endif #endif
} }
#ifdef HAVE_MMAP #ifdef HAVE_MMAP
/* Need to check stream->mapped because this function /* Need to check stream->mapped because this function
* is called in filestream_open() */ * is called in filestream_open() */
if (stream->mapped && stream->hints & if (stream->mapped && stream->hints &
RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS) RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
return stream->mappos; return stream->mappos;
#endif #endif
@ -661,7 +676,21 @@ 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 retro_vfs_file_seek_impl(libretro_vfs_implementation_file *stream,
int64_t offset, int seek_position) int64_t offset, int seek_position)
{ {
return retro_vfs_file_seek_internal(stream, offset, 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);
} }
int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream, int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream,
@ -676,7 +705,13 @@ int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream,
if (stream->scheme == VFS_SCHEME_CDROM) if (stream->scheme == VFS_SCHEME_CDROM)
return retro_vfs_file_read_cdrom(stream, s, len); return retro_vfs_file_read_cdrom(stream, s, len);
#endif #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); return fread(s, 1, (size_t)len, stream->fp);
#endif
} }
#ifdef HAVE_MMAP #ifdef HAVE_MMAP
if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS) if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
@ -699,80 +734,83 @@ 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 retro_vfs_file_write_impl(libretro_vfs_implementation_file *stream, const void *s, uint64_t len)
{ {
int64_t pos = 0;
ssize_t result = -1;
if (!stream) if (!stream)
return -1; return -1;
if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
{ {
pos = retro_vfs_file_tell_impl(stream); #ifdef ORBIS
result = fwrite(s, 1, (size_t)len, stream->fp); if (orbisWrite(stream->fd, s, (size_t)len) < 0)
return -1;
if (result != -1 && pos + result > stream->size) return 0;
stream->size = pos + result; #else
return fwrite(s, 1, (size_t)len, stream->fp);
return result; #endif
} }
#ifdef HAVE_MMAP #ifdef HAVE_MMAP
if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS) if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
return -1; return -1;
#endif #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) int retro_vfs_file_flush_impl(libretro_vfs_implementation_file *stream)
{ {
if (stream && fflush(stream->fp) == 0) if (!stream)
return 0; return -1;
return -1; #ifdef ORBIS
return 0;
#else
return fflush(stream->fp) == 0 ? 0 : -1;
#endif
} }
int retro_vfs_file_remove_impl(const char *path) int retro_vfs_file_remove_impl(const char *path)
{ {
if (path && *path)
{
int ret = -1;
#if defined(_WIN32) && !defined(_XBOX) #if defined(_WIN32) && !defined(_XBOX)
/* Win32 (no Xbox) */ /* Win32 (no Xbox) */
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500
char *path_local = NULL; char *path_local = NULL;
if ((path_local = utf8_to_local_string_alloc(path)))
{
/* We need to check if path is a directory */
if ((retro_vfs_stat_impl(path, NULL) & RETRO_VFS_STAT_IS_DIRECTORY) != 0)
ret = _rmdir(path_local);
else
ret = remove(path_local);
free(path_local);
}
#else #else
wchar_t *path_wide = NULL; wchar_t *path_wide = NULL;
if ((path_wide = utf8_to_utf16_string_alloc(path)))
{
/* We need to check if path is a directory */
if ((retro_vfs_stat_impl(path, NULL) & RETRO_VFS_STAT_IS_DIRECTORY) != 0)
ret = _wrmdir(path_wide);
else
ret = _wremove(path_wide);
free(path_wide);
}
#endif
#else
ret = remove(path);
#endif #endif
if (!path || !*path)
return -1;
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500
path_local = utf8_to_local_string_alloc(path);
if (path_local)
{
int ret = remove(path_local);
free(path_local);
if (ret == 0) if (ret == 0)
return 0; return 0;
} }
#else
path_wide = utf8_to_utf16_string_alloc(path);
if (path_wide)
{
int ret = _wremove(path_wide);
free(path_wide);
if (ret == 0)
return 0;
}
#endif
return -1; return -1;
#elif defined(ORBIS)
/* Orbis
* TODO/FIXME - stub for now */
return 0;
#else
if (remove(path) == 0)
return 0;
return -1;
#endif
} }
int retro_vfs_file_rename_impl(const char *old_path, const char *new_path) int retro_vfs_file_rename_impl(const char *old_path, const char *new_path)
@ -824,6 +862,13 @@ int retro_vfs_file_rename_impl(const char *old_path, const char *new_path)
#endif #endif
return ret; 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 #else
/* Every other platform */ /* Every other platform */
if (!old_path || !*old_path || !new_path || !*new_path) if (!old_path || !*old_path || !new_path || !*new_path)
@ -843,123 +888,154 @@ const char *retro_vfs_file_get_path_impl(
int retro_vfs_stat_impl(const char *path, int32_t *size) int retro_vfs_stat_impl(const char *path, int32_t *size)
{ {
int ret = RETRO_VFS_STAT_IS_VALID; bool is_dir = false;
bool is_character_special = false;
#if defined(VITA) || defined(PSP)
/* Vita / PSP */
SceIoStat buf;
int dir_ret;
char *tmp = NULL;
size_t len = 0;
if (!path || !*path) if (!path || !*path)
return 0; return 0;
{
#if defined(VITA)
/* Vita / PSP */
SceIoStat stat_buf;
int dir_ret;
char *tmp = strdup(path);
size_t len = strlen(tmp);
if (tmp[len-1] == '/')
tmp[len-1] = '\0';
dir_ret = sceIoGetstat(tmp, &stat_buf); tmp = strdup(path);
free(tmp); len = strlen(tmp);
if (dir_ret < 0) if (tmp[len-1] == '/')
return 0; tmp[len-1] = '\0';
if (size) dir_ret = sceIoGetstat(tmp, &buf);
*size = (int32_t)stat_buf.st_size; free(tmp);
if (dir_ret < 0)
return 0;
if (FIO_S_ISDIR(stat_buf.st_mode)) if (size)
ret |= RETRO_VFS_STAT_IS_DIRECTORY; *size = (int32_t)buf.st_size;
is_dir = FIO_S_ISDIR(buf.st_mode);
#elif defined(ORBIS)
/* Orbis */
int dir_ret = 0;
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(__PSL1GHT__) || defined(__PS3__) #elif defined(__PSL1GHT__) || defined(__PS3__)
/* Lowlevel Lv2 */ /* Lowlevel Lv2 */
sysFSStat stat_buf; sysFSStat buf;
if (sysFsStat(path, &stat_buf) < 0) if (!path || !*path)
return 0; return 0;
if (sysFsStat(path, &buf) < 0)
return 0;
if (size) if (size)
*size = (int32_t)stat_buf.st_size; *size = (int32_t)buf.st_size;
if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) is_dir = ((buf.st_mode & S_IFMT) == S_IFDIR);
ret |= RETRO_VFS_STAT_IS_DIRECTORY;
#elif defined(_WIN32) #elif defined(_WIN32)
/* Windows */ /* Windows */
struct _stat stat_buf; DWORD file_info;
struct _stat buf;
#if defined(LEGACY_WIN32) #if defined(LEGACY_WIN32)
char *path_local = utf8_to_local_string_alloc(path); char *path_local = NULL;
DWORD file_info = GetFileAttributes(path_local);
if (!string_is_empty(path_local))
_stat(path_local, &stat_buf);
if (path_local)
free(path_local);
#else #else
wchar_t *path_wide = utf8_to_utf16_string_alloc(path); wchar_t *path_wide = NULL;
DWORD file_info = GetFileAttributesW(path_wide);
_wstat(path_wide, &stat_buf);
if (path_wide)
free(path_wide);
#endif #endif
if (file_info == INVALID_FILE_ATTRIBUTES)
return 0;
if (size) if (!path || !*path)
*size = (int32_t)stat_buf.st_size; return 0;
#if defined(LEGACY_WIN32)
path_local = utf8_to_local_string_alloc(path);
file_info = GetFileAttributes(path_local);
if (!string_is_empty(path_local))
_stat(path_local, &buf);
if (path_local)
free(path_local);
#else
path_wide = utf8_to_utf16_string_alloc(path);
file_info = GetFileAttributesW(path_wide);
_wstat(path_wide, &buf);
if (path_wide)
free(path_wide);
#endif
if (file_info == INVALID_FILE_ATTRIBUTES)
return 0;
if (size)
*size = (int32_t)buf.st_size;
is_dir = (file_info & FILE_ATTRIBUTE_DIRECTORY);
if (file_info & FILE_ATTRIBUTE_DIRECTORY)
ret |= RETRO_VFS_STAT_IS_DIRECTORY;
#elif defined(GEKKO) #elif defined(GEKKO)
/* On GEKKO platforms, paths cannot have /* On GEKKO platforms, paths cannot have
* trailing slashes - we must therefore * trailing slashes - we must therefore
* remove them */ * remove them */
size_t len; char *path_buf = NULL;
char *path_buf = NULL; int stat_ret = -1;
struct stat stat_buf; struct stat stat_buf;
size_t len;
if (!(path_buf = strdup(path))) if (string_is_empty(path))
return 0; return 0;
if ((len = strlen(path_buf)) > 0) path_buf = strdup(path);
if (path_buf[len - 1] == '/') if (!path_buf)
path_buf[len - 1] = '\0'; return 0;
if (stat(path_buf, &stat_buf) < 0) len = strlen(path_buf);
{ if (len > 0)
free(path_buf); if (path_buf[len - 1] == '/')
return 0; path_buf[len - 1] = '\0';
}
free(path_buf); stat_ret = stat(path_buf, &stat_buf);
free(path_buf);
if (size)
*size = (int32_t)stat_buf.st_size; if (stat_ret < 0)
return 0;
if (size)
*size = (int32_t)stat_buf.st_size;
is_dir = S_ISDIR(stat_buf.st_mode);
is_character_special = S_ISCHR(stat_buf.st_mode);
if (S_ISDIR(stat_buf.st_mode))
ret |= RETRO_VFS_STAT_IS_DIRECTORY;
if (S_ISCHR(stat_buf.st_mode))
ret |= RETRO_VFS_STAT_IS_CHARACTER_SPECIAL;
#else #else
/* Every other platform */ /* Every other platform */
struct stat stat_buf; struct stat buf;
if (stat(path, &stat_buf) < 0) if (!path || !*path)
return 0; return 0;
if (stat(path, &buf) < 0)
return 0;
if (size) if (size)
*size = (int32_t)stat_buf.st_size; *size = (int32_t)buf.st_size;
if (S_ISDIR(stat_buf.st_mode)) is_dir = S_ISDIR(buf.st_mode);
ret |= RETRO_VFS_STAT_IS_DIRECTORY; is_character_special = S_ISCHR(buf.st_mode);
if (S_ISCHR(stat_buf.st_mode))
ret |= RETRO_VFS_STAT_IS_CHARACTER_SPECIAL;
#endif #endif
} return RETRO_VFS_STAT_IS_VALID | (is_dir ? RETRO_VFS_STAT_IS_DIRECTORY : 0) | (is_character_special ? RETRO_VFS_STAT_IS_CHARACTER_SPECIAL : 0);
return ret;
} }
#if defined(VITA) #if defined(VITA)
#define path_mkdir_error(ret) (((ret) == SCE_ERROR_ERRNO_EEXIST)) #define path_mkdir_error(ret) (((ret) == SCE_ERROR_ERRNO_EEXIST))
#elif defined(PSP) || defined(PS2) || defined(_3DS) || defined(WIIU) || defined(SWITCH) #elif defined(PSP) || defined(PS2) || defined(_3DS) || defined(WIIU) || defined(SWITCH) || defined(ORBIS)
#define path_mkdir_error(ret) ((ret) == -1) #define path_mkdir_error(ret) ((ret) == -1)
#else #else
#define path_mkdir_error(ret) ((ret) < 0 && errno == EEXIST) #define path_mkdir_error(ret) ((ret) < 0 && errno == EEXIST)
@ -982,11 +1058,13 @@ int retro_vfs_mkdir_impl(const char *dir)
#endif #endif
#elif defined(IOS) #elif defined(IOS)
int ret = mkdir(dir, 0755); int ret = mkdir(dir, 0755);
#elif defined(VITA) #elif defined(VITA) || defined(PSP)
int ret = sceIoMkdir(dir, 0777); int ret = sceIoMkdir(dir, 0777);
#elif defined(ORBIS)
int ret = orbisMkdir(dir, 0755);
#elif defined(__QNX__) #elif defined(__QNX__)
int ret = mkdir(dir, 0777); int ret = mkdir(dir, 0777);
#elif defined(GEKKO) || defined(WIIU) #elif defined(GEKKO)
/* On GEKKO platforms, mkdir() fails if /* On GEKKO platforms, mkdir() fails if
* the path has a trailing slash. We must * the path has a trailing slash. We must
* therefore remove it. */ * therefore remove it. */
@ -1033,13 +1111,16 @@ struct libretro_vfs_implementation_dir
HANDLE directory; HANDLE directory;
bool next; bool next;
char path[PATH_MAX_LENGTH]; char path[PATH_MAX_LENGTH];
#elif defined(VITA) #elif defined(VITA) || defined(PSP)
SceUID directory; SceUID directory;
SceIoDirent entry; SceIoDirent entry;
#elif defined(__PSL1GHT__) || defined(__PS3__) #elif defined(__PSL1GHT__) || defined(__PS3__)
int error; int error;
int directory; int directory;
sysFSDirent entry; sysFSDirent entry;
#elif defined(ORBIS)
int directory;
struct dirent entry;
#else #else
DIR *directory; DIR *directory;
const struct dirent *entry; const struct dirent *entry;
@ -1050,7 +1131,7 @@ static bool dirent_check_error(libretro_vfs_implementation_dir *rdir)
{ {
#if defined(_WIN32) #if defined(_WIN32)
return (rdir->directory == INVALID_HANDLE_VALUE); return (rdir->directory == INVALID_HANDLE_VALUE);
#elif defined(VITA) || defined(ORBIS) #elif defined(VITA) || defined(PSP) || defined(ORBIS)
return (rdir->directory < 0); return (rdir->directory < 0);
#elif defined(__PSL1GHT__) || defined(__PS3__) #elif defined(__PSL1GHT__) || defined(__PS3__)
return (rdir->error != FS_SUCCEEDED); return (rdir->error != FS_SUCCEEDED);
@ -1063,6 +1144,7 @@ libretro_vfs_implementation_dir *retro_vfs_opendir_impl(
const char *name, bool include_hidden) const char *name, bool include_hidden)
{ {
#if defined(_WIN32) #if defined(_WIN32)
unsigned path_len;
char path_buf[1024]; char path_buf[1024];
size_t copied = 0; size_t copied = 0;
#if defined(LEGACY_WIN32) #if defined(LEGACY_WIN32)
@ -1073,25 +1155,28 @@ libretro_vfs_implementation_dir *retro_vfs_opendir_impl(
#endif #endif
libretro_vfs_implementation_dir *rdir; libretro_vfs_implementation_dir *rdir;
/* Reject NULL or empty string paths*/ /*Reject null or empty string paths*/
if (!name || (*name == 0)) if (!name || (*name == 0))
return NULL; return NULL;
/*Allocate RDIR struct. Tidied later with retro_closedir*/ /*Allocate RDIR struct. Tidied later with retro_closedir*/
if (!(rdir = (libretro_vfs_implementation_dir*) rdir = (libretro_vfs_implementation_dir*)calloc(1, sizeof(*rdir));
calloc(1, sizeof(*rdir)))) if (!rdir)
return NULL; return NULL;
rdir->orig_path = strdup(name); rdir->orig_path = strdup(name);
#if defined(_WIN32) #if defined(_WIN32)
path_buf[0] = '\0';
path_len = strlen(name);
copied = strlcpy(path_buf, name, sizeof(path_buf)); copied = strlcpy(path_buf, name, sizeof(path_buf));
/* Non-NT platforms don't like extra slashes in the path */ /* Non-NT platforms don't like extra slashes in the path */
if (path_buf[copied - 1] != '\\') if (name[path_len - 1] != '\\')
path_buf [copied++] = '\\'; path_buf[copied++] = '\\';
path_buf[copied ] = '*'; path_buf[copied] = '*';
path_buf[copied+1] = '\0'; path_buf[copied+1] = '\0';
#if defined(LEGACY_WIN32) #if defined(LEGACY_WIN32)
@ -1108,13 +1193,15 @@ libretro_vfs_implementation_dir *retro_vfs_opendir_impl(
free(path_wide); free(path_wide);
#endif #endif
#elif defined(VITA) #elif defined(VITA) || defined(PSP)
rdir->directory = sceIoDopen(name); rdir->directory = sceIoDopen(name);
#elif defined(_3DS) #elif defined(_3DS)
rdir->directory = !string_is_empty(name) ? opendir(name) : NULL; rdir->directory = !string_is_empty(name) ? opendir(name) : NULL;
rdir->entry = NULL; rdir->entry = NULL;
#elif defined(__PSL1GHT__) || defined(__PS3__) #elif defined(__PSL1GHT__) || defined(__PS3__)
rdir->error = sysFsOpendir(name, &rdir->directory); rdir->error = sysFsOpendir(name, &rdir->directory);
#elif defined(ORBIS)
rdir->directory = orbisDopen(name);
#else #else
rdir->directory = opendir(name); rdir->directory = opendir(name);
rdir->entry = NULL; rdir->entry = NULL;
@ -1146,12 +1233,14 @@ bool retro_vfs_readdir_impl(libretro_vfs_implementation_dir *rdir)
rdir->next = true; rdir->next = true;
return (rdir->directory != INVALID_HANDLE_VALUE); return (rdir->directory != INVALID_HANDLE_VALUE);
#elif defined(VITA) #elif defined(VITA) || defined(PSP)
return (sceIoDread(rdir->directory, &rdir->entry) > 0); return (sceIoDread(rdir->directory, &rdir->entry) > 0);
#elif defined(__PSL1GHT__) || defined(__PS3__) #elif defined(__PSL1GHT__) || defined(__PS3__)
uint64_t nread; uint64_t nread;
rdir->error = sysFsReaddir(rdir->directory, &rdir->entry, &nread); rdir->error = sysFsReaddir(rdir->directory, &rdir->entry, &nread);
return (nread != 0); return (nread != 0);
#elif defined(ORBIS)
return (orbisDread(rdir->directory, &rdir->entry) > 0);
#else #else
return ((rdir->entry = readdir(rdir->directory)) != NULL); return ((rdir->entry = readdir(rdir->directory)) != NULL);
#endif #endif
@ -1170,7 +1259,7 @@ const char *retro_vfs_dirent_get_name_impl(libretro_vfs_implementation_dir *rdir
if (name) if (name)
free(name); free(name);
return (char*)rdir->entry.cFileName; return (char*)rdir->entry.cFileName;
#elif defined(VITA) || defined(__PSL1GHT__) || defined(__PS3__) #elif defined(VITA) || defined(PSP) || defined(ORBIS) || defined(__PSL1GHT__) || defined(__PS3__)
return rdir->entry.d_name; return rdir->entry.d_name;
#else #else
if (!rdir || !rdir->entry) if (!rdir || !rdir->entry)
@ -1184,12 +1273,22 @@ bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *rdir)
#if defined(_WIN32) #if defined(_WIN32)
const WIN32_FIND_DATA *entry = (const WIN32_FIND_DATA*)&rdir->entry; const WIN32_FIND_DATA *entry = (const WIN32_FIND_DATA*)&rdir->entry;
return entry->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; return entry->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
#elif defined(VITA) #elif defined(PSP) || defined(VITA)
const SceIoDirent *entry = (const SceIoDirent*)&rdir->entry; const SceIoDirent *entry = (const SceIoDirent*)&rdir->entry;
#if defined(PSP)
return (entry->d_stat.st_attr & FIO_SO_IFDIR) == FIO_SO_IFDIR;
#elif defined(VITA)
return SCE_S_ISDIR(entry->d_stat.st_mode); return SCE_S_ISDIR(entry->d_stat.st_mode);
#endif
#elif defined(__PSL1GHT__) || defined(__PS3__) #elif defined(__PSL1GHT__) || defined(__PS3__)
sysFSDirent *entry = (sysFSDirent*)&rdir->entry; sysFSDirent *entry = (sysFSDirent*)&rdir->entry;
return (entry->d_type == FS_TYPE_DIR); return (entry->d_type == FS_TYPE_DIR);
#elif defined(ORBIS)
const struct dirent *entry = &rdir->entry;
if (entry->d_type == DT_DIR)
return true;
if (!(entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK))
return false;
#else #else
struct stat buf; struct stat buf;
char path[PATH_MAX_LENGTH]; char path[PATH_MAX_LENGTH];
@ -1202,7 +1301,8 @@ bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *rdir)
return false; return false;
#endif #endif
/* dirent struct doesn't have d_type, do it the slow way ... */ /* dirent struct doesn't have d_type, do it the slow way ... */
fill_pathname_join_special(path, rdir->orig_path, retro_vfs_dirent_get_name_impl(rdir), sizeof(path)); path[0] = '\0';
fill_pathname_join(path, rdir->orig_path, retro_vfs_dirent_get_name_impl(rdir), sizeof(path));
if (stat(path, &buf) < 0) if (stat(path, &buf) < 0)
return false; return false;
return S_ISDIR(buf.st_mode); return S_ISDIR(buf.st_mode);
@ -1217,10 +1317,12 @@ int retro_vfs_closedir_impl(libretro_vfs_implementation_dir *rdir)
#if defined(_WIN32) #if defined(_WIN32)
if (rdir->directory != INVALID_HANDLE_VALUE) if (rdir->directory != INVALID_HANDLE_VALUE)
FindClose(rdir->directory); FindClose(rdir->directory);
#elif defined(VITA) #elif defined(VITA) || defined(PSP)
sceIoDclose(rdir->directory); sceIoDclose(rdir->directory);
#elif defined(__PSL1GHT__) || defined(__PS3__) #elif defined(__PSL1GHT__) || defined(__PS3__)
rdir->error = sysFsClosedir(rdir->directory); rdir->error = sysFsClosedir(rdir->directory);
#elif defined(ORBIS)
orbisDclose(rdir->directory);
#else #else
if (rdir->directory) if (rdir->directory)
closedir(rdir->directory); closedir(rdir->directory);