Update libretro-common
This commit is contained in:
		
							parent
							
								
									f0387156b8
								
							
						
					
					
						commit
						175f00d527
					
				
					 19 changed files with 1679 additions and 951 deletions
				
			
		| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
 | 
			
		||||
LIBRETRO_COMM_DIR := $(CORE_DIR)/libretro/libretro-common/
 | 
			
		||||
LIBRETRO_COMM_DIR := $(CORE_DIR)/libretro/libretro-common
 | 
			
		||||
INCFLAGS   := -I$(CORE_DIR)/libretro -I$(LIBRETRO_COMM_DIR)/include  -I$(CORE_DIR)/
 | 
			
		||||
 | 
			
		||||
SOURCES_ASM := $(CORE_DIR)/bios_data.S
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -60,10 +60,3 @@ size_t strlcat(char *dest, const char *source, size_t size)
 | 
			
		|||
   return len + strlcpy(dest, source, size);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
char *strldup(const char *s, size_t n)
 | 
			
		||||
{
 | 
			
		||||
   char *dst = (char*)malloc(sizeof(char) * (n + 1));
 | 
			
		||||
   strlcpy(dst, s, n);
 | 
			
		||||
   return dst;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,27 +37,28 @@
 | 
			
		|||
void *fopen_utf8(const char * filename, const char * mode)
 | 
			
		||||
{
 | 
			
		||||
#if defined(LEGACY_WIN32)
 | 
			
		||||
   FILE             *ret = NULL;
 | 
			
		||||
   char * filename_local = utf8_to_local_string_alloc(filename);
 | 
			
		||||
 | 
			
		||||
   if (!filename_local)
 | 
			
		||||
      return NULL;
 | 
			
		||||
   ret = fopen(filename_local, mode);
 | 
			
		||||
   if (filename_local)
 | 
			
		||||
   {
 | 
			
		||||
      FILE *ret          = fopen(filename_local, mode);
 | 
			
		||||
      free(filename_local);
 | 
			
		||||
   return ret;
 | 
			
		||||
      return ret;
 | 
			
		||||
   }
 | 
			
		||||
#else
 | 
			
		||||
   wchar_t * filename_w = utf8_to_utf16_string_alloc(filename);
 | 
			
		||||
   wchar_t * mode_w = utf8_to_utf16_string_alloc(mode);
 | 
			
		||||
   FILE* ret = NULL;
 | 
			
		||||
 | 
			
		||||
   if (filename_w && mode_w)
 | 
			
		||||
      ret = _wfopen(filename_w, mode_w);
 | 
			
		||||
   wchar_t * filename_w  = utf8_to_utf16_string_alloc(filename);
 | 
			
		||||
   if (filename_w)
 | 
			
		||||
   {
 | 
			
		||||
      FILE    *ret       = NULL;
 | 
			
		||||
      wchar_t *mode_w    = utf8_to_utf16_string_alloc(mode);
 | 
			
		||||
      if (mode_w)
 | 
			
		||||
      {
 | 
			
		||||
         ret             = _wfopen(filename_w, mode_w);
 | 
			
		||||
         free(mode_w);
 | 
			
		||||
      }
 | 
			
		||||
      free(filename_w);
 | 
			
		||||
   if (mode_w)
 | 
			
		||||
      free(mode_w);
 | 
			
		||||
   return ret;
 | 
			
		||||
      return ret;
 | 
			
		||||
   }
 | 
			
		||||
#endif
 | 
			
		||||
   return NULL;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,9 +51,12 @@ static unsigned leading_ones(uint8_t c)
 | 
			
		|||
   return ones;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Simple implementation. Assumes the sequence is
 | 
			
		||||
 * properly synchronized and terminated. */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * utf8_conv_utf32:
 | 
			
		||||
 *
 | 
			
		||||
 * Simple implementation. Assumes the sequence is
 | 
			
		||||
 * properly synchronized and terminated.
 | 
			
		||||
 **/
 | 
			
		||||
size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
 | 
			
		||||
      const char *in, size_t in_size)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -79,7 +82,7 @@ size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
 | 
			
		|||
      for (i = 0; i < extra; i++, in++, shift -= 6)
 | 
			
		||||
         c |= (*in & 0x3f) << shift;
 | 
			
		||||
 | 
			
		||||
      *out++ = c;
 | 
			
		||||
      *out++   = c;
 | 
			
		||||
      in_size -= 1 + extra;
 | 
			
		||||
      out_chars--;
 | 
			
		||||
      ret++;
 | 
			
		||||
| 
						 | 
				
			
			@ -88,6 +91,11 @@ size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
 | 
			
		|||
   return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * utf16_conv_utf8:
 | 
			
		||||
 *
 | 
			
		||||
 * Leaf function.
 | 
			
		||||
 **/
 | 
			
		||||
bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
 | 
			
		||||
     const uint16_t *in, size_t in_size)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -148,16 +156,20 @@ bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
 | 
			
		|||
   return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Acts mostly like strlcpy.
 | 
			
		||||
/**
 | 
			
		||||
 * utf8cpy:
 | 
			
		||||
 *
 | 
			
		||||
 * Acts mostly like strlcpy.
 | 
			
		||||
 *
 | 
			
		||||
 * Copies the given number of UTF-8 characters,
 | 
			
		||||
 * but at most d_len bytes.
 | 
			
		||||
 * but at most @d_len bytes.
 | 
			
		||||
 *
 | 
			
		||||
 * Always NULL terminates.
 | 
			
		||||
 * Does not copy half a character.
 | 
			
		||||
 * Always NULL terminates. Does not copy half a character.
 | 
			
		||||
 * @s is assumed valid UTF-8.
 | 
			
		||||
 * Use only if @chars is considerably less than @d_len. 
 | 
			
		||||
 *
 | 
			
		||||
 * Returns number of bytes. 's' is assumed valid UTF-8.
 | 
			
		||||
 * Use only if 'chars' is considerably less than 'd_len'. */
 | 
			
		||||
 * @return Number of bytes. 
 | 
			
		||||
 **/
 | 
			
		||||
size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars)
 | 
			
		||||
{
 | 
			
		||||
   const uint8_t *sb     = (const uint8_t*)s;
 | 
			
		||||
| 
						 | 
				
			
			@ -186,6 +198,11 @@ size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars)
 | 
			
		|||
   return sb-sb_org;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * utf8skip:
 | 
			
		||||
 *
 | 
			
		||||
 * Leaf function
 | 
			
		||||
 **/
 | 
			
		||||
const char *utf8skip(const char *str, size_t chars)
 | 
			
		||||
{
 | 
			
		||||
   const uint8_t *strb = (const uint8_t*)str;
 | 
			
		||||
| 
						 | 
				
			
			@ -204,6 +221,11 @@ const char *utf8skip(const char *str, size_t chars)
 | 
			
		|||
   return (const char*)strb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * utf8len:
 | 
			
		||||
 *
 | 
			
		||||
 * Leaf function.
 | 
			
		||||
 **/
 | 
			
		||||
size_t utf8len(const char *string)
 | 
			
		||||
{
 | 
			
		||||
   size_t ret = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -220,7 +242,15 @@ size_t utf8len(const char *string)
 | 
			
		|||
   return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Does not validate the input, returns garbage if it's not UTF-8. */
 | 
			
		||||
/** 
 | 
			
		||||
 * utf8_walk:
 | 
			
		||||
 *
 | 
			
		||||
 * Does not validate the input.
 | 
			
		||||
 *
 | 
			
		||||
 * Leaf function.
 | 
			
		||||
 *
 | 
			
		||||
 * @return Returns garbage if it's not UTF-8.
 | 
			
		||||
 **/
 | 
			
		||||
uint32_t utf8_walk(const char **string)
 | 
			
		||||
{
 | 
			
		||||
   uint8_t first = UTF8_WALKBYTE(string);
 | 
			
		||||
| 
						 | 
				
			
			@ -248,24 +278,23 @@ static bool utf16_to_char(uint8_t **utf_data,
 | 
			
		|||
      size_t *dest_len, const uint16_t *in)
 | 
			
		||||
{
 | 
			
		||||
   unsigned len    = 0;
 | 
			
		||||
 | 
			
		||||
   while (in[len] != '\0')
 | 
			
		||||
      len++;
 | 
			
		||||
 | 
			
		||||
   utf16_conv_utf8(NULL, dest_len, in, len);
 | 
			
		||||
   *dest_len  += 1;
 | 
			
		||||
   *utf_data   = (uint8_t*)malloc(*dest_len);
 | 
			
		||||
   if (*utf_data == 0)
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
   return utf16_conv_utf8(*utf_data, dest_len, in, len);
 | 
			
		||||
   if ((*utf_data = (uint8_t*)malloc(*dest_len)) != 0)
 | 
			
		||||
      return utf16_conv_utf8(*utf_data, dest_len, in, len);
 | 
			
		||||
   return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * utf16_to_char_string:
 | 
			
		||||
 **/
 | 
			
		||||
bool utf16_to_char_string(const uint16_t *in, char *s, size_t len)
 | 
			
		||||
{
 | 
			
		||||
   size_t     dest_len  = 0;
 | 
			
		||||
   uint8_t *utf16_data  = NULL;
 | 
			
		||||
   bool            ret  = utf16_to_char(&utf16_data, &dest_len, in);
 | 
			
		||||
   size_t     dest_len     = 0;
 | 
			
		||||
   uint8_t *utf16_data     = NULL;
 | 
			
		||||
   bool            ret     = utf16_to_char(&utf16_data, &dest_len, in);
 | 
			
		||||
 | 
			
		||||
   if (ret)
 | 
			
		||||
   {
 | 
			
		||||
| 
						 | 
				
			
			@ -274,13 +303,17 @@ bool utf16_to_char_string(const uint16_t *in, char *s, size_t len)
 | 
			
		|||
   }
 | 
			
		||||
 | 
			
		||||
   free(utf16_data);
 | 
			
		||||
   utf16_data = NULL;
 | 
			
		||||
   utf16_data              = NULL;
 | 
			
		||||
 | 
			
		||||
   return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE)
 | 
			
		||||
/* Returned pointer MUST be freed by the caller if non-NULL. */
 | 
			
		||||
/**
 | 
			
		||||
 * mb_to_mb_string_alloc:
 | 
			
		||||
 *
 | 
			
		||||
 * @return Returned pointer MUST be freed by the caller if non-NULL.
 | 
			
		||||
 **/
 | 
			
		||||
static char *mb_to_mb_string_alloc(const char *str,
 | 
			
		||||
      enum CodePage cp_in, enum CodePage cp_out)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -300,10 +333,8 @@ static char *mb_to_mb_string_alloc(const char *str,
 | 
			
		|||
   if (!path_buf_wide_len)
 | 
			
		||||
      return strdup(str);
 | 
			
		||||
 | 
			
		||||
   path_buf_wide = (wchar_t*)
 | 
			
		||||
      calloc(path_buf_wide_len + sizeof(wchar_t), sizeof(wchar_t));
 | 
			
		||||
 | 
			
		||||
   if (path_buf_wide)
 | 
			
		||||
   if ((path_buf_wide = (wchar_t*)
 | 
			
		||||
      calloc(path_buf_wide_len + sizeof(wchar_t), sizeof(wchar_t))))
 | 
			
		||||
   {
 | 
			
		||||
      MultiByteToWideChar(cp_in, 0,
 | 
			
		||||
            str, -1, path_buf_wide, path_buf_wide_len);
 | 
			
		||||
| 
						 | 
				
			
			@ -347,45 +378,49 @@ static char *mb_to_mb_string_alloc(const char *str,
 | 
			
		|||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Returned pointer MUST be freed by the caller if non-NULL. */
 | 
			
		||||
/**
 | 
			
		||||
 * utf8_to_local_string_alloc:
 | 
			
		||||
 *
 | 
			
		||||
 * @return Returned pointer MUST be freed by the caller if non-NULL.
 | 
			
		||||
 **/
 | 
			
		||||
char* utf8_to_local_string_alloc(const char *str)
 | 
			
		||||
{
 | 
			
		||||
   if (str && *str)
 | 
			
		||||
   {
 | 
			
		||||
#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE)
 | 
			
		||||
      return mb_to_mb_string_alloc(str, CODEPAGE_UTF8, CODEPAGE_LOCAL);
 | 
			
		||||
#else
 | 
			
		||||
      /* assume string needs no modification if not on Windows */
 | 
			
		||||
      return strdup(str);
 | 
			
		||||
      return strdup(str); /* Assume string needs no modification if not on Windows */
 | 
			
		||||
#endif
 | 
			
		||||
   }
 | 
			
		||||
   return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returned pointer MUST be freed by the caller if non-NULL. */
 | 
			
		||||
char* local_to_utf8_string_alloc(const char *str)
 | 
			
		||||
/**
 | 
			
		||||
 * local_to_utf8_string_alloc:
 | 
			
		||||
 *
 | 
			
		||||
 * @return Returned pointer MUST be freed by the caller if non-NULL.
 | 
			
		||||
 **/
 | 
			
		||||
char *local_to_utf8_string_alloc(const char *str)
 | 
			
		||||
{
 | 
			
		||||
   if (str && *str)
 | 
			
		||||
   {
 | 
			
		||||
	if (str && *str)
 | 
			
		||||
#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE)
 | 
			
		||||
      return mb_to_mb_string_alloc(str, CODEPAGE_LOCAL, CODEPAGE_UTF8);
 | 
			
		||||
		return mb_to_mb_string_alloc(str, CODEPAGE_LOCAL, CODEPAGE_UTF8);
 | 
			
		||||
#else
 | 
			
		||||
      /* assume string needs no modification if not on Windows */
 | 
			
		||||
      return strdup(str);
 | 
			
		||||
      return strdup(str); /* Assume string needs no modification if not on Windows */
 | 
			
		||||
#endif
 | 
			
		||||
   }
 | 
			
		||||
   return NULL;
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returned pointer MUST be freed by the caller if non-NULL. */
 | 
			
		||||
/**
 | 
			
		||||
 * utf8_to_utf16_string_alloc:
 | 
			
		||||
 * 
 | 
			
		||||
 * @return Returned pointer MUST be freed by the caller if non-NULL.
 | 
			
		||||
 **/
 | 
			
		||||
wchar_t* utf8_to_utf16_string_alloc(const char *str)
 | 
			
		||||
{
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
   int len        = 0;
 | 
			
		||||
   int out_len    = 0;
 | 
			
		||||
#else
 | 
			
		||||
   size_t len     = 0;
 | 
			
		||||
   size_t out_len = 0;
 | 
			
		||||
#endif
 | 
			
		||||
   wchar_t *buf   = NULL;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -393,63 +428,55 @@ wchar_t* utf8_to_utf16_string_alloc(const char *str)
 | 
			
		|||
      return NULL;
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
   len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
 | 
			
		||||
 | 
			
		||||
   if (len)
 | 
			
		||||
   if ((len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0)))
 | 
			
		||||
   {
 | 
			
		||||
      buf = (wchar_t*)calloc(len, sizeof(wchar_t));
 | 
			
		||||
 | 
			
		||||
      if (!buf)
 | 
			
		||||
      if (!(buf = (wchar_t*)calloc(len, sizeof(wchar_t))))
 | 
			
		||||
         return NULL;
 | 
			
		||||
 | 
			
		||||
      out_len = MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, len);
 | 
			
		||||
      if ((MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, len)) < 0)
 | 
			
		||||
      {
 | 
			
		||||
         free(buf);
 | 
			
		||||
         return NULL;
 | 
			
		||||
      }
 | 
			
		||||
   }
 | 
			
		||||
   else
 | 
			
		||||
   {
 | 
			
		||||
      /* fallback to ANSI codepage instead */
 | 
			
		||||
      len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
 | 
			
		||||
 | 
			
		||||
      if (len)
 | 
			
		||||
      /* Fallback to ANSI codepage instead */
 | 
			
		||||
      if ((len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0)))
 | 
			
		||||
      {
 | 
			
		||||
         buf = (wchar_t*)calloc(len, sizeof(wchar_t));
 | 
			
		||||
 | 
			
		||||
         if (!buf)
 | 
			
		||||
         if (!(buf = (wchar_t*)calloc(len, sizeof(wchar_t))))
 | 
			
		||||
            return NULL;
 | 
			
		||||
 | 
			
		||||
         out_len = MultiByteToWideChar(CP_ACP, 0, str, -1, buf, len);
 | 
			
		||||
         if ((MultiByteToWideChar(CP_ACP, 0, str, -1, buf, len)) < 0)
 | 
			
		||||
         {
 | 
			
		||||
            free(buf);
 | 
			
		||||
            return NULL;
 | 
			
		||||
         }
 | 
			
		||||
      }
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   if (out_len < 0)
 | 
			
		||||
   {
 | 
			
		||||
      free(buf);
 | 
			
		||||
      return NULL;
 | 
			
		||||
   }
 | 
			
		||||
#else
 | 
			
		||||
   /* NOTE: For now, assume non-Windows platforms' locale is already UTF-8. */
 | 
			
		||||
   len = mbstowcs(NULL, str, 0) + 1;
 | 
			
		||||
 | 
			
		||||
   if (len)
 | 
			
		||||
   if ((len = mbstowcs(NULL, str, 0) + 1))
 | 
			
		||||
   {
 | 
			
		||||
      buf = (wchar_t*)calloc(len, sizeof(wchar_t));
 | 
			
		||||
 | 
			
		||||
      if (!buf)
 | 
			
		||||
      if (!(buf = (wchar_t*)calloc(len, sizeof(wchar_t))))
 | 
			
		||||
         return NULL;
 | 
			
		||||
 | 
			
		||||
      out_len = mbstowcs(buf, str, len);
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   if (out_len == (size_t)-1)
 | 
			
		||||
   {
 | 
			
		||||
      free(buf);
 | 
			
		||||
      return NULL;
 | 
			
		||||
      if ((mbstowcs(buf, str, len)) == (size_t)-1)
 | 
			
		||||
      {
 | 
			
		||||
         free(buf);
 | 
			
		||||
         return NULL;
 | 
			
		||||
      }
 | 
			
		||||
   }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
   return buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returned pointer MUST be freed by the caller if non-NULL. */
 | 
			
		||||
/**
 | 
			
		||||
 * utf16_to_utf8_string_alloc:
 | 
			
		||||
 *
 | 
			
		||||
 * @return Returned pointer MUST be freed by the caller if non-NULL.
 | 
			
		||||
 **/
 | 
			
		||||
char* utf16_to_utf8_string_alloc(const wchar_t *str)
 | 
			
		||||
{
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
| 
						 | 
				
			
			@ -465,20 +492,17 @@ char* utf16_to_utf8_string_alloc(const wchar_t *str)
 | 
			
		|||
#ifdef _WIN32
 | 
			
		||||
   {
 | 
			
		||||
      UINT code_page = CP_UTF8;
 | 
			
		||||
      len            = WideCharToMultiByte(code_page,
 | 
			
		||||
            0, str, -1, NULL, 0, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
      /* fallback to ANSI codepage instead */
 | 
			
		||||
      if (!len)
 | 
			
		||||
      if (!(len = WideCharToMultiByte(code_page,
 | 
			
		||||
            0, str, -1, NULL, 0, NULL, NULL)))
 | 
			
		||||
      {
 | 
			
		||||
         code_page   = CP_ACP;
 | 
			
		||||
         len         = WideCharToMultiByte(code_page,
 | 
			
		||||
               0, str, -1, NULL, 0, NULL, NULL);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      buf = (char*)calloc(len, sizeof(char));
 | 
			
		||||
 | 
			
		||||
      if (!buf)
 | 
			
		||||
      if (!(buf = (char*)calloc(len, sizeof(char))))
 | 
			
		||||
         return NULL;
 | 
			
		||||
 | 
			
		||||
      if (WideCharToMultiByte(code_page,
 | 
			
		||||
| 
						 | 
				
			
			@ -491,13 +515,9 @@ char* utf16_to_utf8_string_alloc(const wchar_t *str)
 | 
			
		|||
#else
 | 
			
		||||
   /* NOTE: For now, assume non-Windows platforms' 
 | 
			
		||||
    * locale is already UTF-8. */
 | 
			
		||||
   len = wcstombs(NULL, str, 0) + 1;
 | 
			
		||||
 | 
			
		||||
   if (len)
 | 
			
		||||
   if ((len = wcstombs(NULL, str, 0) + 1))
 | 
			
		||||
   {
 | 
			
		||||
      buf = (char*)calloc(len, sizeof(char));
 | 
			
		||||
 | 
			
		||||
      if (!buf)
 | 
			
		||||
      if (!(buf = (char*)calloc(len, sizeof(char))))
 | 
			
		||||
         return NULL;
 | 
			
		||||
 | 
			
		||||
      if (wcstombs(buf, str, len) == (size_t)-1)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -24,13 +24,11 @@
 | 
			
		|||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
 | 
			
		||||
#include <boolean.h>
 | 
			
		||||
#include <file/file_path.h>
 | 
			
		||||
#include <retro_assert.h>
 | 
			
		||||
#include <compat/strl.h>
 | 
			
		||||
#include <compat/posix_string.h>
 | 
			
		||||
#include <retro_miscellaneous.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -74,7 +72,7 @@ int path_stat(const char *path)
 | 
			
		|||
 *
 | 
			
		||||
 * Checks if path is a directory.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: true (1) if path is a directory, otherwise false (0).
 | 
			
		||||
 * @return true if path is a directory, otherwise false.
 | 
			
		||||
 */
 | 
			
		||||
bool path_is_directory(const char *path)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -105,8 +103,10 @@ int32_t path_get_size(const char *path)
 | 
			
		|||
 * @dir                : directory
 | 
			
		||||
 *
 | 
			
		||||
 * Create directory on filesystem.
 | 
			
		||||
 * 
 | 
			
		||||
 * Recursive function.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: true (1) if directory could be created, otherwise false (0).
 | 
			
		||||
 * @return true if directory could be created, otherwise false.
 | 
			
		||||
 **/
 | 
			
		||||
bool path_mkdir(const char *dir)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -118,12 +118,10 @@ bool path_mkdir(const char *dir)
 | 
			
		|||
 | 
			
		||||
   /* Use heap. Real chance of stack 
 | 
			
		||||
    * overflow if we recurse too hard. */
 | 
			
		||||
   basedir            = strdup(dir);
 | 
			
		||||
   if (!(basedir = strdup(dir)))
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
   if (!basedir)
 | 
			
		||||
	   return false;
 | 
			
		||||
 | 
			
		||||
   path_parent_dir(basedir);
 | 
			
		||||
   path_parent_dir(basedir, strlen(basedir));
 | 
			
		||||
 | 
			
		||||
   if (!*basedir || !strcmp(basedir, dir))
 | 
			
		||||
   {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,29 +38,99 @@ enum CodePage
 | 
			
		|||
   CODEPAGE_UTF8  = 65001 /* CP_UTF8 */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * utf8_conv_utf32:
 | 
			
		||||
 *
 | 
			
		||||
 * Simple implementation. Assumes the sequence is
 | 
			
		||||
 * properly synchronized and terminated.
 | 
			
		||||
 **/
 | 
			
		||||
size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
 | 
			
		||||
      const char *in, size_t in_size);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * utf16_conv_utf8:
 | 
			
		||||
 *
 | 
			
		||||
 * Leaf function.
 | 
			
		||||
 **/
 | 
			
		||||
bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
 | 
			
		||||
      const uint16_t *in, size_t in_size);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * utf8len:
 | 
			
		||||
 *
 | 
			
		||||
 * Leaf function.
 | 
			
		||||
 **/
 | 
			
		||||
size_t utf8len(const char *string);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * utf8cpy:
 | 
			
		||||
 *
 | 
			
		||||
 * Acts mostly like strlcpy.
 | 
			
		||||
 *
 | 
			
		||||
 * Copies the given number of UTF-8 characters,
 | 
			
		||||
 * but at most @d_len bytes.
 | 
			
		||||
 *
 | 
			
		||||
 * Always NULL terminates. Does not copy half a character.
 | 
			
		||||
 * @s is assumed valid UTF-8.
 | 
			
		||||
 * Use only if @chars is considerably less than @d_len. 
 | 
			
		||||
 *
 | 
			
		||||
 * Hidden non-leaf function cost:
 | 
			
		||||
 * - Calls memcpy
 | 
			
		||||
 *
 | 
			
		||||
 * @return Number of bytes. 
 | 
			
		||||
 **/
 | 
			
		||||
size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * utf8skip:
 | 
			
		||||
 *
 | 
			
		||||
 * Leaf function
 | 
			
		||||
 **/
 | 
			
		||||
const char *utf8skip(const char *str, size_t chars);
 | 
			
		||||
 | 
			
		||||
/** 
 | 
			
		||||
 * utf8_walk:
 | 
			
		||||
 *
 | 
			
		||||
 * Does not validate the input.
 | 
			
		||||
 *
 | 
			
		||||
 * Leaf function.
 | 
			
		||||
 *
 | 
			
		||||
 * @return Returns garbage if it's not UTF-8.
 | 
			
		||||
 **/
 | 
			
		||||
uint32_t utf8_walk(const char **string);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * utf16_to_char_string:
 | 
			
		||||
 **/
 | 
			
		||||
bool utf16_to_char_string(const uint16_t *in, char *s, size_t len);
 | 
			
		||||
 | 
			
		||||
char* utf8_to_local_string_alloc(const char *str);
 | 
			
		||||
/**
 | 
			
		||||
 * utf8_to_local_string_alloc:
 | 
			
		||||
 *
 | 
			
		||||
 * @return Returned pointer MUST be freed by the caller if non-NULL.
 | 
			
		||||
 **/
 | 
			
		||||
char *utf8_to_local_string_alloc(const char *str);
 | 
			
		||||
 | 
			
		||||
char* local_to_utf8_string_alloc(const char *str);
 | 
			
		||||
/**
 | 
			
		||||
 * local_to_utf8_string_alloc:
 | 
			
		||||
 *
 | 
			
		||||
 * @return Returned pointer MUST be freed by the caller if non-NULL.
 | 
			
		||||
 **/
 | 
			
		||||
char *local_to_utf8_string_alloc(const char *str);
 | 
			
		||||
 | 
			
		||||
wchar_t* utf8_to_utf16_string_alloc(const char *str);
 | 
			
		||||
/**
 | 
			
		||||
 * utf8_to_utf16_string_alloc:
 | 
			
		||||
 * 
 | 
			
		||||
 * @return Returned pointer MUST be freed by the caller if non-NULL.
 | 
			
		||||
 **/
 | 
			
		||||
wchar_t *utf8_to_utf16_string_alloc(const char *str);
 | 
			
		||||
 | 
			
		||||
char* utf16_to_utf8_string_alloc(const wchar_t *str);
 | 
			
		||||
/**
 | 
			
		||||
 * utf16_to_utf8_string_alloc:
 | 
			
		||||
 *
 | 
			
		||||
 * @return Returned pointer MUST be freed by the caller if non-NULL.
 | 
			
		||||
 **/
 | 
			
		||||
char *utf16_to_utf8_string_alloc(const wchar_t *str);
 | 
			
		||||
 | 
			
		||||
RETRO_END_DECLS
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,6 +51,28 @@ enum
 | 
			
		|||
   RARCH_FILE_UNSUPPORTED
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct path_linked_list
 | 
			
		||||
{
 | 
			
		||||
   char *path;
 | 
			
		||||
   struct path_linked_list *next;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create a new linked list with one item in it
 | 
			
		||||
 * The path on this item will be set to NULL
 | 
			
		||||
**/
 | 
			
		||||
struct path_linked_list* path_linked_list_new(void);
 | 
			
		||||
 | 
			
		||||
/* Free the entire linked list */
 | 
			
		||||
void path_linked_list_free(struct path_linked_list *in_path_linked_list);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Add a node to the linked list with this path
 | 
			
		||||
 * If the first node's path if it's not yet set, 
 | 
			
		||||
 * set this instead
 | 
			
		||||
**/
 | 
			
		||||
void path_linked_list_add_path(struct path_linked_list *in_path_linked_list, char *path);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * path_is_compressed_file:
 | 
			
		||||
 * @path               : path
 | 
			
		||||
| 
						 | 
				
			
			@ -81,12 +103,12 @@ bool path_is_compressed_file(const char *path);
 | 
			
		|||
 * path_get_archive_delim:
 | 
			
		||||
 * @path               : path
 | 
			
		||||
 *
 | 
			
		||||
 * Gets delimiter of an archive file. Only the first '#'
 | 
			
		||||
 * Find delimiter of an archive file. Only the first '#'
 | 
			
		||||
 * after a compression extension is considered.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: pointer to the delimiter in the path if it contains
 | 
			
		||||
 * a compressed file, otherwise NULL.
 | 
			
		||||
 */
 | 
			
		||||
 * @return pointer to the delimiter in the path if it contains
 | 
			
		||||
 * a path inside a compressed file, otherwise NULL.
 | 
			
		||||
 **/
 | 
			
		||||
const char *path_get_archive_delim(const char *path);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -96,10 +118,28 @@ const char *path_get_archive_delim(const char *path);
 | 
			
		|||
 * Gets extension of file. Only '.'s
 | 
			
		||||
 * after the last slash are considered.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: extension part from the path.
 | 
			
		||||
 */
 | 
			
		||||
 * Hidden non-leaf function cost:
 | 
			
		||||
 * - calls string_is_empty()
 | 
			
		||||
 * - calls strrchr
 | 
			
		||||
 *
 | 
			
		||||
 * @return extension part from the path.
 | 
			
		||||
 **/
 | 
			
		||||
const char *path_get_extension(const char *path);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * path_get_extension_mutable:
 | 
			
		||||
 * @path               : path
 | 
			
		||||
 *
 | 
			
		||||
 * Specialized version of path_get_extension(). Return
 | 
			
		||||
 * value is mutable.
 | 
			
		||||
 *
 | 
			
		||||
 * Gets extension of file. Only '.'s
 | 
			
		||||
 * after the last slash are considered.
 | 
			
		||||
 *
 | 
			
		||||
 * @return extension part from the path.
 | 
			
		||||
 **/
 | 
			
		||||
char *path_get_extension_mutable(const char *path);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * path_remove_extension:
 | 
			
		||||
 * @path               : path
 | 
			
		||||
| 
						 | 
				
			
			@ -108,7 +148,10 @@ const char *path_get_extension(const char *path);
 | 
			
		|||
 * text after and including the last '.'.
 | 
			
		||||
 * Only '.'s after the last slash are considered.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns:
 | 
			
		||||
 * Hidden non-leaf function cost:
 | 
			
		||||
 * - calls strrchr
 | 
			
		||||
 *
 | 
			
		||||
 * @return
 | 
			
		||||
 * 1) If path has an extension, returns path with the
 | 
			
		||||
 *    extension removed.
 | 
			
		||||
 * 2) If there is no extension, returns NULL.
 | 
			
		||||
| 
						 | 
				
			
			@ -122,9 +165,26 @@ char *path_remove_extension(char *path);
 | 
			
		|||
 *
 | 
			
		||||
 * Get basename from @path.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: basename from path.
 | 
			
		||||
 * Hidden non-leaf function cost:
 | 
			
		||||
 * - Calls path_get_archive_delim()
 | 
			
		||||
 *   - can call find_last_slash() once if it returns NULL
 | 
			
		||||
 *
 | 
			
		||||
 * @return basename from path.
 | 
			
		||||
 **/
 | 
			
		||||
const char *path_basename(const char *path);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * path_basename_nocompression:
 | 
			
		||||
 * @path               : path
 | 
			
		||||
 *
 | 
			
		||||
 * Specialized version of path_basename().
 | 
			
		||||
 * Get basename from @path.
 | 
			
		||||
 *
 | 
			
		||||
 * Hidden non-leaf function cost:
 | 
			
		||||
 * - Calls find_last_slash()
 | 
			
		||||
 *
 | 
			
		||||
 * @return basename from path.
 | 
			
		||||
 **/
 | 
			
		||||
const char *path_basename_nocompression(const char *path);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -139,12 +199,13 @@ void path_basedir(char *path);
 | 
			
		|||
/**
 | 
			
		||||
 * path_parent_dir:
 | 
			
		||||
 * @path               : path
 | 
			
		||||
 * @len                : length of @path
 | 
			
		||||
 *
 | 
			
		||||
 * Extracts parent directory by mutating path.
 | 
			
		||||
 * Assumes that path is a directory. Keeps trailing '/'.
 | 
			
		||||
 * If the path was already at the root directory, returns empty string
 | 
			
		||||
 **/
 | 
			
		||||
void path_parent_dir(char *path);
 | 
			
		||||
void path_parent_dir(char *path, size_t len);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * path_resolve_realpath:
 | 
			
		||||
| 
						 | 
				
			
			@ -156,7 +217,7 @@ void path_parent_dir(char *path);
 | 
			
		|||
 *
 | 
			
		||||
 * Relative paths are rebased on the current working dir.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: @buf if successful, NULL otherwise.
 | 
			
		||||
 * @return @buf if successful, NULL otherwise.
 | 
			
		||||
 * Note: Not implemented on consoles
 | 
			
		||||
 * Note: Symlinks are only resolved on Unix-likes
 | 
			
		||||
 * Note: The current working dir might not be what you expect,
 | 
			
		||||
| 
						 | 
				
			
			@ -178,8 +239,11 @@ char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks);
 | 
			
		|||
 * Both @path and @base are assumed to be absolute paths without "." or "..".
 | 
			
		||||
 *
 | 
			
		||||
 * E.g. path /a/b/e/f.cgp with base /a/b/c/d/ turns into ../../e/f.cgp
 | 
			
		||||
 *
 | 
			
		||||
 * @return Length of the string copied into @out
 | 
			
		||||
 **/
 | 
			
		||||
size_t path_relative_to(char *out, const char *path, const char *base, size_t size);
 | 
			
		||||
size_t path_relative_to(char *out, const char *path, const char *base,
 | 
			
		||||
      size_t size);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * path_is_absolute:
 | 
			
		||||
| 
						 | 
				
			
			@ -187,7 +251,7 @@ size_t path_relative_to(char *out, const char *path, const char *base, size_t si
 | 
			
		|||
 *
 | 
			
		||||
 * Checks if @path is an absolute path or a relative path.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: true if path is absolute, false if path is relative.
 | 
			
		||||
 * @return true if path is absolute, false if path is relative.
 | 
			
		||||
 **/
 | 
			
		||||
bool path_is_absolute(const char *path);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -211,8 +275,15 @@ bool path_is_absolute(const char *path);
 | 
			
		|||
 * out_path = "/foo/bar/baz/boo.asm"
 | 
			
		||||
 * E.g.: in_path = "/foo/bar/baz/boo.c", replace = ""     =>
 | 
			
		||||
 * out_path = "/foo/bar/baz/boo"
 | 
			
		||||
 *
 | 
			
		||||
 * Hidden non-leaf function cost:
 | 
			
		||||
 * - calls strlcpy 2x
 | 
			
		||||
 * - calls strrchr
 | 
			
		||||
 * - calls strlcat
 | 
			
		||||
 *
 | 
			
		||||
 * @return Length of the string copied into @out
 | 
			
		||||
 */
 | 
			
		||||
void fill_pathname(char *out_path, const char *in_path,
 | 
			
		||||
size_t fill_pathname(char *out_path, const char *in_path,
 | 
			
		||||
      const char *replace, size_t size);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -226,6 +297,12 @@ void fill_pathname(char *out_path, const char *in_path,
 | 
			
		|||
 *
 | 
			
		||||
 * E.g.:
 | 
			
		||||
 * out_filename = "RetroArch-{month}{day}-{Hours}{Minutes}.{@ext}"
 | 
			
		||||
 *
 | 
			
		||||
 * Hidden non-leaf function cost:
 | 
			
		||||
 * - Calls rtime_localtime()
 | 
			
		||||
 * - Calls strftime
 | 
			
		||||
 * - Calls strlcat
 | 
			
		||||
 *
 | 
			
		||||
 **/
 | 
			
		||||
size_t fill_dated_filename(char *out_filename,
 | 
			
		||||
      const char *ext, size_t size);
 | 
			
		||||
| 
						 | 
				
			
			@ -242,34 +319,33 @@ size_t fill_dated_filename(char *out_filename,
 | 
			
		|||
 *
 | 
			
		||||
 * E.g.:
 | 
			
		||||
 * out_filename = "RetroArch-{year}{month}{day}-{Hour}{Minute}{Second}.{@ext}"
 | 
			
		||||
 *
 | 
			
		||||
 * Hidden non-leaf function cost:
 | 
			
		||||
 * - Calls time
 | 
			
		||||
 * - Calls rtime_localtime()
 | 
			
		||||
 * - Calls strlcpy
 | 
			
		||||
 * - Calls string_is_empty()
 | 
			
		||||
 * - Calls strftime
 | 
			
		||||
 * - Calls strlcat at least 2x
 | 
			
		||||
 *
 | 
			
		||||
 * @return Length of the string copied into @out_path
 | 
			
		||||
 **/
 | 
			
		||||
void fill_str_dated_filename(char *out_filename,
 | 
			
		||||
size_t fill_str_dated_filename(char *out_filename,
 | 
			
		||||
      const char *in_str, const char *ext, size_t size);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * fill_pathname_noext:
 | 
			
		||||
 * @out_path           : output path
 | 
			
		||||
 * @in_path            : input  path
 | 
			
		||||
 * @replace            : what to replace
 | 
			
		||||
 * @size               : buffer size of output path
 | 
			
		||||
 *
 | 
			
		||||
 * Appends a filename extension 'replace' to 'in_path', and outputs
 | 
			
		||||
 * result in 'out_path'.
 | 
			
		||||
 *
 | 
			
		||||
 * Assumes in_path has no extension. If an extension is still
 | 
			
		||||
 * present in 'in_path', it will be ignored.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
size_t fill_pathname_noext(char *out_path, const char *in_path,
 | 
			
		||||
      const char *replace, size_t size);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * find_last_slash:
 | 
			
		||||
 * @str : input path
 | 
			
		||||
 * @str                : path
 | 
			
		||||
 * @size               : size of path
 | 
			
		||||
 *
 | 
			
		||||
 * Gets a pointer to the last slash in the input path.
 | 
			
		||||
 * Find last slash in path. Tries to find
 | 
			
		||||
 * a backslash on Windows too which takes precedence
 | 
			
		||||
 * over regular slash.
 | 
			
		||||
 | 
			
		||||
 * Hidden non-leaf function cost: 
 | 
			
		||||
 * - calls strrchr
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: a pointer to the last slash in the input path.
 | 
			
		||||
 * @return pointer to last slash/backslash found in @str.
 | 
			
		||||
 **/
 | 
			
		||||
char *find_last_slash(const char *str);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -289,6 +365,11 @@ char *find_last_slash(const char *str);
 | 
			
		|||
 *
 | 
			
		||||
 * E.g..: in_dir = "/tmp/some_dir", in_basename = "/some_content/foo.c",
 | 
			
		||||
 * replace = ".asm" => in_dir = "/tmp/some_dir/foo.c.asm"
 | 
			
		||||
 *
 | 
			
		||||
 * Hidden non-leaf function cost:
 | 
			
		||||
 * - Calls fill_pathname_slash()
 | 
			
		||||
 * - Calls path_basename()
 | 
			
		||||
 * - Calls strlcat 2x
 | 
			
		||||
 **/
 | 
			
		||||
size_t fill_pathname_dir(char *in_dir, const char *in_basename,
 | 
			
		||||
      const char *replace, size_t size);
 | 
			
		||||
| 
						 | 
				
			
			@ -300,16 +381,15 @@ size_t fill_pathname_dir(char *in_dir, const char *in_basename,
 | 
			
		|||
 * @size               : size of output path
 | 
			
		||||
 *
 | 
			
		||||
 * Copies basename of @in_path into @out_path.
 | 
			
		||||
 *
 | 
			
		||||
 * Hidden non-leaf function cost:
 | 
			
		||||
 * - Calls path_basename()
 | 
			
		||||
 * - Calls strlcpy
 | 
			
		||||
 *
 | 
			
		||||
 * @return length of the string copied into @out
 | 
			
		||||
 **/
 | 
			
		||||
size_t fill_pathname_base(char *out_path, const char *in_path, size_t size);
 | 
			
		||||
 | 
			
		||||
void fill_pathname_base_noext(char *out_dir,
 | 
			
		||||
      const char *in_path, size_t size);
 | 
			
		||||
 | 
			
		||||
size_t fill_pathname_base_ext(char *out,
 | 
			
		||||
      const char *in_path, const char *ext,
 | 
			
		||||
      size_t size);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * fill_pathname_basedir:
 | 
			
		||||
 * @out_dir            : output directory
 | 
			
		||||
| 
						 | 
				
			
			@ -319,12 +399,13 @@ size_t fill_pathname_base_ext(char *out,
 | 
			
		|||
 * Copies base directory of @in_path into @out_path.
 | 
			
		||||
 * If in_path is a path without any slashes (relative current directory),
 | 
			
		||||
 * @out_path will get path "./".
 | 
			
		||||
 *
 | 
			
		||||
 * Hidden non-leaf function cost:
 | 
			
		||||
 * - Calls strlcpy
 | 
			
		||||
 * - Calls path_basedir()
 | 
			
		||||
 **/
 | 
			
		||||
void fill_pathname_basedir(char *out_path, const char *in_path, size_t size);
 | 
			
		||||
 | 
			
		||||
void fill_pathname_basedir_noext(char *out_dir,
 | 
			
		||||
      const char *in_path, size_t size);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * fill_pathname_parent_dir_name:
 | 
			
		||||
 * @out_dir            : output directory
 | 
			
		||||
| 
						 | 
				
			
			@ -333,7 +414,13 @@ void fill_pathname_basedir_noext(char *out_dir,
 | 
			
		|||
 *
 | 
			
		||||
 * Copies only the parent directory name of @in_dir into @out_dir.
 | 
			
		||||
 * The two buffers must not overlap. Removes trailing '/'.
 | 
			
		||||
 * Returns true on success, false if a slash was not found in the path.
 | 
			
		||||
 *
 | 
			
		||||
 * Hidden non-leaf function cost:
 | 
			
		||||
 * - Calls strdup
 | 
			
		||||
 * - Calls find_last_slash() x times
 | 
			
		||||
 * - Can call strlcpy
 | 
			
		||||
 *
 | 
			
		||||
 * @return true on success, false if a slash was not found in the path.
 | 
			
		||||
 **/
 | 
			
		||||
bool fill_pathname_parent_dir_name(char *out_dir,
 | 
			
		||||
      const char *in_dir, size_t size);
 | 
			
		||||
| 
						 | 
				
			
			@ -347,6 +434,11 @@ bool fill_pathname_parent_dir_name(char *out_dir,
 | 
			
		|||
 * Copies parent directory of @in_dir into @out_dir.
 | 
			
		||||
 * Assumes @in_dir is a directory. Keeps trailing '/'.
 | 
			
		||||
 * If the path was already at the root directory, @out_dir will be an empty string.
 | 
			
		||||
 *
 | 
			
		||||
 * Hidden non-leaf function cost:
 | 
			
		||||
 * - Can call strlcpy if (@out_dir != @in_dir)
 | 
			
		||||
 * - Calls strlen if (@out_dir == @in_dir)
 | 
			
		||||
 * - Calls path_parent_dir()
 | 
			
		||||
 **/
 | 
			
		||||
void fill_pathname_parent_dir(char *out_dir,
 | 
			
		||||
      const char *in_dir, size_t size);
 | 
			
		||||
| 
						 | 
				
			
			@ -374,30 +466,53 @@ void fill_pathname_resolve_relative(char *out_path, const char *in_refpath,
 | 
			
		|||
 * @size               : size of output path
 | 
			
		||||
 *
 | 
			
		||||
 * Joins a directory (@dir) and path (@path) together.
 | 
			
		||||
 * Makes sure not to get  two consecutive slashes
 | 
			
		||||
 * Makes sure not to get two consecutive slashes
 | 
			
		||||
 * between directory and path.
 | 
			
		||||
 * 
 | 
			
		||||
 * Hidden non-leaf function cost: 
 | 
			
		||||
 * - calls strlcpy
 | 
			
		||||
 * - calls fill_pathname_slash()
 | 
			
		||||
 * - calls strlcat
 | 
			
		||||
 *
 | 
			
		||||
 * Deprecated. Use fill_pathname_join_special() instead
 | 
			
		||||
 * if you can ensure @dir != @out_path
 | 
			
		||||
 *
 | 
			
		||||
 * @return Length of the string copied into @out_path
 | 
			
		||||
 **/
 | 
			
		||||
size_t fill_pathname_join(char *out_path, const char *dir,
 | 
			
		||||
      const char *path, size_t size);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * fill_pathname_join_special:
 | 
			
		||||
 * @out_path           : output path
 | 
			
		||||
 * @dir                : directory. Cannot be identical to @out_path
 | 
			
		||||
 * @path               : path
 | 
			
		||||
 * @size               : size of output path
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 * Specialized version of fill_pathname_join.
 | 
			
		||||
 * Unlike fill_pathname_join(),
 | 
			
		||||
 * @dir and @out_path CANNOT be identical.
 | 
			
		||||
 *
 | 
			
		||||
 * Joins a directory (@dir) and path (@path) together.
 | 
			
		||||
 * Makes sure not to get two consecutive slashes
 | 
			
		||||
 * between directory and path.
 | 
			
		||||
 *
 | 
			
		||||
 * Hidden non-leaf function cost: 
 | 
			
		||||
 * - calls strlcpy
 | 
			
		||||
 * - calls find_last_slash()
 | 
			
		||||
 * - calls strlcat
 | 
			
		||||
 *
 | 
			
		||||
 * @return Length of the string copied into @out_path
 | 
			
		||||
 **/
 | 
			
		||||
size_t fill_pathname_join_special(char *out_path,
 | 
			
		||||
      const char *dir, const char *path, size_t size);
 | 
			
		||||
 | 
			
		||||
size_t fill_pathname_join_special_ext(char *out_path,
 | 
			
		||||
      const char *dir,  const char *path,
 | 
			
		||||
      const char *last, const char *ext,
 | 
			
		||||
      size_t size);
 | 
			
		||||
 | 
			
		||||
size_t fill_pathname_join_concat_noext(char *out_path,
 | 
			
		||||
      const char *dir, const char *path,
 | 
			
		||||
      const char *concat,
 | 
			
		||||
      size_t size);
 | 
			
		||||
 | 
			
		||||
size_t fill_pathname_join_concat(char *out_path,
 | 
			
		||||
      const char *dir, const char *path,
 | 
			
		||||
      const char *concat,
 | 
			
		||||
      size_t size);
 | 
			
		||||
 | 
			
		||||
void fill_pathname_join_noext(char *out_path,
 | 
			
		||||
      const char *dir, const char *path, size_t size);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * fill_pathname_join_delim:
 | 
			
		||||
 * @out_path           : output path
 | 
			
		||||
| 
						 | 
				
			
			@ -408,45 +523,57 @@ void fill_pathname_join_noext(char *out_path,
 | 
			
		|||
 *
 | 
			
		||||
 * Joins a directory (@dir) and path (@path) together
 | 
			
		||||
 * using the given delimiter (@delim).
 | 
			
		||||
 *
 | 
			
		||||
 * Hidden non-leaf function cost: 
 | 
			
		||||
 * - can call strlen
 | 
			
		||||
 * - can call strlcpy
 | 
			
		||||
 * - can call strlcat
 | 
			
		||||
 **/
 | 
			
		||||
size_t fill_pathname_join_delim(char *out_path, const char *dir,
 | 
			
		||||
      const char *path, const char delim, size_t size);
 | 
			
		||||
 | 
			
		||||
size_t fill_pathname_join_delim_concat(char *out_path, const char *dir,
 | 
			
		||||
      const char *path, const char delim, const char *concat,
 | 
			
		||||
      size_t size);
 | 
			
		||||
size_t fill_pathname_expand_special(char *out_path,
 | 
			
		||||
      const char *in_path, size_t size);
 | 
			
		||||
 | 
			
		||||
size_t fill_pathname_abbreviate_special(char *out_path,
 | 
			
		||||
      const char *in_path, size_t size);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * fill_short_pathname_representation:
 | 
			
		||||
 * @out_rep            : output representation
 | 
			
		||||
 * @in_path            : input path
 | 
			
		||||
 * @size               : size of output representation
 | 
			
		||||
 * fill_pathname_abbreviated_or_relative:
 | 
			
		||||
 *
 | 
			
		||||
 * Generates a short representation of path. It should only
 | 
			
		||||
 * be used for displaying the result; the output representation is not
 | 
			
		||||
 * binding in any meaningful way (for a normal path, this is the same as basename)
 | 
			
		||||
 * In case of more complex URLs, this should cut everything except for
 | 
			
		||||
 * the main image file.
 | 
			
		||||
 * Fills the supplied path with either the abbreviated path or 
 | 
			
		||||
 * the relative path, which ever one has less depth / number of slashes
 | 
			
		||||
 * 
 | 
			
		||||
 * If lengths of abbreviated and relative paths are the same,
 | 
			
		||||
 * the relative path will be used
 | 
			
		||||
 * @in_path can be an absolute, relative or abbreviated path
 | 
			
		||||
 *
 | 
			
		||||
 * E.g.: "/path/to/game.img" -> game.img
 | 
			
		||||
 *       "/path/to/myarchive.7z#folder/to/game.img" -> game.img
 | 
			
		||||
 */
 | 
			
		||||
size_t fill_short_pathname_representation(char* out_rep,
 | 
			
		||||
      const char *in_path, size_t size);
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
 * @return Length of the string copied into @out_path
 | 
			
		||||
 **/
 | 
			
		||||
size_t fill_pathname_abbreviated_or_relative(char *out_path,
 | 
			
		||||
		const char *in_refpath, const char *in_path, size_t size);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -464,8 +591,8 @@ void path_basedir_wrapper(char *path);
 | 
			
		|||
 *
 | 
			
		||||
 * Checks if character (@c) is a slash.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: true (1) if character is a slash, otherwise false (0).
 | 
			
		||||
 */
 | 
			
		||||
 * @return true if character is a slash, otherwise false.
 | 
			
		||||
 **/
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
#define PATH_CHAR_IS_SLASH(c) (((c) == '/') || ((c) == '\\'))
 | 
			
		||||
#else
 | 
			
		||||
| 
						 | 
				
			
			@ -477,8 +604,8 @@ void path_basedir_wrapper(char *path);
 | 
			
		|||
 *
 | 
			
		||||
 * Gets the default slash separator.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: default slash separator.
 | 
			
		||||
 */
 | 
			
		||||
 * @return default slash separator.
 | 
			
		||||
 **/
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
#define PATH_DEFAULT_SLASH() "\\"
 | 
			
		||||
#define PATH_DEFAULT_SLASH_C() '\\'
 | 
			
		||||
| 
						 | 
				
			
			@ -494,6 +621,11 @@ void path_basedir_wrapper(char *path);
 | 
			
		|||
 *
 | 
			
		||||
 * Assumes path is a directory. Appends a slash
 | 
			
		||||
 * if not already there.
 | 
			
		||||
 | 
			
		||||
 * Hidden non-leaf function cost: 
 | 
			
		||||
 * - calls find_last_slash()
 | 
			
		||||
 *   - can call strlcat once if it returns false
 | 
			
		||||
 * - calls strlen
 | 
			
		||||
 **/
 | 
			
		||||
void fill_pathname_slash(char *path, size_t size);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -509,7 +641,16 @@ void fill_pathname_home_dir(char *buf, size_t size);
 | 
			
		|||
 *
 | 
			
		||||
 * Create directory on filesystem.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: true (1) if directory could be created, otherwise false (0).
 | 
			
		||||
 * Recursive function.
 | 
			
		||||
 *
 | 
			
		||||
 * Hidden non-leaf function cost:
 | 
			
		||||
 * - Calls strdup
 | 
			
		||||
 * - Calls path_parent_dir()
 | 
			
		||||
 * - Calls strcmp
 | 
			
		||||
 * - Calls path_is_directory()
 | 
			
		||||
 * - Calls path_mkdir()
 | 
			
		||||
 *
 | 
			
		||||
 * @return true if directory could be created, otherwise false.
 | 
			
		||||
 **/
 | 
			
		||||
bool path_mkdir(const char *dir);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -519,7 +660,7 @@ bool path_mkdir(const char *dir);
 | 
			
		|||
 *
 | 
			
		||||
 * Checks if path is a directory.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: true (1) if path is a directory, otherwise false (0).
 | 
			
		||||
 * @return true if path is a directory, otherwise false.
 | 
			
		||||
 */
 | 
			
		||||
bool path_is_directory(const char *path);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -283,6 +283,14 @@ enum retro_language
 | 
			
		|||
   RETRO_LANGUAGE_HEBREW              = 21,
 | 
			
		||||
   RETRO_LANGUAGE_ASTURIAN            = 22,
 | 
			
		||||
   RETRO_LANGUAGE_FINNISH             = 23,
 | 
			
		||||
   RETRO_LANGUAGE_INDONESIAN          = 24,
 | 
			
		||||
   RETRO_LANGUAGE_SWEDISH             = 25,
 | 
			
		||||
   RETRO_LANGUAGE_UKRAINIAN           = 26,
 | 
			
		||||
   RETRO_LANGUAGE_CZECH               = 27,
 | 
			
		||||
   RETRO_LANGUAGE_CATALAN_VALENCIA    = 28,
 | 
			
		||||
   RETRO_LANGUAGE_CATALAN             = 29,
 | 
			
		||||
   RETRO_LANGUAGE_BRITISH_ENGLISH     = 30,
 | 
			
		||||
   RETRO_LANGUAGE_HUNGARIAN           = 31,
 | 
			
		||||
   RETRO_LANGUAGE_LAST,
 | 
			
		||||
 | 
			
		||||
   /* Ensure sizeof(enum) == sizeof(int) */
 | 
			
		||||
| 
						 | 
				
			
			@ -1722,6 +1730,70 @@ enum retro_mod
 | 
			
		|||
                                            * Must be called in retro_set_environment().
 | 
			
		||||
                                            */
 | 
			
		||||
 | 
			
		||||
#define RETRO_ENVIRONMENT_SET_VARIABLE 70
 | 
			
		||||
                                           /* const struct retro_variable * --
 | 
			
		||||
                                            * Allows an implementation to notify the frontend
 | 
			
		||||
                                            * that a core option value has changed.
 | 
			
		||||
                                            *
 | 
			
		||||
                                            * retro_variable::key and retro_variable::value
 | 
			
		||||
                                            * must match strings that have been set previously
 | 
			
		||||
                                            * via one of the following:
 | 
			
		||||
                                            *
 | 
			
		||||
                                            * - RETRO_ENVIRONMENT_SET_VARIABLES
 | 
			
		||||
                                            * - RETRO_ENVIRONMENT_SET_CORE_OPTIONS
 | 
			
		||||
                                            * - RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL
 | 
			
		||||
                                            * - RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2
 | 
			
		||||
                                            * - RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL
 | 
			
		||||
                                            *
 | 
			
		||||
                                            * After changing a core option value via this
 | 
			
		||||
                                            * callback, RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE
 | 
			
		||||
                                            * will return true.
 | 
			
		||||
                                            *
 | 
			
		||||
                                            * If data is NULL, no changes will be registered
 | 
			
		||||
                                            * and the callback will return true; an
 | 
			
		||||
                                            * implementation may therefore pass NULL in order
 | 
			
		||||
                                            * to test whether the callback is supported.
 | 
			
		||||
                                            */
 | 
			
		||||
 | 
			
		||||
#define RETRO_ENVIRONMENT_GET_THROTTLE_STATE (71 | RETRO_ENVIRONMENT_EXPERIMENTAL)
 | 
			
		||||
                                           /* struct retro_throttle_state * --
 | 
			
		||||
                                            * Allows an implementation to get details on the actual rate
 | 
			
		||||
                                            * the frontend is attempting to call retro_run().
 | 
			
		||||
                                            */
 | 
			
		||||
 | 
			
		||||
#define RETRO_ENVIRONMENT_GET_SAVESTATE_CONTEXT (72 | RETRO_ENVIRONMENT_EXPERIMENTAL)
 | 
			
		||||
                                           /* int * --
 | 
			
		||||
                                            * Tells the core about the context the frontend is asking for savestate.
 | 
			
		||||
                                            * (see enum retro_savestate_context)
 | 
			
		||||
                                            */
 | 
			
		||||
 | 
			
		||||
#define RETRO_ENVIRONMENT_GET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_SUPPORT (73 | RETRO_ENVIRONMENT_EXPERIMENTAL)
 | 
			
		||||
                                            /* struct retro_hw_render_context_negotiation_interface * --
 | 
			
		||||
                                             * Before calling SET_HW_RNEDER_CONTEXT_NEGOTIATION_INTERFACE, a core can query
 | 
			
		||||
                                             * which version of the interface is supported.
 | 
			
		||||
                                             *
 | 
			
		||||
                                             * Frontend looks at interface_type and returns the maximum supported
 | 
			
		||||
                                             * context negotiation interface version.
 | 
			
		||||
                                             * If the interface_type is not supported or recognized by the frontend, a version of 0
 | 
			
		||||
                                             * must be returned in interface_version and true is returned by frontend.
 | 
			
		||||
                                             *
 | 
			
		||||
                                             * If this environment call returns true with interface_version greater than 0,
 | 
			
		||||
                                             * a core can always use a negotiation interface version larger than what the frontend returns, but only
 | 
			
		||||
                                             * earlier versions of the interface will be used by the frontend.
 | 
			
		||||
                                             * A frontend must not reject a negotiation interface version that is larger than
 | 
			
		||||
                                             * what the frontend supports. Instead, the frontend will use the older entry points that it recognizes.
 | 
			
		||||
                                             * If this is incompatible with a particular core's requirements, it can error out early.
 | 
			
		||||
                                             *
 | 
			
		||||
                                             * Backwards compatibility note:
 | 
			
		||||
                                             * This environment call was introduced after Vulkan v1 context negotiation.
 | 
			
		||||
                                             * If this environment call is not supported by frontend - i.e. the environment call returns false -
 | 
			
		||||
                                             * only Vulkan v1 context negotiation is supported (if Vulkan HW rendering is supported at all).
 | 
			
		||||
                                             * If a core uses Vulkan negotiation interface with version > 1, negotiation may fail unexpectedly.
 | 
			
		||||
                                             * All future updates to the context negotiation interface implies that frontend must support
 | 
			
		||||
                                             * this environment call to query support.
 | 
			
		||||
                                             */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* VFS functionality */
 | 
			
		||||
 | 
			
		||||
/* File paths:
 | 
			
		||||
| 
						 | 
				
			
			@ -2959,6 +3031,35 @@ enum retro_pixel_format
 | 
			
		|||
   RETRO_PIXEL_FORMAT_UNKNOWN  = INT_MAX
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum retro_savestate_context
 | 
			
		||||
{
 | 
			
		||||
   /* Standard savestate written to disk. */
 | 
			
		||||
   RETRO_SAVESTATE_CONTEXT_NORMAL                 = 0,
 | 
			
		||||
 | 
			
		||||
   /* Savestate where you are guaranteed that the same instance will load the save state.
 | 
			
		||||
    * You can store internal pointers to code or data.
 | 
			
		||||
    * It's still a full serialization and deserialization, and could be loaded or saved at any time. 
 | 
			
		||||
    * It won't be written to disk or sent over the network.
 | 
			
		||||
    */
 | 
			
		||||
   RETRO_SAVESTATE_CONTEXT_RUNAHEAD_SAME_INSTANCE = 1,
 | 
			
		||||
 | 
			
		||||
   /* Savestate where you are guaranteed that the same emulator binary will load that savestate.
 | 
			
		||||
    * You can skip anything that would slow down saving or loading state but you can not store internal pointers. 
 | 
			
		||||
    * It won't be written to disk or sent over the network.
 | 
			
		||||
    * Example: "Second Instance" runahead
 | 
			
		||||
    */
 | 
			
		||||
   RETRO_SAVESTATE_CONTEXT_RUNAHEAD_SAME_BINARY   = 2,
 | 
			
		||||
 | 
			
		||||
   /* Savestate used within a rollback netplay feature.
 | 
			
		||||
    * You should skip anything that would unnecessarily increase bandwidth usage.
 | 
			
		||||
    * It won't be written to disk but it will be sent over the network.
 | 
			
		||||
    */
 | 
			
		||||
   RETRO_SAVESTATE_CONTEXT_ROLLBACK_NETPLAY       = 3,
 | 
			
		||||
 | 
			
		||||
   /* Ensure sizeof() == sizeof(int). */
 | 
			
		||||
   RETRO_SAVESTATE_CONTEXT_UNKNOWN                = INT_MAX
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct retro_message
 | 
			
		||||
{
 | 
			
		||||
   const char *msg;        /* Message to be displayed. */
 | 
			
		||||
| 
						 | 
				
			
			@ -3430,6 +3531,10 @@ struct retro_core_option_definition
 | 
			
		|||
   const char *default_value;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef __PS3__
 | 
			
		||||
#undef local
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct retro_core_options_intl
 | 
			
		||||
{
 | 
			
		||||
   /* Pointer to an array of retro_core_option_definition structs
 | 
			
		||||
| 
						 | 
				
			
			@ -3667,6 +3772,43 @@ struct retro_fastforwarding_override
 | 
			
		|||
   bool inhibit_toggle;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* During normal operation. Rate will be equal to the core's internal FPS. */
 | 
			
		||||
#define RETRO_THROTTLE_NONE              0
 | 
			
		||||
 | 
			
		||||
/* While paused or stepping single frames. Rate will be 0. */
 | 
			
		||||
#define RETRO_THROTTLE_FRAME_STEPPING    1
 | 
			
		||||
 | 
			
		||||
/* During fast forwarding.
 | 
			
		||||
 * Rate will be 0 if not specifically limited to a maximum speed. */
 | 
			
		||||
#define RETRO_THROTTLE_FAST_FORWARD      2
 | 
			
		||||
 | 
			
		||||
/* During slow motion. Rate will be less than the core's internal FPS. */
 | 
			
		||||
#define RETRO_THROTTLE_SLOW_MOTION       3
 | 
			
		||||
 | 
			
		||||
/* While rewinding recorded save states. Rate can vary depending on the rewind
 | 
			
		||||
 * speed or be 0 if the frontend is not aiming for a specific rate. */
 | 
			
		||||
#define RETRO_THROTTLE_REWINDING         4
 | 
			
		||||
 | 
			
		||||
/* While vsync is active in the video driver and the target refresh rate is
 | 
			
		||||
 * lower than the core's internal FPS. Rate is the target refresh rate. */
 | 
			
		||||
#define RETRO_THROTTLE_VSYNC             5
 | 
			
		||||
 | 
			
		||||
/* When the frontend does not throttle in any way. Rate will be 0.
 | 
			
		||||
 * An example could be if no vsync or audio output is active. */
 | 
			
		||||
#define RETRO_THROTTLE_UNBLOCKED         6
 | 
			
		||||
 | 
			
		||||
struct retro_throttle_state
 | 
			
		||||
{
 | 
			
		||||
   /* The current throttling mode. Should be one of the values above. */
 | 
			
		||||
   unsigned mode;
 | 
			
		||||
 | 
			
		||||
   /* How many times per second the frontend aims to call retro_run.
 | 
			
		||||
    * Depending on the mode, it can be 0 if there is no known fixed rate.
 | 
			
		||||
    * This won't be accurate if the total processing time of the core and
 | 
			
		||||
    * the frontend is longer than what is available for one frame. */
 | 
			
		||||
   float rate;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Callbacks */
 | 
			
		||||
 | 
			
		||||
/* Environment callback. Gives implementations a way of performing
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -74,14 +74,29 @@ static INLINE bool bits_any_set(uint32_t* ptr, uint32_t count)
 | 
			
		|||
   return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static INLINE bool bits_any_different(uint32_t *a, uint32_t *b, uint32_t count)
 | 
			
		||||
{
 | 
			
		||||
   uint32_t i;
 | 
			
		||||
   for (i = 0; i < count; i++)
 | 
			
		||||
   {
 | 
			
		||||
      if (a[i] != b[i])
 | 
			
		||||
         return true;
 | 
			
		||||
   }
 | 
			
		||||
   return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifndef PATH_MAX_LENGTH
 | 
			
		||||
#if defined(_XBOX1) || defined(_3DS) || defined(PSP) || defined(PS2) || defined(GEKKO)|| defined(WIIU) || defined(ORBIS) || defined(__PSL1GHT__) || defined(__PS3__)
 | 
			
		||||
#if defined(_XBOX1) || defined(_3DS) || defined(PSP) || defined(PS2) || defined(GEKKO)|| defined(WIIU) || defined(__PSL1GHT__) || defined(__PS3__)
 | 
			
		||||
#define PATH_MAX_LENGTH 512
 | 
			
		||||
#else
 | 
			
		||||
#define PATH_MAX_LENGTH 4096
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef NAME_MAX_LENGTH
 | 
			
		||||
#define NAME_MAX_LENGTH 256
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef MAX
 | 
			
		||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,7 +59,7 @@ int64_t filestream_truncate(RFILE *stream, int64_t length);
 | 
			
		|||
 * @bufsize            : optional buffer size (-1 or 0 to use default)
 | 
			
		||||
 *
 | 
			
		||||
 * Opens a file for reading or writing, depending on the requested mode.
 | 
			
		||||
 * Returns a pointer to an RFILE if opened successfully, otherwise NULL.
 | 
			
		||||
 * @return A pointer to an RFILE if opened successfully, otherwise NULL.
 | 
			
		||||
 **/
 | 
			
		||||
RFILE* filestream_open(const char *path, unsigned mode, unsigned hints);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -75,16 +75,39 @@ void filestream_rewind(RFILE *stream);
 | 
			
		|||
 | 
			
		||||
int filestream_close(RFILE *stream);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * filestream_read_file:
 | 
			
		||||
 * @path             : path to file.
 | 
			
		||||
 * @buf              : buffer to allocate and read the contents of the
 | 
			
		||||
 *                     file into. Needs to be freed manually.
 | 
			
		||||
 * @len              : optional output integer containing bytes read.
 | 
			
		||||
 *
 | 
			
		||||
 * Read the contents of a file into @buf.
 | 
			
		||||
 *
 | 
			
		||||
 * @return Non-zero on success.
 | 
			
		||||
 */
 | 
			
		||||
int64_t filestream_read_file(const char *path, void **buf, int64_t *len);
 | 
			
		||||
 | 
			
		||||
char* filestream_gets(RFILE *stream, char *s, size_t len);
 | 
			
		||||
 | 
			
		||||
int filestream_getc(RFILE *stream);
 | 
			
		||||
 | 
			
		||||
int filestream_vscanf(RFILE *stream, const char* format, va_list *args);
 | 
			
		||||
 | 
			
		||||
int filestream_scanf(RFILE *stream, const char* format, ...);
 | 
			
		||||
 | 
			
		||||
int filestream_eof(RFILE *stream);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * filestream_write_file:
 | 
			
		||||
 * @path             : path to file.
 | 
			
		||||
 * @data             : contents to write to the file.
 | 
			
		||||
 * @size             : size of the contents.
 | 
			
		||||
 *
 | 
			
		||||
 * Writes data to a file.
 | 
			
		||||
 *
 | 
			
		||||
 * @return true on success, otherwise false.
 | 
			
		||||
 **/
 | 
			
		||||
bool filestream_write_file(const char *path, const void *data, int64_t size);
 | 
			
		||||
 | 
			
		||||
int filestream_putc(RFILE *stream, int c);
 | 
			
		||||
| 
						 | 
				
			
			@ -105,7 +128,11 @@ const char* filestream_get_path(RFILE *stream);
 | 
			
		|||
 | 
			
		||||
bool filestream_exists(const char *path);
 | 
			
		||||
 | 
			
		||||
/* Returned pointer must be freed by the caller. */
 | 
			
		||||
/**
 | 
			
		||||
 * filestream_getline:
 | 
			
		||||
 *
 | 
			
		||||
 * Returned pointer must be freed by the caller.
 | 
			
		||||
 **/
 | 
			
		||||
char* filestream_getline(RFILE *stream);
 | 
			
		||||
 | 
			
		||||
libretro_vfs_implementation_file* filestream_get_vfs_handle(RFILE *stream);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -92,16 +92,20 @@ static INLINE bool string_ends_with_size(const char *str, const char *suffix,
 | 
			
		|||
 | 
			
		||||
static INLINE bool string_ends_with(const char *str, const char *suffix)
 | 
			
		||||
{
 | 
			
		||||
   if (!str || !suffix)
 | 
			
		||||
      return false;
 | 
			
		||||
   return string_ends_with_size(str, suffix, strlen(str), strlen(suffix));
 | 
			
		||||
   return str && suffix && string_ends_with_size(str, suffix, strlen(str), strlen(suffix));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns the length of 'str' (c.f. strlen()), but only
 | 
			
		||||
/**
 | 
			
		||||
 * strlen_size:
 | 
			
		||||
 *
 | 
			
		||||
 * Leaf function.
 | 
			
		||||
 *
 | 
			
		||||
 * @return the length of 'str' (c.f. strlen()), but only
 | 
			
		||||
 * checks the first 'size' characters
 | 
			
		||||
 * - If 'str' is NULL, returns 0
 | 
			
		||||
 * - If 'str' is not NULL and no '\0' character is found
 | 
			
		||||
 *   in the first 'size' characters, returns 'size' */
 | 
			
		||||
 *   in the first 'size' characters, returns 'size'
 | 
			
		||||
 **/
 | 
			
		||||
static INLINE size_t strlen_size(const char *str, size_t size)
 | 
			
		||||
{
 | 
			
		||||
   size_t i = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -130,25 +134,68 @@ static INLINE bool string_is_equal_case_insensitive(const char *a,
 | 
			
		|||
   return (result == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static INLINE bool string_starts_with_case_insensitive(const char *str,
 | 
			
		||||
      const char *prefix)
 | 
			
		||||
{
 | 
			
		||||
   int result              = 0;
 | 
			
		||||
   const unsigned char *p1 = (const unsigned char*)str;
 | 
			
		||||
   const unsigned char *p2 = (const unsigned char*)prefix;
 | 
			
		||||
 | 
			
		||||
   if (!str || !prefix)
 | 
			
		||||
      return false;
 | 
			
		||||
   if (p1 == p2)
 | 
			
		||||
      return true;
 | 
			
		||||
 | 
			
		||||
   while ((result = tolower (*p1++) - tolower (*p2)) == 0)
 | 
			
		||||
      if (*p2++ == '\0')
 | 
			
		||||
         break;
 | 
			
		||||
 | 
			
		||||
   return (result == 0 || *p2 == '\0');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *string_to_upper(char *s);
 | 
			
		||||
 | 
			
		||||
char *string_to_lower(char *s);
 | 
			
		||||
 | 
			
		||||
char *string_ucwords(char *s);
 | 
			
		||||
 | 
			
		||||
char *string_replace_substring(const char *in, const char *pattern,
 | 
			
		||||
      const char *by);
 | 
			
		||||
char *string_replace_substring(const char *in,
 | 
			
		||||
      const char *pattern, size_t pattern_len,
 | 
			
		||||
      const char *replacement, size_t replacement_len);
 | 
			
		||||
 | 
			
		||||
/* Remove leading whitespaces */
 | 
			
		||||
/**
 | 
			
		||||
 * string_trim_whitespace_left:
 | 
			
		||||
 *
 | 
			
		||||
 * Remove leading whitespaces
 | 
			
		||||
 **/
 | 
			
		||||
char *string_trim_whitespace_left(char *const s);
 | 
			
		||||
 | 
			
		||||
/* Remove trailing whitespaces */
 | 
			
		||||
/**
 | 
			
		||||
 * string_trim_whitespace_right:
 | 
			
		||||
 *
 | 
			
		||||
 * Remove trailing whitespaces
 | 
			
		||||
 **/
 | 
			
		||||
char *string_trim_whitespace_right(char *const s);
 | 
			
		||||
 | 
			
		||||
/* Remove leading and trailing whitespaces */
 | 
			
		||||
/**
 | 
			
		||||
 * string_trim_whitespace:
 | 
			
		||||
 *
 | 
			
		||||
 * Remove leading and trailing whitespaces
 | 
			
		||||
 **/
 | 
			
		||||
char *string_trim_whitespace(char *const s);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
/**
 | 
			
		||||
 * word_wrap:
 | 
			
		||||
 * @dst                : pointer to destination buffer.
 | 
			
		||||
 * @dst_size           : size of destination buffer.
 | 
			
		||||
 * @src                : pointer to input string.
 | 
			
		||||
 * @src_len            : length of @src
 | 
			
		||||
 * @line_width         : max number of characters per line.
 | 
			
		||||
 * @wideglyph_width    : not used, but is necessary to keep
 | 
			
		||||
 *                       compatibility with word_wrap_wideglyph().
 | 
			
		||||
 * @max_lines          : max lines of destination string.
 | 
			
		||||
 *                       0 means no limit.
 | 
			
		||||
 *
 | 
			
		||||
 * Wraps string specified by 'src' to destination buffer
 | 
			
		||||
 * specified by 'dst' and 'dst_size'.
 | 
			
		||||
 * This function assumes that all glyphs in the string
 | 
			
		||||
| 
						 | 
				
			
			@ -156,58 +203,57 @@ char *string_trim_whitespace(char *const s);
 | 
			
		|||
 * regular Latin characters - i.e. it will not wrap
 | 
			
		||||
 * correctly any text containing so-called 'wide' Unicode
 | 
			
		||||
 * characters (e.g. CJK languages, emojis, etc.).
 | 
			
		||||
 *
 | 
			
		||||
 * @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,
 | 
			
		||||
 **/
 | 
			
		||||
void word_wrap(char *dst, size_t dst_size, const char *src, size_t src_len,
 | 
			
		||||
      int line_width, int wideglyph_width, unsigned max_lines);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Wraps string specified by 'src' to destination buffer
 | 
			
		||||
 * specified by 'dst' and 'dst_size'.
 | 
			
		||||
/**
 | 
			
		||||
 * word_wrap_wideglyph:
 | 
			
		||||
 * @dst                : pointer to destination buffer.
 | 
			
		||||
 * @dst_size           : size of destination buffer.
 | 
			
		||||
 * @src                : pointer to input string.
 | 
			
		||||
 * @src_len            : length of @src
 | 
			
		||||
 * @line_width         : max number of characters per line.
 | 
			
		||||
 * @wideglyph_width    : effective width of 'wide' Unicode glyphs.
 | 
			
		||||
 *                       the value here is normalised relative to the
 | 
			
		||||
 *                       typical on-screen pixel width of a regular
 | 
			
		||||
 *                       Latin character:
 | 
			
		||||
 *                       - a regular Latin character is defined to
 | 
			
		||||
 *                         have an effective width of 100
 | 
			
		||||
 *                       - wideglyph_width = 100 * (wide_character_pixel_width / latin_character_pixel_width)
 | 
			
		||||
 *                       - e.g. if 'wide' Unicode characters in 'src'
 | 
			
		||||
 *                         have an on-screen pixel width twice that of
 | 
			
		||||
 *                         regular Latin characters, wideglyph_width
 | 
			
		||||
 *                         would be 200
 | 
			
		||||
 * @max_lines          : max lines of destination string.
 | 
			
		||||
 *                       0 means no limit.
 | 
			
		||||
 *
 | 
			
		||||
 * Wraps string specified by @src to destination buffer
 | 
			
		||||
 * specified by @dst and @dst_size.
 | 
			
		||||
 * This function assumes that all glyphs in the string
 | 
			
		||||
 * are:
 | 
			
		||||
 * - EITHER 'non-wide' Unicode glyphs, with an on-screen
 | 
			
		||||
 *   pixel width similar to that of regular Latin characters
 | 
			
		||||
 * - OR 'wide' Unicode glyphs (e.g. CJK languages, emojis, etc.)
 | 
			
		||||
 *   with an on-screen pixel width defined by 'wideglyph_width'
 | 
			
		||||
 *   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
 | 
			
		||||
 * if @src string contains 'wide' Unicode characters whose
 | 
			
		||||
 * on-screen pixel width deviates greatly from the set
 | 
			
		||||
 * 'wideglyph_width' value.
 | 
			
		||||
 *
 | 
			
		||||
 * @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);
 | 
			
		||||
 * @wideglyph_width value.
 | 
			
		||||
 **/
 | 
			
		||||
void word_wrap_wideglyph(
 | 
			
		||||
      char *dst, size_t dst_size,
 | 
			
		||||
      const char *src, size_t src_len,
 | 
			
		||||
      int line_width, int wideglyph_width,
 | 
			
		||||
      unsigned max_lines);
 | 
			
		||||
 | 
			
		||||
/* Splits string into tokens seperated by 'delim'
 | 
			
		||||
/**
 | 
			
		||||
 * string_tokenize:
 | 
			
		||||
 *
 | 
			
		||||
 * Splits string into tokens seperated by @delim
 | 
			
		||||
 * > Returned token string must be free()'d
 | 
			
		||||
 * > Returns NULL if token is not found
 | 
			
		||||
 * > After each call, 'str' is set to the position after the
 | 
			
		||||
 * > After each call, @str is set to the position after the
 | 
			
		||||
 *   last found token
 | 
			
		||||
 * > Tokens *include* empty strings
 | 
			
		||||
 * Usage example:
 | 
			
		||||
| 
						 | 
				
			
			@ -220,29 +266,120 @@ void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src,
 | 
			
		|||
 *        free(token);
 | 
			
		||||
 *        token = NULL;
 | 
			
		||||
 *    }
 | 
			
		||||
 */
 | 
			
		||||
 **/
 | 
			
		||||
char* string_tokenize(char **str, const char *delim);
 | 
			
		||||
 | 
			
		||||
/* Removes every instance of character 'c' from 'str' */
 | 
			
		||||
/**
 | 
			
		||||
 * string_remove_all_chars:
 | 
			
		||||
 * @str                : input string (must be non-NULL, otherwise UB)
 | 
			
		||||
 *
 | 
			
		||||
 * Leaf function.
 | 
			
		||||
 *
 | 
			
		||||
 * Removes every instance of character @c from @str
 | 
			
		||||
 **/
 | 
			
		||||
void string_remove_all_chars(char *str, char c);
 | 
			
		||||
 | 
			
		||||
/* Replaces every instance of character 'find' in 'str'
 | 
			
		||||
 * with character 'replace' */
 | 
			
		||||
/**
 | 
			
		||||
 * string_replace_all_chars:
 | 
			
		||||
 * @str                : input string (must be non-NULL, otherwise UB)
 | 
			
		||||
 * @find               : character to find
 | 
			
		||||
 * @replace            : character to replace @find with
 | 
			
		||||
 *
 | 
			
		||||
 * Hidden non-leaf function cost:
 | 
			
		||||
 * - Calls strchr (in a loop)
 | 
			
		||||
 *
 | 
			
		||||
 * Replaces every instance of character @find in @str
 | 
			
		||||
 * with character @replace
 | 
			
		||||
 **/
 | 
			
		||||
void string_replace_all_chars(char *str, char find, char replace);
 | 
			
		||||
 | 
			
		||||
/* Converts string to unsigned integer.
 | 
			
		||||
 * Returns 0 if string is invalid  */
 | 
			
		||||
/**
 | 
			
		||||
 * string_to_unsigned:
 | 
			
		||||
 * @str                : input string
 | 
			
		||||
 *
 | 
			
		||||
 * Converts string to unsigned integer.
 | 
			
		||||
 *
 | 
			
		||||
 * @return 0 if string is invalid, otherwise > 0
 | 
			
		||||
 **/
 | 
			
		||||
unsigned string_to_unsigned(const char *str);
 | 
			
		||||
 | 
			
		||||
/* Converts hexadecimal string to unsigned integer.
 | 
			
		||||
/**
 | 
			
		||||
 * string_hex_to_unsigned:
 | 
			
		||||
 * @str                : input string (must be non-NULL, otherwise UB)
 | 
			
		||||
 *
 | 
			
		||||
 * Converts hexadecimal string to unsigned integer.
 | 
			
		||||
 * Handles optional leading '0x'.
 | 
			
		||||
 * Returns 0 if string is invalid  */
 | 
			
		||||
 *
 | 
			
		||||
 * @return 0 if string is invalid, otherwise > 0
 | 
			
		||||
 **/
 | 
			
		||||
unsigned string_hex_to_unsigned(const char *str);
 | 
			
		||||
 | 
			
		||||
char *string_init(const char *src);
 | 
			
		||||
 | 
			
		||||
void string_set(char **string, const char *src);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * string_count_occurrences_single_character:
 | 
			
		||||
 *
 | 
			
		||||
 * Leaf function.
 | 
			
		||||
 *
 | 
			
		||||
 * Get the total number of occurrences of character @c in @str.
 | 
			
		||||
 *
 | 
			
		||||
 * @return Total number of occurrences of character @c
 | 
			
		||||
 */
 | 
			
		||||
int string_count_occurrences_single_character(const char *str, char c);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * string_replace_whitespace_with_single_character:
 | 
			
		||||
 * 
 | 
			
		||||
 * Leaf function.
 | 
			
		||||
 *
 | 
			
		||||
 * Replaces all spaces with given character @c.
 | 
			
		||||
 **/
 | 
			
		||||
void string_replace_whitespace_with_single_character(char *str, char c);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * string_replace_multi_space_with_single_space:
 | 
			
		||||
 *
 | 
			
		||||
 * Leaf function.
 | 
			
		||||
 *
 | 
			
		||||
 * Replaces multiple spaces with a single space in a string.
 | 
			
		||||
 **/
 | 
			
		||||
void string_replace_multi_space_with_single_space(char *str);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * string_remove_all_whitespace:
 | 
			
		||||
 *
 | 
			
		||||
 * Leaf function.
 | 
			
		||||
 *
 | 
			
		||||
 * Remove all spaces from the given string.
 | 
			
		||||
 **/
 | 
			
		||||
void string_remove_all_whitespace(char *str_trimmed, const char *str);
 | 
			
		||||
 | 
			
		||||
/* Retrieve the last occurance of the given character in a string. */
 | 
			
		||||
int string_index_last_occurance(const char *str, char c);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * string_find_index_substring_string:
 | 
			
		||||
 * @str                : input string (must be non-NULL, otherwise UB)
 | 
			
		||||
 * @substr             : substring to find in @str
 | 
			
		||||
 *
 | 
			
		||||
 * Hidden non-leaf function cost:
 | 
			
		||||
 * - Calls strstr
 | 
			
		||||
 *
 | 
			
		||||
 * Find the position of substring @substr in string @str.
 | 
			
		||||
 **/
 | 
			
		||||
int string_find_index_substring_string(const char *str, const char *substr);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * string_copy_only_ascii:
 | 
			
		||||
 *
 | 
			
		||||
 * Leaf function.
 | 
			
		||||
 *
 | 
			
		||||
 * Strips non-ASCII characters from a string.
 | 
			
		||||
 **/
 | 
			
		||||
void string_copy_only_ascii(char *str_stripped, const char *str);
 | 
			
		||||
 | 
			
		||||
extern const unsigned char lr_char_props[256];
 | 
			
		||||
 | 
			
		||||
RETRO_END_DECLS
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -62,7 +62,7 @@ enum vfs_scheme
 | 
			
		|||
   VFS_SCHEME_CDROM
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifndef __WINRT__
 | 
			
		||||
#if !(defined(__WINRT__) && defined(__cplusplus_winrt))
 | 
			
		||||
#ifdef VFS_FRONTEND
 | 
			
		||||
struct retro_vfs_file_handle
 | 
			
		||||
#else
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -71,6 +71,12 @@ bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *dirstream);
 | 
			
		|||
 | 
			
		||||
int retro_vfs_closedir_impl(libretro_vfs_implementation_dir *dirstream);
 | 
			
		||||
 | 
			
		||||
#ifdef __WINRT__
 | 
			
		||||
 | 
			
		||||
void uwp_set_acl(const wchar_t* path, const wchar_t* AccessString);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
RETRO_END_DECLS
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,7 +25,6 @@
 | 
			
		|||
#include <string.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include "config.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +45,6 @@ struct RFILE
 | 
			
		|||
{
 | 
			
		||||
   struct retro_vfs_file_handle *hfile;
 | 
			
		||||
	bool error_flag;
 | 
			
		||||
	bool eof_flag;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static retro_vfs_get_path_t filestream_get_path_cb = NULL;
 | 
			
		||||
| 
						 | 
				
			
			@ -109,18 +107,14 @@ bool filestream_exists(const char *path)
 | 
			
		|||
 | 
			
		||||
   if (!path || !*path)
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
   dummy                  = filestream_open(
 | 
			
		||||
   if (!(dummy = filestream_open(
 | 
			
		||||
         path,
 | 
			
		||||
         RETRO_VFS_FILE_ACCESS_READ,
 | 
			
		||||
         RETRO_VFS_FILE_ACCESS_HINT_NONE);
 | 
			
		||||
 | 
			
		||||
   if (!dummy)
 | 
			
		||||
         RETRO_VFS_FILE_ACCESS_HINT_NONE)))
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
   if (filestream_close(dummy) != 0)
 | 
			
		||||
      if (dummy)
 | 
			
		||||
         free(dummy);
 | 
			
		||||
      free(dummy);
 | 
			
		||||
 | 
			
		||||
   dummy = NULL;
 | 
			
		||||
   return true;
 | 
			
		||||
| 
						 | 
				
			
			@ -165,7 +159,7 @@ int64_t filestream_truncate(RFILE *stream, int64_t length)
 | 
			
		|||
 * @hints              :
 | 
			
		||||
 *
 | 
			
		||||
 * Opens a file for reading or writing, depending on the requested mode.
 | 
			
		||||
 * Returns a pointer to an RFILE if opened successfully, otherwise NULL.
 | 
			
		||||
 * @return A pointer to an RFILE if opened successfully, otherwise NULL.
 | 
			
		||||
 **/
 | 
			
		||||
RFILE* filestream_open(const char *path, unsigned mode, unsigned hints)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -184,7 +178,6 @@ RFILE* filestream_open(const char *path, unsigned mode, unsigned hints)
 | 
			
		|||
 | 
			
		||||
   output             = (RFILE*)malloc(sizeof(RFILE));
 | 
			
		||||
   output->error_flag = false;
 | 
			
		||||
   output->eof_flag   = false;
 | 
			
		||||
   output->hfile      = fp;
 | 
			
		||||
   return output;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -221,14 +214,14 @@ int filestream_getc(RFILE *stream)
 | 
			
		|||
   return EOF;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int filestream_scanf(RFILE *stream, const char* format, ...)
 | 
			
		||||
int filestream_vscanf(RFILE *stream, const char* format, va_list *args)
 | 
			
		||||
{
 | 
			
		||||
   char buf[4096];
 | 
			
		||||
   char subfmt[64];
 | 
			
		||||
   va_list args;
 | 
			
		||||
   const char * bufiter = buf;
 | 
			
		||||
   va_list args_copy;
 | 
			
		||||
   const char *bufiter  = buf;
 | 
			
		||||
   int        ret       = 0;
 | 
			
		||||
   int64_t startpos     = filestream_tell(stream);
 | 
			
		||||
   int64_t startpos     = 0;
 | 
			
		||||
   int64_t maxlen       = filestream_read(stream, buf, sizeof(buf)-1);
 | 
			
		||||
 | 
			
		||||
   if (maxlen <= 0)
 | 
			
		||||
| 
						 | 
				
			
			@ -236,7 +229,16 @@ int filestream_scanf(RFILE *stream, const char* format, ...)
 | 
			
		|||
 | 
			
		||||
   buf[maxlen] = '\0';
 | 
			
		||||
 | 
			
		||||
   va_start(args, format);
 | 
			
		||||
   /* Have to copy the input va_list here
 | 
			
		||||
    * > Calling va_arg() on 'args' directly would
 | 
			
		||||
    *   cause the va_list to have an indeterminate value
 | 
			
		||||
    *   in the function calling filestream_vscanf(),
 | 
			
		||||
    *   leading to unexpected behaviour */
 | 
			
		||||
#ifdef __va_copy
 | 
			
		||||
   __va_copy(args_copy, *args);
 | 
			
		||||
#else
 | 
			
		||||
   va_copy(args_copy, *args);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
   while (*format)
 | 
			
		||||
   {
 | 
			
		||||
| 
						 | 
				
			
			@ -302,7 +304,7 @@ int filestream_scanf(RFILE *stream, const char* format, ...)
 | 
			
		|||
         }
 | 
			
		||||
         else
 | 
			
		||||
         {
 | 
			
		||||
            int v = sscanf(bufiter, subfmt, va_arg(args, void*), &sublen);
 | 
			
		||||
            int v = sscanf(bufiter, subfmt, va_arg(args_copy, void*), &sublen);
 | 
			
		||||
            if (v == EOF)
 | 
			
		||||
               return EOF;
 | 
			
		||||
            if (v != 1)
 | 
			
		||||
| 
						 | 
				
			
			@ -327,13 +329,24 @@ int filestream_scanf(RFILE *stream, const char* format, ...)
 | 
			
		|||
      }
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   va_end(args);
 | 
			
		||||
   filestream_seek(stream, startpos+(bufiter-buf),
 | 
			
		||||
   va_end(args_copy);
 | 
			
		||||
   startpos             = filestream_tell(stream);
 | 
			
		||||
   filestream_seek(stream, startpos + (bufiter - buf),
 | 
			
		||||
         RETRO_VFS_SEEK_POSITION_START);
 | 
			
		||||
 | 
			
		||||
   return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int filestream_scanf(RFILE *stream, const char* format, ...)
 | 
			
		||||
{
 | 
			
		||||
   int result;
 | 
			
		||||
   va_list vl;
 | 
			
		||||
   va_start(vl, format);
 | 
			
		||||
   result = filestream_vscanf(stream, format, &vl);
 | 
			
		||||
   va_end(vl);
 | 
			
		||||
   return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position)
 | 
			
		||||
{
 | 
			
		||||
   int64_t output;
 | 
			
		||||
| 
						 | 
				
			
			@ -348,14 +361,12 @@ int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position)
 | 
			
		|||
   if (output == VFS_ERROR_RETURN_VALUE)
 | 
			
		||||
      stream->error_flag = true;
 | 
			
		||||
 | 
			
		||||
   stream->eof_flag      = false;
 | 
			
		||||
 | 
			
		||||
   return output;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int filestream_eof(RFILE *stream)
 | 
			
		||||
{
 | 
			
		||||
   return stream->eof_flag;
 | 
			
		||||
   return filestream_tell(stream) == filestream_get_size(stream) ? EOF : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int64_t filestream_tell(RFILE *stream)
 | 
			
		||||
| 
						 | 
				
			
			@ -380,7 +391,6 @@ void filestream_rewind(RFILE *stream)
 | 
			
		|||
      return;
 | 
			
		||||
   filestream_seek(stream, 0L, RETRO_VFS_SEEK_POSITION_START);
 | 
			
		||||
   stream->error_flag = false;
 | 
			
		||||
   stream->eof_flag   = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int64_t filestream_read(RFILE *stream, void *s, int64_t len)
 | 
			
		||||
| 
						 | 
				
			
			@ -395,8 +405,6 @@ int64_t filestream_read(RFILE *stream, void *s, int64_t len)
 | 
			
		|||
 | 
			
		||||
   if (output == VFS_ERROR_RETURN_VALUE)
 | 
			
		||||
      stream->error_flag = true;
 | 
			
		||||
   if (output < len)
 | 
			
		||||
      stream->eof_flag   = true;
 | 
			
		||||
 | 
			
		||||
   return output;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -494,9 +502,7 @@ int filestream_printf(RFILE *stream, const char* format, ...)
 | 
			
		|||
 | 
			
		||||
int filestream_error(RFILE *stream)
 | 
			
		||||
{
 | 
			
		||||
   if (stream && stream->error_flag)
 | 
			
		||||
      return 1;
 | 
			
		||||
   return 0;
 | 
			
		||||
   return (stream && stream->error_flag);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int filestream_close(RFILE *stream)
 | 
			
		||||
| 
						 | 
				
			
			@ -525,7 +531,7 @@ int filestream_close(RFILE *stream)
 | 
			
		|||
 *
 | 
			
		||||
 * Read the contents of a file into @buf.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: non zero on success.
 | 
			
		||||
 * @return Non-zero on success.
 | 
			
		||||
 */
 | 
			
		||||
int64_t filestream_read_file(const char *path, void **buf, int64_t *len)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -542,25 +548,20 @@ int64_t filestream_read_file(const char *path, void **buf, int64_t *len)
 | 
			
		|||
      return 0;
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   content_buf_size = filestream_get_size(file);
 | 
			
		||||
 | 
			
		||||
   if (content_buf_size < 0)
 | 
			
		||||
   if ((content_buf_size = filestream_get_size(file)) < 0)
 | 
			
		||||
      goto error;
 | 
			
		||||
 | 
			
		||||
   content_buf      = malloc((size_t)(content_buf_size + 1));
 | 
			
		||||
 | 
			
		||||
   if (!content_buf)
 | 
			
		||||
   if (!(content_buf = malloc((size_t)(content_buf_size + 1))))
 | 
			
		||||
      goto error;
 | 
			
		||||
   if ((int64_t)(uint64_t)(content_buf_size + 1) != (content_buf_size + 1))
 | 
			
		||||
      goto error;
 | 
			
		||||
 | 
			
		||||
   ret = filestream_read(file, content_buf, (int64_t)content_buf_size);
 | 
			
		||||
   if (ret < 0)
 | 
			
		||||
   if ((ret = filestream_read(file, content_buf, (int64_t)content_buf_size)) <
 | 
			
		||||
         0)
 | 
			
		||||
      goto error;
 | 
			
		||||
 | 
			
		||||
   if (filestream_close(file) != 0)
 | 
			
		||||
      if (file)
 | 
			
		||||
         free(file);
 | 
			
		||||
      free(file);
 | 
			
		||||
 | 
			
		||||
   *buf    = content_buf;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -574,9 +575,8 @@ int64_t filestream_read_file(const char *path, void **buf, int64_t *len)
 | 
			
		|||
   return 1;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
   if (file)
 | 
			
		||||
      if (filestream_close(file) != 0)
 | 
			
		||||
         free(file);
 | 
			
		||||
   if (filestream_close(file) != 0)
 | 
			
		||||
      free(file);
 | 
			
		||||
   if (content_buf)
 | 
			
		||||
      free(content_buf);
 | 
			
		||||
   if (len)
 | 
			
		||||
| 
						 | 
				
			
			@ -593,8 +593,8 @@ error:
 | 
			
		|||
 *
 | 
			
		||||
 * Writes data to a file.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: true (1) on success, false (0) otherwise.
 | 
			
		||||
 */
 | 
			
		||||
 * @return true on success, otherwise false.
 | 
			
		||||
 **/
 | 
			
		||||
bool filestream_write_file(const char *path, const void *data, int64_t size)
 | 
			
		||||
{
 | 
			
		||||
   int64_t ret   = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -603,20 +603,18 @@ bool filestream_write_file(const char *path, const void *data, int64_t size)
 | 
			
		|||
         RETRO_VFS_FILE_ACCESS_HINT_NONE);
 | 
			
		||||
   if (!file)
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
   ret = filestream_write(file, data, size);
 | 
			
		||||
   if (filestream_close(file) != 0)
 | 
			
		||||
      if (file)
 | 
			
		||||
         free(file);
 | 
			
		||||
 | 
			
		||||
   if (ret != size)
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
   return true;
 | 
			
		||||
      free(file);
 | 
			
		||||
   return (ret == size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returned pointer must be freed by the caller. */
 | 
			
		||||
char* filestream_getline(RFILE *stream)
 | 
			
		||||
/**
 | 
			
		||||
 * filestream_getline:
 | 
			
		||||
 *
 | 
			
		||||
 * Returned pointer must be freed by the caller.
 | 
			
		||||
 **/
 | 
			
		||||
char *filestream_getline(RFILE *stream)
 | 
			
		||||
{
 | 
			
		||||
   char *newline_tmp  = NULL;
 | 
			
		||||
   size_t cur_size    = 8;
 | 
			
		||||
| 
						 | 
				
			
			@ -631,16 +629,15 @@ char* filestream_getline(RFILE *stream)
 | 
			
		|||
      return NULL;
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   in                 = filestream_getc(stream);
 | 
			
		||||
   in = filestream_getc(stream);
 | 
			
		||||
 | 
			
		||||
   while (in != EOF && in != '\n')
 | 
			
		||||
   {
 | 
			
		||||
      if (idx == cur_size)
 | 
			
		||||
      {
 | 
			
		||||
         cur_size    *= 2;
 | 
			
		||||
         newline_tmp  = (char*)realloc(newline, cur_size + 1);
 | 
			
		||||
 | 
			
		||||
         if (!newline_tmp)
 | 
			
		||||
         if (!(newline_tmp = (char*)realloc(newline, cur_size + 1)))
 | 
			
		||||
         {
 | 
			
		||||
            free(newline);
 | 
			
		||||
            return NULL;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -69,17 +69,27 @@ RFILE* rfopen(const char *path, const char *mode)
 | 
			
		|||
 | 
			
		||||
int rfclose(RFILE* stream)
 | 
			
		||||
{
 | 
			
		||||
   if (!stream)
 | 
			
		||||
      return EOF;
 | 
			
		||||
 | 
			
		||||
   return filestream_close(stream);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int64_t rftell(RFILE* stream)
 | 
			
		||||
{
 | 
			
		||||
   if (!stream)
 | 
			
		||||
      return -1;
 | 
			
		||||
 | 
			
		||||
   return filestream_tell(stream);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int64_t rfseek(RFILE* stream, int64_t offset, int origin)
 | 
			
		||||
{
 | 
			
		||||
   int seek_position = -1;
 | 
			
		||||
 | 
			
		||||
   if (!stream)
 | 
			
		||||
      return -1;
 | 
			
		||||
 | 
			
		||||
   switch (origin)
 | 
			
		||||
   {
 | 
			
		||||
      case SEEK_SET:
 | 
			
		||||
| 
						 | 
				
			
			@ -99,39 +109,61 @@ int64_t rfseek(RFILE* stream, int64_t offset, int origin)
 | 
			
		|||
int64_t rfread(void* buffer,
 | 
			
		||||
   size_t elem_size, size_t elem_count, RFILE* stream)
 | 
			
		||||
{
 | 
			
		||||
   if (!stream || (elem_size == 0) || (elem_count == 0))
 | 
			
		||||
      return 0;
 | 
			
		||||
 | 
			
		||||
   return (filestream_read(stream, buffer, elem_size * elem_count) / elem_size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *rfgets(char *buffer, int maxCount, RFILE* stream)
 | 
			
		||||
{
 | 
			
		||||
   if (!stream)
 | 
			
		||||
      return NULL;
 | 
			
		||||
 | 
			
		||||
   return filestream_gets(stream, buffer, maxCount);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int rfgetc(RFILE* stream)
 | 
			
		||||
{
 | 
			
		||||
   if (!stream)
 | 
			
		||||
      return EOF;
 | 
			
		||||
 | 
			
		||||
   return filestream_getc(stream);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int64_t rfwrite(void const* buffer,
 | 
			
		||||
   size_t elem_size, size_t elem_count, RFILE* stream)
 | 
			
		||||
{
 | 
			
		||||
   return filestream_write(stream, buffer, elem_size * elem_count);
 | 
			
		||||
   if (!stream || (elem_size == 0) || (elem_count == 0))
 | 
			
		||||
      return 0;
 | 
			
		||||
 | 
			
		||||
   return (filestream_write(stream, buffer, elem_size * elem_count) / elem_size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int rfputc(int character, RFILE * stream)
 | 
			
		||||
{
 | 
			
		||||
    return filestream_putc(stream, character);
 | 
			
		||||
   if (!stream)
 | 
			
		||||
      return EOF;
 | 
			
		||||
 | 
			
		||||
   return filestream_putc(stream, character);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int64_t rfflush(RFILE * stream)
 | 
			
		||||
{
 | 
			
		||||
    return filestream_flush(stream);
 | 
			
		||||
   if (!stream)
 | 
			
		||||
      return EOF;
 | 
			
		||||
 | 
			
		||||
   return filestream_flush(stream);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int rfprintf(RFILE * stream, const char * format, ...)
 | 
			
		||||
{
 | 
			
		||||
   int result;
 | 
			
		||||
   va_list vl;
 | 
			
		||||
 | 
			
		||||
   if (!stream)
 | 
			
		||||
      return -1;
 | 
			
		||||
 | 
			
		||||
   va_start(vl, format);
 | 
			
		||||
   result = filestream_vprintf(stream, format, vl);
 | 
			
		||||
   va_end(vl);
 | 
			
		||||
| 
						 | 
				
			
			@ -152,8 +184,12 @@ int rfscanf(RFILE * stream, const char * format, ...)
 | 
			
		|||
{
 | 
			
		||||
   int result;
 | 
			
		||||
   va_list vl;
 | 
			
		||||
 | 
			
		||||
   if (!stream)
 | 
			
		||||
      return 0;
 | 
			
		||||
 | 
			
		||||
   va_start(vl, format);
 | 
			
		||||
   result = filestream_scanf(stream, format, vl);
 | 
			
		||||
   result = filestream_vscanf(stream, format, &vl);
 | 
			
		||||
   va_end(vl);
 | 
			
		||||
   return result;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,7 @@
 | 
			
		|||
#include <ctype.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include <compat/strl.h>
 | 
			
		||||
#include <string/stdstring.h>
 | 
			
		||||
#include <encodings/utf.h>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -89,9 +90,11 @@ char *string_ucwords(char *s)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
char *string_replace_substring(const char *in,
 | 
			
		||||
      const char *pattern, const char *replacement)
 | 
			
		||||
      const char *pattern, size_t pattern_len,
 | 
			
		||||
      const char *replacement, size_t replacement_len)
 | 
			
		||||
{
 | 
			
		||||
   size_t numhits, pattern_len, replacement_len, outlen;
 | 
			
		||||
   size_t outlen;
 | 
			
		||||
   size_t numhits     = 0;
 | 
			
		||||
   const char *inat   = NULL;
 | 
			
		||||
   const char *inprev = NULL;
 | 
			
		||||
   char          *out = NULL;
 | 
			
		||||
| 
						 | 
				
			
			@ -102,9 +105,6 @@ char *string_replace_substring(const char *in,
 | 
			
		|||
   if (!pattern || !replacement)
 | 
			
		||||
      return strdup(in);
 | 
			
		||||
 | 
			
		||||
   pattern_len     = strlen(pattern);
 | 
			
		||||
   replacement_len = strlen(replacement);
 | 
			
		||||
   numhits         = 0;
 | 
			
		||||
   inat            = in;
 | 
			
		||||
 | 
			
		||||
   while ((inat = strstr(inat, pattern)))
 | 
			
		||||
| 
						 | 
				
			
			@ -114,9 +114,8 @@ char *string_replace_substring(const char *in,
 | 
			
		|||
   }
 | 
			
		||||
 | 
			
		||||
   outlen          = strlen(in) - pattern_len*numhits + replacement_len*numhits;
 | 
			
		||||
   out             = (char *)malloc(outlen+1);
 | 
			
		||||
 | 
			
		||||
   if (!out)
 | 
			
		||||
   if (!(out = (char *)malloc(outlen+1)))
 | 
			
		||||
      return NULL;
 | 
			
		||||
 | 
			
		||||
   outat           = out;
 | 
			
		||||
| 
						 | 
				
			
			@ -129,7 +128,7 @@ char *string_replace_substring(const char *in,
 | 
			
		|||
      outat += inat-inprev;
 | 
			
		||||
      memcpy(outat, replacement, replacement_len);
 | 
			
		||||
      outat += replacement_len;
 | 
			
		||||
      inat += pattern_len;
 | 
			
		||||
      inat  += pattern_len;
 | 
			
		||||
      inprev = inat;
 | 
			
		||||
   }
 | 
			
		||||
   strcpy(outat, inprev);
 | 
			
		||||
| 
						 | 
				
			
			@ -137,7 +136,11 @@ char *string_replace_substring(const char *in,
 | 
			
		|||
   return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Remove leading whitespaces */
 | 
			
		||||
/**
 | 
			
		||||
 * string_trim_whitespace_left:
 | 
			
		||||
 *
 | 
			
		||||
 * Remove leading whitespaces
 | 
			
		||||
 **/
 | 
			
		||||
char *string_trim_whitespace_left(char *const s)
 | 
			
		||||
{
 | 
			
		||||
   if (s && *s)
 | 
			
		||||
| 
						 | 
				
			
			@ -158,7 +161,11 @@ char *string_trim_whitespace_left(char *const s)
 | 
			
		|||
   return s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Remove trailing whitespaces */
 | 
			
		||||
/**
 | 
			
		||||
 * string_trim_whitespace_right:
 | 
			
		||||
 *
 | 
			
		||||
 * Remove trailing whitespaces
 | 
			
		||||
 **/
 | 
			
		||||
char *string_trim_whitespace_right(char *const s)
 | 
			
		||||
{
 | 
			
		||||
   if (s && *s)
 | 
			
		||||
| 
						 | 
				
			
			@ -178,7 +185,11 @@ char *string_trim_whitespace_right(char *const s)
 | 
			
		|||
   return s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Remove leading and trailing whitespaces */
 | 
			
		||||
/**
 | 
			
		||||
 * string_trim_whitespace:
 | 
			
		||||
 *
 | 
			
		||||
 * Remove leading and trailing whitespaces
 | 
			
		||||
 **/
 | 
			
		||||
char *string_trim_whitespace(char *const s)
 | 
			
		||||
{
 | 
			
		||||
   string_trim_whitespace_right(s);  /* order matters */
 | 
			
		||||
| 
						 | 
				
			
			@ -187,12 +198,33 @@ char *string_trim_whitespace(char *const 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.).
 | 
			
		||||
 **/
 | 
			
		||||
void word_wrap(
 | 
			
		||||
      char *dst,       size_t dst_size,
 | 
			
		||||
      const char *src, size_t src_len,
 | 
			
		||||
      int line_width,  int wideglyph_width, unsigned max_lines)
 | 
			
		||||
{
 | 
			
		||||
   char *lastspace     = NULL;
 | 
			
		||||
   unsigned counter    = 0;
 | 
			
		||||
   unsigned lines      = 1;
 | 
			
		||||
   size_t src_len      = strlen(src);
 | 
			
		||||
   const char *src_end = src + src_len;
 | 
			
		||||
 | 
			
		||||
   /* Prevent buffer overflow */
 | 
			
		||||
| 
						 | 
				
			
			@ -201,17 +233,15 @@ void word_wrap(char *dst, size_t dst_size, const char *src, int line_width, int
 | 
			
		|||
 | 
			
		||||
   /* Early return if src string length is less
 | 
			
		||||
    * than line width */
 | 
			
		||||
   if (src_len < line_width)
 | 
			
		||||
   if (src_len < (size_t)line_width)
 | 
			
		||||
   {
 | 
			
		||||
      strcpy(dst, src);
 | 
			
		||||
      strlcpy(dst, src, dst_size);
 | 
			
		||||
      return;
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   while (*src != '\0')
 | 
			
		||||
   {
 | 
			
		||||
      unsigned char_len;
 | 
			
		||||
 | 
			
		||||
      char_len = (unsigned)(utf8skip(src, 1) - src);
 | 
			
		||||
      unsigned char_len = (unsigned)(utf8skip(src, 1) - src);
 | 
			
		||||
      counter++;
 | 
			
		||||
 | 
			
		||||
      if (*src == ' ')
 | 
			
		||||
| 
						 | 
				
			
			@ -227,7 +257,7 @@ void word_wrap(char *dst, size_t dst_size, const char *src, int line_width, int
 | 
			
		|||
          * length is less than line width */
 | 
			
		||||
         if (src_end - src <= line_width)
 | 
			
		||||
         {
 | 
			
		||||
            strcpy(dst, src);
 | 
			
		||||
            strlcpy(dst, src, dst_size);
 | 
			
		||||
            return;
 | 
			
		||||
         }
 | 
			
		||||
     }
 | 
			
		||||
| 
						 | 
				
			
			@ -254,7 +284,7 @@ void word_wrap(char *dst, size_t dst_size, const char *src, int line_width, int
 | 
			
		|||
             * length is less than line width */
 | 
			
		||||
            if (src_end - src < line_width)
 | 
			
		||||
            {
 | 
			
		||||
               strcpy(dst, src);
 | 
			
		||||
               strlcpy(dst, src, dst_size);
 | 
			
		||||
               return;
 | 
			
		||||
            }
 | 
			
		||||
         }
 | 
			
		||||
| 
						 | 
				
			
			@ -264,11 +294,46 @@ void word_wrap(char *dst, size_t dst_size, const char *src, int line_width, int
 | 
			
		|||
   *dst = '\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.
 | 
			
		||||
 **/
 | 
			
		||||
void word_wrap_wideglyph(char *dst, size_t dst_size,
 | 
			
		||||
      const char *src, size_t src_len, int line_width,
 | 
			
		||||
      int wideglyph_width, unsigned max_lines)
 | 
			
		||||
{
 | 
			
		||||
   char *lastspace                   = NULL;
 | 
			
		||||
   char *lastwideglyph               = NULL;
 | 
			
		||||
   const char *src_end               = src + strlen(src);
 | 
			
		||||
   const char *src_end               = src + src_len;
 | 
			
		||||
   unsigned lines                    = 1;
 | 
			
		||||
   /* 'line_width' means max numbers of characters per line,
 | 
			
		||||
    * but this metric is only meaningful when dealing with
 | 
			
		||||
| 
						 | 
				
			
			@ -305,9 +370,7 @@ void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src, int line_w
 | 
			
		|||
 | 
			
		||||
   while (*src != '\0')
 | 
			
		||||
   {
 | 
			
		||||
      unsigned char_len;
 | 
			
		||||
 | 
			
		||||
      char_len = (unsigned)(utf8skip(src, 1) - src);
 | 
			
		||||
      unsigned char_len   = (unsigned)(utf8skip(src, 1) - src);
 | 
			
		||||
      counter_normalized += 100;
 | 
			
		||||
 | 
			
		||||
      /* Prevent buffer overflow */
 | 
			
		||||
| 
						 | 
				
			
			@ -315,7 +378,7 @@ void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src, int line_w
 | 
			
		|||
         break;
 | 
			
		||||
 | 
			
		||||
      if (*src == ' ')
 | 
			
		||||
         lastspace = dst; /* Remember the location of the whitespace */
 | 
			
		||||
         lastspace          = dst; /* Remember the location of the whitespace */
 | 
			
		||||
      else if (*src == '\n')
 | 
			
		||||
      {
 | 
			
		||||
         /* If newlines embedded in the input,
 | 
			
		||||
| 
						 | 
				
			
			@ -335,7 +398,7 @@ void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src, int line_w
 | 
			
		|||
      {
 | 
			
		||||
         /* Remember the location of the first byte
 | 
			
		||||
          * whose length as UTF-8 >= 3*/
 | 
			
		||||
         lastwideglyph = dst;
 | 
			
		||||
         lastwideglyph       = dst;
 | 
			
		||||
         counter_normalized += additional_counter_normalized;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -354,9 +417,9 @@ void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src, int line_w
 | 
			
		|||
            /* Insert newline character */
 | 
			
		||||
            *lastwideglyph = '\n';
 | 
			
		||||
            lines++;
 | 
			
		||||
            src -= dst - lastwideglyph;
 | 
			
		||||
            dst = lastwideglyph + 1;
 | 
			
		||||
            lastwideglyph = NULL;
 | 
			
		||||
            src           -= dst - lastwideglyph;
 | 
			
		||||
            dst            = lastwideglyph + 1;
 | 
			
		||||
            lastwideglyph  = NULL;
 | 
			
		||||
 | 
			
		||||
            /* Early return if remaining src string
 | 
			
		||||
             * length is less than line width */
 | 
			
		||||
| 
						 | 
				
			
			@ -372,9 +435,9 @@ void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src, int line_w
 | 
			
		|||
             * with newline character */
 | 
			
		||||
            *lastspace = '\n';
 | 
			
		||||
            lines++;
 | 
			
		||||
            src -= dst - lastspace - 1;
 | 
			
		||||
            dst = lastspace + 1;
 | 
			
		||||
            lastspace = NULL;
 | 
			
		||||
            src       -= dst - lastspace - 1;
 | 
			
		||||
            dst        = lastspace + 1;
 | 
			
		||||
            lastspace  = NULL;
 | 
			
		||||
 | 
			
		||||
            /* Early return if remaining src string
 | 
			
		||||
             * length is less than line width */
 | 
			
		||||
| 
						 | 
				
			
			@ -390,10 +453,13 @@ void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src, int line_w
 | 
			
		|||
   *dst = '\0';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Splits string into tokens seperated by 'delim'
 | 
			
		||||
/**
 | 
			
		||||
 * string_tokenize:
 | 
			
		||||
 *
 | 
			
		||||
 * Splits string into tokens seperated by @delim
 | 
			
		||||
 * > Returned token string must be free()'d
 | 
			
		||||
 * > Returns NULL if token is not found
 | 
			
		||||
 * > After each call, 'str' is set to the position after the
 | 
			
		||||
 * > After each call, @str is set to the position after the
 | 
			
		||||
 *   last found token
 | 
			
		||||
 * > Tokens *include* empty strings
 | 
			
		||||
 * Usage example:
 | 
			
		||||
| 
						 | 
				
			
			@ -406,7 +472,7 @@ void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src, int line_w
 | 
			
		|||
 *        free(token);
 | 
			
		||||
 *        token = NULL;
 | 
			
		||||
 *    }
 | 
			
		||||
 */
 | 
			
		||||
 **/
 | 
			
		||||
char* string_tokenize(char **str, const char *delim)
 | 
			
		||||
{
 | 
			
		||||
   /* Taken from https://codereview.stackexchange.com/questions/216956/strtok-function-thread-safe-supports-empty-tokens-doesnt-change-string# */
 | 
			
		||||
| 
						 | 
				
			
			@ -419,25 +485,20 @@ char* string_tokenize(char **str, const char *delim)
 | 
			
		|||
   if (!str || string_is_empty(delim))
 | 
			
		||||
      return NULL;
 | 
			
		||||
 | 
			
		||||
   str_ptr = *str;
 | 
			
		||||
 | 
			
		||||
   /* Note: we don't check string_is_empty() here,
 | 
			
		||||
    * empty strings are valid */
 | 
			
		||||
   if (!str_ptr)
 | 
			
		||||
   if (!(str_ptr = *str))
 | 
			
		||||
      return NULL;
 | 
			
		||||
 | 
			
		||||
   /* Search for delimiter */
 | 
			
		||||
   delim_ptr = strstr(str_ptr, delim);
 | 
			
		||||
 | 
			
		||||
   if (delim_ptr)
 | 
			
		||||
   if ((delim_ptr = strstr(str_ptr, delim)))
 | 
			
		||||
      token_len = delim_ptr - str_ptr;
 | 
			
		||||
   else
 | 
			
		||||
      token_len = strlen(str_ptr);
 | 
			
		||||
 | 
			
		||||
   /* Allocate token string */
 | 
			
		||||
   token = (char *)malloc((token_len + 1) * sizeof(char));
 | 
			
		||||
 | 
			
		||||
   if (!token)
 | 
			
		||||
   if (!(token = (char *)malloc((token_len + 1) * sizeof(char))))
 | 
			
		||||
      return NULL;
 | 
			
		||||
 | 
			
		||||
   /* Copy token */
 | 
			
		||||
| 
						 | 
				
			
			@ -450,42 +511,53 @@ char* string_tokenize(char **str, const char *delim)
 | 
			
		|||
   return token;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Removes every instance of character 'c' from 'str' */
 | 
			
		||||
/**
 | 
			
		||||
 * string_remove_all_chars:
 | 
			
		||||
 * @str                : input string (must be non-NULL, otherwise UB)
 | 
			
		||||
 *
 | 
			
		||||
 * Leaf function.
 | 
			
		||||
 *
 | 
			
		||||
 * Removes every instance of character @c from @str
 | 
			
		||||
 **/
 | 
			
		||||
void string_remove_all_chars(char *str, char c)
 | 
			
		||||
{
 | 
			
		||||
   char *read_ptr  = NULL;
 | 
			
		||||
   char *write_ptr = NULL;
 | 
			
		||||
 | 
			
		||||
   if (string_is_empty(str))
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
   read_ptr  = str;
 | 
			
		||||
   write_ptr = str;
 | 
			
		||||
   char *read_ptr  = str;
 | 
			
		||||
   char *write_ptr = str;
 | 
			
		||||
 | 
			
		||||
   while (*read_ptr != '\0')
 | 
			
		||||
   {
 | 
			
		||||
      *write_ptr = *read_ptr++;
 | 
			
		||||
      write_ptr += (*write_ptr != c) ? 1 : 0;
 | 
			
		||||
      if (*write_ptr != c)
 | 
			
		||||
         write_ptr++;
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   *write_ptr = '\0';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Replaces every instance of character 'find' in 'str'
 | 
			
		||||
 * with character 'replace' */
 | 
			
		||||
/**
 | 
			
		||||
 * string_replace_all_chars:
 | 
			
		||||
 * @str                : input string (must be non-NULL, otherwise UB)
 | 
			
		||||
 * @find               : character to find
 | 
			
		||||
 * @replace            : character to replace @find with
 | 
			
		||||
 *
 | 
			
		||||
 * Replaces every instance of character @find in @str
 | 
			
		||||
 * with character @replace
 | 
			
		||||
 **/
 | 
			
		||||
void string_replace_all_chars(char *str, char find, char replace)
 | 
			
		||||
{
 | 
			
		||||
   char *str_ptr = str;
 | 
			
		||||
 | 
			
		||||
   if (string_is_empty(str))
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
   while ((str_ptr = strchr(str_ptr, find)))
 | 
			
		||||
      *str_ptr++ = replace;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Converts string to unsigned integer.
 | 
			
		||||
 * Returns 0 if string is invalid  */
 | 
			
		||||
/**
 | 
			
		||||
 * string_to_unsigned:
 | 
			
		||||
 * @str                : input string
 | 
			
		||||
 *
 | 
			
		||||
 * Converts string to unsigned integer.
 | 
			
		||||
 *
 | 
			
		||||
 * @return 0 if string is invalid, otherwise > 0
 | 
			
		||||
 **/
 | 
			
		||||
unsigned string_to_unsigned(const char *str)
 | 
			
		||||
{
 | 
			
		||||
   const char *ptr = NULL;
 | 
			
		||||
| 
						 | 
				
			
			@ -502,27 +574,33 @@ unsigned string_to_unsigned(const char *str)
 | 
			
		|||
   return (unsigned)strtoul(str, NULL, 10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Converts hexadecimal string to unsigned integer.
 | 
			
		||||
/**
 | 
			
		||||
 * string_hex_to_unsigned:
 | 
			
		||||
 * @str                : input string (must be non-NULL, otherwise UB)
 | 
			
		||||
 *
 | 
			
		||||
 * Converts hexadecimal string to unsigned integer.
 | 
			
		||||
 * Handles optional leading '0x'.
 | 
			
		||||
 * Returns 0 if string is invalid  */
 | 
			
		||||
 *
 | 
			
		||||
 * @return 0 if string is invalid, otherwise > 0
 | 
			
		||||
 **/
 | 
			
		||||
unsigned string_hex_to_unsigned(const char *str)
 | 
			
		||||
{
 | 
			
		||||
   const char *hex_str = str;
 | 
			
		||||
   const char *ptr     = NULL;
 | 
			
		||||
   size_t len;
 | 
			
		||||
 | 
			
		||||
   if (string_is_empty(str))
 | 
			
		||||
      return 0;
 | 
			
		||||
 | 
			
		||||
   /* Remove leading '0x', if required */
 | 
			
		||||
   len = strlen(str);
 | 
			
		||||
 | 
			
		||||
   if (len >= 2)
 | 
			
		||||
      if ((str[0] == '0') &&
 | 
			
		||||
          ((str[1] == 'x') || (str[1] == 'X')))
 | 
			
		||||
   if (str[0] != '\0' && str[1] != '\0')
 | 
			
		||||
   {
 | 
			
		||||
      if ( (str[0] == '0') &&
 | 
			
		||||
          ((str[1] == 'x') || 
 | 
			
		||||
           (str[1] == 'X')))
 | 
			
		||||
      {
 | 
			
		||||
         hex_str = str + 2;
 | 
			
		||||
 | 
			
		||||
   if (string_is_empty(hex_str))
 | 
			
		||||
         if (string_is_empty(hex_str))
 | 
			
		||||
            return 0;
 | 
			
		||||
      }
 | 
			
		||||
   }
 | 
			
		||||
   else
 | 
			
		||||
      return 0;
 | 
			
		||||
 | 
			
		||||
   /* Check for valid characters */
 | 
			
		||||
| 
						 | 
				
			
			@ -534,3 +612,117 @@ unsigned string_hex_to_unsigned(const char *str)
 | 
			
		|||
 | 
			
		||||
   return (unsigned)strtoul(hex_str, NULL, 16);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * string_count_occurrences_single_character:
 | 
			
		||||
 *
 | 
			
		||||
 * Leaf function.
 | 
			
		||||
 *
 | 
			
		||||
 * Get the total number of occurrences of character @c in @str.
 | 
			
		||||
 *
 | 
			
		||||
 * @return Total number of occurrences of character @c
 | 
			
		||||
 */
 | 
			
		||||
int string_count_occurrences_single_character(const char *str, char c)
 | 
			
		||||
{
 | 
			
		||||
   int count = 0;
 | 
			
		||||
 | 
			
		||||
   for (; *str; str++)
 | 
			
		||||
      if (*str == c)
 | 
			
		||||
         count++;
 | 
			
		||||
 | 
			
		||||
   return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * string_replace_whitespace_with_single_character:
 | 
			
		||||
 * 
 | 
			
		||||
 * Leaf function.
 | 
			
		||||
 *
 | 
			
		||||
 * Replaces all spaces with given character @c.
 | 
			
		||||
 **/
 | 
			
		||||
void string_replace_whitespace_with_single_character(char *str, char c)
 | 
			
		||||
{
 | 
			
		||||
   for (; *str; str++)
 | 
			
		||||
      if (ISSPACE(*str))
 | 
			
		||||
         *str = c;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * string_replace_multi_space_with_single_space:
 | 
			
		||||
 *
 | 
			
		||||
 * Leaf function.
 | 
			
		||||
 *
 | 
			
		||||
 * Replaces multiple spaces with a single space in a string.
 | 
			
		||||
 **/
 | 
			
		||||
void string_replace_multi_space_with_single_space(char *str)
 | 
			
		||||
{
 | 
			
		||||
   char *str_trimmed  = str;
 | 
			
		||||
   bool prev_is_space = false;
 | 
			
		||||
   bool curr_is_space = false;
 | 
			
		||||
 | 
			
		||||
   for (; *str; str++)
 | 
			
		||||
   {
 | 
			
		||||
      curr_is_space  = ISSPACE(*str);
 | 
			
		||||
      if (prev_is_space && curr_is_space)
 | 
			
		||||
         continue;
 | 
			
		||||
      *str_trimmed++ = *str;
 | 
			
		||||
      prev_is_space  = curr_is_space;
 | 
			
		||||
   }
 | 
			
		||||
   *str_trimmed = '\0';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * string_remove_all_whitespace:
 | 
			
		||||
 *
 | 
			
		||||
 * Leaf function.
 | 
			
		||||
 *
 | 
			
		||||
 * Remove all spaces from the given string.
 | 
			
		||||
 **/
 | 
			
		||||
void string_remove_all_whitespace(char *str_trimmed, const char *str)
 | 
			
		||||
{
 | 
			
		||||
   for (; *str; str++)
 | 
			
		||||
      if (!ISSPACE(*str))
 | 
			
		||||
         *str_trimmed++ = *str;
 | 
			
		||||
   *str_trimmed = '\0';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Retrieve the last occurance of the given character in a string.
 | 
			
		||||
 */
 | 
			
		||||
int string_index_last_occurance(const char *str, char c)
 | 
			
		||||
{
 | 
			
		||||
   const char *pos = strrchr(str, c);
 | 
			
		||||
   if (pos)
 | 
			
		||||
      return (int)(pos - str);
 | 
			
		||||
   return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * string_find_index_substring_string:
 | 
			
		||||
 * @str                : input string (must be non-NULL, otherwise UB)
 | 
			
		||||
 * @substr             : substring to find in @str
 | 
			
		||||
 *
 | 
			
		||||
 * Find the position of substring @substr in string @str.
 | 
			
		||||
 **/
 | 
			
		||||
int string_find_index_substring_string(const char *str, const char *substr)
 | 
			
		||||
{
 | 
			
		||||
   const char *pos = strstr(str, substr);
 | 
			
		||||
   if (pos)
 | 
			
		||||
      return (int)(pos - str);
 | 
			
		||||
   return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * string_copy_only_ascii:
 | 
			
		||||
 *
 | 
			
		||||
 * Leaf function.
 | 
			
		||||
 *
 | 
			
		||||
 * Strips non-ASCII characters from a string.
 | 
			
		||||
 **/
 | 
			
		||||
void string_copy_only_ascii(char *str_stripped, const char *str)
 | 
			
		||||
{
 | 
			
		||||
   for (; *str; str++)
 | 
			
		||||
      if (*str > 0x1F && *str < 0x7F)
 | 
			
		||||
         *str_stripped++ = *str;
 | 
			
		||||
   *str_stripped = '\0';
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,7 +22,6 @@
 | 
			
		|||
 | 
			
		||||
#ifdef HAVE_THREADS
 | 
			
		||||
#include <rthreads/rthreads.h>
 | 
			
		||||
#include <retro_assert.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -41,8 +40,6 @@ void rtime_init(void)
 | 
			
		|||
#ifdef HAVE_THREADS
 | 
			
		||||
   if (!rtime_localtime_lock)
 | 
			
		||||
      rtime_localtime_lock = slock_new();
 | 
			
		||||
 | 
			
		||||
   retro_assert(rtime_localtime_lock);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,7 +26,7 @@
 | 
			
		|||
#include <errno.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
 | 
			
		||||
#include <string/stdstring.h>
 | 
			
		||||
#include <string/stdstring.h> /* string_is_empty */
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include "config.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -57,11 +57,6 @@
 | 
			
		|||
#  include <dirent.h>
 | 
			
		||||
#  endif
 | 
			
		||||
#  include <unistd.h>
 | 
			
		||||
#  if defined(ORBIS)
 | 
			
		||||
#  include <sys/fcntl.h>
 | 
			
		||||
#  include <sys/dirent.h>
 | 
			
		||||
#  include <orbisFile.h>
 | 
			
		||||
#  endif
 | 
			
		||||
#  if defined(WIIU)
 | 
			
		||||
#  include <malloc.h>
 | 
			
		||||
#  endif
 | 
			
		||||
| 
						 | 
				
			
			@ -74,11 +69,6 @@
 | 
			
		|||
#  include <psp2/io/fcntl.h>
 | 
			
		||||
#  include <psp2/io/dirent.h>
 | 
			
		||||
#  include <psp2/io/stat.h>
 | 
			
		||||
#elif defined(ORBIS)
 | 
			
		||||
#  include <orbisFile.h>
 | 
			
		||||
#  include <ps4link.h>
 | 
			
		||||
#  include <sys/dirent.h>
 | 
			
		||||
#  include <sys/fcntl.h>
 | 
			
		||||
#elif !defined(_WIN32)
 | 
			
		||||
#  if defined(PSP)
 | 
			
		||||
#    include <pspiofilemgr.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -124,19 +114,66 @@
 | 
			
		|||
#include <unistd.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(ORBIS)
 | 
			
		||||
#include <orbisFile.h>
 | 
			
		||||
#include <sys/fcntl.h>
 | 
			
		||||
#include <sys/dirent.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(PSP)
 | 
			
		||||
#include <pspkernel.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(__PS3__) || defined(__PSL1GHT__)
 | 
			
		||||
#include <defines/ps3_defines.h>
 | 
			
		||||
#if defined(__PSL1GHT__)
 | 
			
		||||
#define FS_SUCCEEDED 0
 | 
			
		||||
#define FS_TYPE_DIR 1
 | 
			
		||||
#ifdef __PSL1GHT__
 | 
			
		||||
#include <lv2/sysfs.h>
 | 
			
		||||
#ifndef O_RDONLY
 | 
			
		||||
#define O_RDONLY SYS_O_RDONLY
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef O_WRONLY
 | 
			
		||||
#define O_WRONLY SYS_O_WRONLY
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef O_CREAT
 | 
			
		||||
#define O_CREAT SYS_O_CREAT
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef O_TRUNC
 | 
			
		||||
#define O_TRUNC SYS_O_TRUNC
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef O_RDWR
 | 
			
		||||
#define O_RDWR SYS_O_RDWR
 | 
			
		||||
#endif
 | 
			
		||||
#else
 | 
			
		||||
#include <cell/cell_fs.h>
 | 
			
		||||
#ifndef O_RDONLY
 | 
			
		||||
#define O_RDONLY CELL_FS_O_RDONLY
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef O_WRONLY
 | 
			
		||||
#define O_WRONLY CELL_FS_O_WRONLY
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef O_CREAT
 | 
			
		||||
#define O_CREAT CELL_FS_O_CREAT
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef O_TRUNC
 | 
			
		||||
#define O_TRUNC CELL_FS_O_TRUNC
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef O_RDWR
 | 
			
		||||
#define O_RDWR CELL_FS_O_RDWR
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef sysFsStat
 | 
			
		||||
#define sysFsStat cellFsStat
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef sysFSDirent
 | 
			
		||||
#define sysFSDirent CellFsDirent
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef sysFsOpendir
 | 
			
		||||
#define sysFsOpendir cellFsOpendir
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef sysFsReaddir
 | 
			
		||||
#define sysFsReaddir cellFsReaddir
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef sysFSDirent
 | 
			
		||||
#define sysFSDirent CellFsDirent
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef sysFsClosedir
 | 
			
		||||
#define sysFsClosedir cellFsClosedir
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -200,13 +237,6 @@ int64_t retro_vfs_file_seek_internal(
 | 
			
		|||
#ifdef ATLEAST_VC2005
 | 
			
		||||
      /* VC2005 and up have a special 64-bit fseek */
 | 
			
		||||
      return _fseeki64(stream->fp, offset, whence);
 | 
			
		||||
#elif defined(ORBIS)
 | 
			
		||||
      {
 | 
			
		||||
         int ret = orbisLseek(stream->fd, offset, whence);
 | 
			
		||||
         if (ret < 0)
 | 
			
		||||
            return -1;
 | 
			
		||||
         return 0;
 | 
			
		||||
      }
 | 
			
		||||
#elif defined(HAVE_64BIT_OFFSETS)
 | 
			
		||||
      return fseeko(stream->fp, (off_t)offset, whence);
 | 
			
		||||
#else
 | 
			
		||||
| 
						 | 
				
			
			@ -269,19 +299,6 @@ int64_t retro_vfs_file_seek_internal(
 | 
			
		|||
libretro_vfs_implementation_file *retro_vfs_file_open_impl(
 | 
			
		||||
      const char *path, unsigned mode, unsigned hints)
 | 
			
		||||
{
 | 
			
		||||
#if defined(VFS_FRONTEND) || defined(HAVE_CDROM)
 | 
			
		||||
   int                             path_len = (int)strlen(path);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef VFS_FRONTEND
 | 
			
		||||
   const char                 *dumb_prefix  = "vfsonly://";
 | 
			
		||||
   size_t                   dumb_prefix_siz = STRLEN_CONST("vfsonly://");
 | 
			
		||||
   int                      dumb_prefix_len = (int)dumb_prefix_siz;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef HAVE_CDROM
 | 
			
		||||
   const char *cdrom_prefix                 = "cdrom://";
 | 
			
		||||
   size_t cdrom_prefix_siz                  = STRLEN_CONST("cdrom://");
 | 
			
		||||
   int cdrom_prefix_len                     = (int)cdrom_prefix_siz;
 | 
			
		||||
#endif
 | 
			
		||||
   int                                flags = 0;
 | 
			
		||||
   const char                     *mode_str = NULL;
 | 
			
		||||
   libretro_vfs_implementation_file *stream = 
 | 
			
		||||
| 
						 | 
				
			
			@ -306,9 +323,18 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
 | 
			
		|||
   stream->scheme                 = VFS_SCHEME_NONE;
 | 
			
		||||
 | 
			
		||||
#ifdef VFS_FRONTEND
 | 
			
		||||
   if (path_len >= dumb_prefix_len)
 | 
			
		||||
      if (!memcmp(path, dumb_prefix, dumb_prefix_len))
 | 
			
		||||
         path             += dumb_prefix_siz;
 | 
			
		||||
   if (     path
 | 
			
		||||
         && path[0] == 'v'
 | 
			
		||||
         && path[1] == 'f'
 | 
			
		||||
         && path[2] == 's'
 | 
			
		||||
         && path[3] == 'o'
 | 
			
		||||
         && path[4] == 'n'
 | 
			
		||||
         && path[5] == 'l'
 | 
			
		||||
         && path[6] == 'y'
 | 
			
		||||
         && path[7] == ':'
 | 
			
		||||
         && path[8] == '/'
 | 
			
		||||
         && path[9] == '/')
 | 
			
		||||
         path             += sizeof("vfsonly://")-1;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CDROM
 | 
			
		||||
| 
						 | 
				
			
			@ -325,13 +351,19 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
 | 
			
		|||
   stream->cdrom.last_frame[0]    = '\0';
 | 
			
		||||
   stream->cdrom.last_frame_valid = false;
 | 
			
		||||
 | 
			
		||||
   if (path_len > cdrom_prefix_len)
 | 
			
		||||
   if (     path
 | 
			
		||||
         && path[0] == 'c'
 | 
			
		||||
         && path[1] == 'd'
 | 
			
		||||
         && path[2] == 'r'
 | 
			
		||||
         && path[3] == 'o'
 | 
			
		||||
         && path[4] == 'm'
 | 
			
		||||
         && path[5] == ':'
 | 
			
		||||
         && path[6] == '/'
 | 
			
		||||
         && path[7] == '/'
 | 
			
		||||
         && path[8] != '\0')
 | 
			
		||||
   {
 | 
			
		||||
      if (!memcmp(path, cdrom_prefix, cdrom_prefix_len))
 | 
			
		||||
      {
 | 
			
		||||
         path             += cdrom_prefix_siz;
 | 
			
		||||
         stream->scheme    = VFS_SCHEME_CDROM;
 | 
			
		||||
      }
 | 
			
		||||
      path             += sizeof("cdrom://")-1;
 | 
			
		||||
      stream->scheme    = VFS_SCHEME_CDROM;
 | 
			
		||||
   }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -359,24 +391,20 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
 | 
			
		|||
         mode_str = "wb";
 | 
			
		||||
 | 
			
		||||
         flags    = O_WRONLY | O_CREAT | O_TRUNC;
 | 
			
		||||
#if !defined(ORBIS)
 | 
			
		||||
#if !defined(_WIN32)
 | 
			
		||||
         flags   |= S_IRUSR | S_IWUSR;
 | 
			
		||||
#else
 | 
			
		||||
         flags   |= O_BINARY;
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
         break;
 | 
			
		||||
 | 
			
		||||
      case RETRO_VFS_FILE_ACCESS_READ_WRITE:
 | 
			
		||||
         mode_str = "w+b";
 | 
			
		||||
         flags    = O_RDWR | O_CREAT | O_TRUNC;
 | 
			
		||||
#if !defined(ORBIS)
 | 
			
		||||
#if !defined(_WIN32)
 | 
			
		||||
         flags   |= S_IRUSR | S_IWUSR;
 | 
			
		||||
#else
 | 
			
		||||
         flags   |= O_BINARY;
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
         break;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -385,12 +413,10 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
 | 
			
		|||
         mode_str = "r+b";
 | 
			
		||||
 | 
			
		||||
         flags    = O_RDWR;
 | 
			
		||||
#if !defined(ORBIS)
 | 
			
		||||
#if !defined(_WIN32)
 | 
			
		||||
         flags   |= S_IRUSR | S_IWUSR;
 | 
			
		||||
#else
 | 
			
		||||
         flags   |= O_BINARY;
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
         break;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -400,15 +426,6 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
 | 
			
		|||
 | 
			
		||||
   if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
 | 
			
		||||
   {
 | 
			
		||||
#ifdef ORBIS
 | 
			
		||||
      int fd = orbisOpen(path, flags, 0644);
 | 
			
		||||
      if (fd < 0)
 | 
			
		||||
      {
 | 
			
		||||
         stream->fd = -1;
 | 
			
		||||
         goto error;
 | 
			
		||||
      }
 | 
			
		||||
      stream->fd    = fd;
 | 
			
		||||
#else
 | 
			
		||||
      FILE *fp;
 | 
			
		||||
#ifdef HAVE_CDROM
 | 
			
		||||
      if (stream->scheme == VFS_SCHEME_CDROM)
 | 
			
		||||
| 
						 | 
				
			
			@ -425,13 +442,12 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
 | 
			
		|||
      else
 | 
			
		||||
#endif
 | 
			
		||||
      {
 | 
			
		||||
         fp = (FILE*)fopen_utf8(path, mode_str);
 | 
			
		||||
 | 
			
		||||
         if (!fp)
 | 
			
		||||
         if (!(fp = (FILE*)fopen_utf8(path, mode_str)))
 | 
			
		||||
            goto error;
 | 
			
		||||
 | 
			
		||||
         stream->fp  = fp;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      /* Regarding setvbuf:
 | 
			
		||||
       *
 | 
			
		||||
       * https://www.freebsd.org/cgi/man.cgi?query=setvbuf&apropos=0&sektion=0&manpath=FreeBSD+11.1-RELEASE&arch=default&format=html
 | 
			
		||||
| 
						 | 
				
			
			@ -455,19 +471,14 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
 | 
			
		|||
#elif defined(WIIU)
 | 
			
		||||
      if (stream->scheme != VFS_SCHEME_CDROM)
 | 
			
		||||
      {
 | 
			
		||||
         const int bufsize = 128*1024;
 | 
			
		||||
         const int bufsize = 128 * 1024;
 | 
			
		||||
         stream->buf = (char*)memalign(0x40, bufsize);
 | 
			
		||||
         if (stream->fp)
 | 
			
		||||
            setvbuf(stream->fp, stream->buf, _IOFBF, bufsize);
 | 
			
		||||
      }
 | 
			
		||||
#elif !defined(PSP)
 | 
			
		||||
      if (stream->scheme != VFS_SCHEME_CDROM)
 | 
			
		||||
      {
 | 
			
		||||
         stream->buf = (char*)calloc(1, 0x4000);
 | 
			
		||||
         if (stream->fp)
 | 
			
		||||
            setvbuf(stream->fp, stream->buf, _IOFBF, 0x4000);
 | 
			
		||||
      }
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
   }
 | 
			
		||||
   else
 | 
			
		||||
| 
						 | 
				
			
			@ -503,18 +514,12 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
 | 
			
		|||
 | 
			
		||||
         retro_vfs_file_seek_internal(stream, 0, SEEK_SET);
 | 
			
		||||
 | 
			
		||||
         stream->mapped = (uint8_t*)mmap((void*)0,
 | 
			
		||||
               stream->mapsize, PROT_READ,  MAP_SHARED, stream->fd, 0);
 | 
			
		||||
 | 
			
		||||
         if (stream->mapped == MAP_FAILED)
 | 
			
		||||
         if ((stream->mapped = (uint8_t*)mmap((void*)0,
 | 
			
		||||
               stream->mapsize, PROT_READ,  MAP_SHARED, stream->fd, 0)) == MAP_FAILED)
 | 
			
		||||
            stream->hints &= ~RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS;
 | 
			
		||||
      }
 | 
			
		||||
#endif
 | 
			
		||||
   }
 | 
			
		||||
#ifdef ORBIS
 | 
			
		||||
   stream->size = orbisLseek(stream->fd, 0, SEEK_END);
 | 
			
		||||
   orbisLseek(stream->fd, 0, SEEK_SET);
 | 
			
		||||
#else
 | 
			
		||||
#ifdef HAVE_CDROM
 | 
			
		||||
   if (stream->scheme == VFS_SCHEME_CDROM)
 | 
			
		||||
   {
 | 
			
		||||
| 
						 | 
				
			
			@ -535,7 +540,6 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
 | 
			
		|||
 | 
			
		||||
      retro_vfs_file_seek_internal(stream, 0, SEEK_SET);
 | 
			
		||||
   }
 | 
			
		||||
#endif
 | 
			
		||||
   return stream;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
| 
						 | 
				
			
			@ -570,14 +574,7 @@ int retro_vfs_file_close_impl(libretro_vfs_implementation_file *stream)
 | 
			
		|||
   }
 | 
			
		||||
 | 
			
		||||
   if (stream->fd > 0)
 | 
			
		||||
   {
 | 
			
		||||
#ifdef ORBIS
 | 
			
		||||
      orbisClose(stream->fd);
 | 
			
		||||
      stream->fd = -1;
 | 
			
		||||
#else
 | 
			
		||||
      close(stream->fd);
 | 
			
		||||
#endif
 | 
			
		||||
   }
 | 
			
		||||
#ifdef HAVE_CDROM
 | 
			
		||||
end:
 | 
			
		||||
   if (stream->cdrom.cue_buf)
 | 
			
		||||
| 
						 | 
				
			
			@ -600,12 +597,7 @@ int retro_vfs_file_error_impl(libretro_vfs_implementation_file *stream)
 | 
			
		|||
   if (stream->scheme == VFS_SCHEME_CDROM)
 | 
			
		||||
      return retro_vfs_file_error_cdrom(stream);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef ORBIS
 | 
			
		||||
   /* TODO/FIXME - implement this? */
 | 
			
		||||
   return 0;
 | 
			
		||||
#else
 | 
			
		||||
   return ferror(stream->fp);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int64_t retro_vfs_file_size_impl(libretro_vfs_implementation_file *stream)
 | 
			
		||||
| 
						 | 
				
			
			@ -617,18 +609,20 @@ int64_t retro_vfs_file_size_impl(libretro_vfs_implementation_file *stream)
 | 
			
		|||
 | 
			
		||||
int64_t retro_vfs_file_truncate_impl(libretro_vfs_implementation_file *stream, int64_t length)
 | 
			
		||||
{
 | 
			
		||||
   if (!stream)
 | 
			
		||||
      return -1;
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
   if (_chsize(_fileno(stream->fp), length) != 0)
 | 
			
		||||
      return -1;
 | 
			
		||||
   if (stream && _chsize(_fileno(stream->fp), length) == 0)
 | 
			
		||||
   {
 | 
			
		||||
	   stream->size = length;
 | 
			
		||||
	   return 0;
 | 
			
		||||
   }
 | 
			
		||||
#elif !defined(VITA) && !defined(PSP) && !defined(PS2) && !defined(ORBIS) && (!defined(SWITCH) || defined(HAVE_LIBNX))
 | 
			
		||||
   if (ftruncate(fileno(stream->fp), (off_t)length) != 0)
 | 
			
		||||
      return -1;
 | 
			
		||||
   if (stream && ftruncate(fileno(stream->fp), (off_t)length) == 0)
 | 
			
		||||
   {
 | 
			
		||||
      stream->size = length;
 | 
			
		||||
      return 0;
 | 
			
		||||
   }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
   return 0;
 | 
			
		||||
   return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream)
 | 
			
		||||
| 
						 | 
				
			
			@ -642,14 +636,6 @@ int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream)
 | 
			
		|||
      if (stream->scheme == VFS_SCHEME_CDROM)
 | 
			
		||||
         return retro_vfs_file_tell_cdrom(stream);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef ORBIS
 | 
			
		||||
      {
 | 
			
		||||
         int64_t ret = orbisLseek(stream->fd, 0, SEEK_CUR);
 | 
			
		||||
         if (ret < 0)
 | 
			
		||||
            return -1;
 | 
			
		||||
         return ret;
 | 
			
		||||
      }
 | 
			
		||||
#else
 | 
			
		||||
#ifdef ATLEAST_VC2005
 | 
			
		||||
      /* VC2005 and up have a special 64-bit ftell */
 | 
			
		||||
      return _ftelli64(stream->fp);
 | 
			
		||||
| 
						 | 
				
			
			@ -657,7 +643,6 @@ int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream)
 | 
			
		|||
      return ftello(stream->fp);
 | 
			
		||||
#else
 | 
			
		||||
      return ftell(stream->fp);
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
   }
 | 
			
		||||
#ifdef HAVE_MMAP
 | 
			
		||||
| 
						 | 
				
			
			@ -676,21 +661,7 @@ int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream)
 | 
			
		|||
int64_t retro_vfs_file_seek_impl(libretro_vfs_implementation_file *stream,
 | 
			
		||||
      int64_t offset, int seek_position)
 | 
			
		||||
{
 | 
			
		||||
   int whence = -1;
 | 
			
		||||
   switch (seek_position)
 | 
			
		||||
   {
 | 
			
		||||
      case RETRO_VFS_SEEK_POSITION_START:
 | 
			
		||||
         whence = SEEK_SET;
 | 
			
		||||
         break;
 | 
			
		||||
      case RETRO_VFS_SEEK_POSITION_CURRENT:
 | 
			
		||||
         whence = SEEK_CUR;
 | 
			
		||||
         break;
 | 
			
		||||
      case RETRO_VFS_SEEK_POSITION_END:
 | 
			
		||||
         whence = SEEK_END;
 | 
			
		||||
         break;
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   return retro_vfs_file_seek_internal(stream, offset, whence);
 | 
			
		||||
   return retro_vfs_file_seek_internal(stream, offset, seek_position);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream,
 | 
			
		||||
| 
						 | 
				
			
			@ -705,13 +676,7 @@ int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream,
 | 
			
		|||
      if (stream->scheme == VFS_SCHEME_CDROM)
 | 
			
		||||
         return retro_vfs_file_read_cdrom(stream, s, len);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef ORBIS
 | 
			
		||||
      if (orbisRead(stream->fd, s, (size_t)len) < 0)
 | 
			
		||||
         return -1;
 | 
			
		||||
      return 0;
 | 
			
		||||
#else
 | 
			
		||||
      return fread(s, 1, (size_t)len, stream->fp);
 | 
			
		||||
#endif
 | 
			
		||||
   }
 | 
			
		||||
#ifdef HAVE_MMAP
 | 
			
		||||
   if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
 | 
			
		||||
| 
						 | 
				
			
			@ -734,36 +699,41 @@ int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream,
 | 
			
		|||
 | 
			
		||||
int64_t retro_vfs_file_write_impl(libretro_vfs_implementation_file *stream, const void *s, uint64_t len)
 | 
			
		||||
{
 | 
			
		||||
   int64_t pos   = 0;
 | 
			
		||||
   size_t result = -1;
 | 
			
		||||
 | 
			
		||||
   if (!stream)
 | 
			
		||||
      return -1;
 | 
			
		||||
 | 
			
		||||
   if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
 | 
			
		||||
   {
 | 
			
		||||
#ifdef ORBIS
 | 
			
		||||
      if (orbisWrite(stream->fd, s, (size_t)len) < 0)
 | 
			
		||||
         return -1;
 | 
			
		||||
      return 0;
 | 
			
		||||
#else
 | 
			
		||||
      return fwrite(s, 1, (size_t)len, stream->fp);
 | 
			
		||||
#endif
 | 
			
		||||
   }
 | 
			
		||||
      pos    = retro_vfs_file_tell_impl(stream);
 | 
			
		||||
      result = fwrite(s, 1, (size_t)len, stream->fp);
 | 
			
		||||
 | 
			
		||||
      if (result != -1 && pos + result > stream->size)
 | 
			
		||||
         stream->size = pos + result;
 | 
			
		||||
 | 
			
		||||
      return result;
 | 
			
		||||
   }
 | 
			
		||||
#ifdef HAVE_MMAP
 | 
			
		||||
   if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
 | 
			
		||||
      return -1;
 | 
			
		||||
#endif
 | 
			
		||||
   return write(stream->fd, s, (size_t)len);
 | 
			
		||||
 | 
			
		||||
   pos    = retro_vfs_file_tell_impl(stream);
 | 
			
		||||
   result = write(stream->fd, s, (size_t)len);
 | 
			
		||||
 | 
			
		||||
   if (result != -1 && pos + result > stream->size)
 | 
			
		||||
      stream->size = pos + result;
 | 
			
		||||
 | 
			
		||||
   return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int retro_vfs_file_flush_impl(libretro_vfs_implementation_file *stream)
 | 
			
		||||
{
 | 
			
		||||
   if (!stream)
 | 
			
		||||
      return -1;
 | 
			
		||||
#ifdef ORBIS
 | 
			
		||||
   return 0;
 | 
			
		||||
#else
 | 
			
		||||
   return fflush(stream->fp) == 0 ? 0 : -1;
 | 
			
		||||
#endif
 | 
			
		||||
   if (stream && fflush(stream->fp) == 0)
 | 
			
		||||
      return 0;
 | 
			
		||||
   return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int retro_vfs_file_remove_impl(const char *path)
 | 
			
		||||
| 
						 | 
				
			
			@ -779,9 +749,7 @@ int retro_vfs_file_remove_impl(const char *path)
 | 
			
		|||
   if (!path || !*path)
 | 
			
		||||
      return -1;
 | 
			
		||||
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500
 | 
			
		||||
   path_local = utf8_to_local_string_alloc(path);
 | 
			
		||||
 | 
			
		||||
   if (path_local)
 | 
			
		||||
   if ((path_local = utf8_to_local_string_alloc(path)))
 | 
			
		||||
   {
 | 
			
		||||
      int ret = remove(path_local);
 | 
			
		||||
      free(path_local);
 | 
			
		||||
| 
						 | 
				
			
			@ -790,9 +758,7 @@ int retro_vfs_file_remove_impl(const char *path)
 | 
			
		|||
         return 0;
 | 
			
		||||
   }
 | 
			
		||||
#else
 | 
			
		||||
   path_wide = utf8_to_utf16_string_alloc(path);
 | 
			
		||||
 | 
			
		||||
   if (path_wide)
 | 
			
		||||
   if ((path_wide = utf8_to_utf16_string_alloc(path)))
 | 
			
		||||
   {
 | 
			
		||||
      int ret = _wremove(path_wide);
 | 
			
		||||
      free(path_wide);
 | 
			
		||||
| 
						 | 
				
			
			@ -801,16 +767,11 @@ int retro_vfs_file_remove_impl(const char *path)
 | 
			
		|||
         return 0;
 | 
			
		||||
   }
 | 
			
		||||
#endif
 | 
			
		||||
   return -1;
 | 
			
		||||
#elif defined(ORBIS)
 | 
			
		||||
   /* Orbis
 | 
			
		||||
    * TODO/FIXME - stub for now */
 | 
			
		||||
   return 0;
 | 
			
		||||
#else
 | 
			
		||||
   if (remove(path) == 0)
 | 
			
		||||
      return 0;
 | 
			
		||||
   return -1;
 | 
			
		||||
#endif
 | 
			
		||||
   return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int retro_vfs_file_rename_impl(const char *old_path, const char *new_path)
 | 
			
		||||
| 
						 | 
				
			
			@ -862,13 +823,6 @@ int retro_vfs_file_rename_impl(const char *old_path, const char *new_path)
 | 
			
		|||
#endif
 | 
			
		||||
   return ret;
 | 
			
		||||
 | 
			
		||||
#elif defined(ORBIS)
 | 
			
		||||
   /* Orbis */
 | 
			
		||||
   /* TODO/FIXME - Stub for now */
 | 
			
		||||
   if (!old_path || !*old_path || !new_path || !*new_path)
 | 
			
		||||
      return -1;
 | 
			
		||||
   return 0;
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
   /* Every other platform */
 | 
			
		||||
   if (!old_path || !*old_path || !new_path || !*new_path)
 | 
			
		||||
| 
						 | 
				
			
			@ -890,7 +844,7 @@ int retro_vfs_stat_impl(const char *path, int32_t *size)
 | 
			
		|||
{
 | 
			
		||||
   bool is_dir               = false;
 | 
			
		||||
   bool is_character_special = false;
 | 
			
		||||
#if defined(VITA) || defined(PSP)
 | 
			
		||||
#if defined(VITA)
 | 
			
		||||
   /* Vita / PSP */
 | 
			
		||||
   SceIoStat buf;
 | 
			
		||||
   int dir_ret;
 | 
			
		||||
| 
						 | 
				
			
			@ -903,7 +857,7 @@ int retro_vfs_stat_impl(const char *path, int32_t *size)
 | 
			
		|||
   tmp                       = strdup(path);
 | 
			
		||||
   len                       = strlen(tmp);
 | 
			
		||||
   if (tmp[len-1] == '/')
 | 
			
		||||
      tmp[len-1] = '\0';
 | 
			
		||||
      tmp[len-1]             = '\0';
 | 
			
		||||
 | 
			
		||||
   dir_ret                   = sceIoGetstat(tmp, &buf);
 | 
			
		||||
   free(tmp);
 | 
			
		||||
| 
						 | 
				
			
			@ -914,21 +868,6 @@ int retro_vfs_stat_impl(const char *path, int32_t *size)
 | 
			
		|||
      *size                  = (int32_t)buf.st_size;
 | 
			
		||||
 | 
			
		||||
   is_dir                    = FIO_S_ISDIR(buf.st_mode);
 | 
			
		||||
#elif defined(ORBIS)
 | 
			
		||||
   /* Orbis */
 | 
			
		||||
   int dir_ret               = 0;
 | 
			
		||||
 | 
			
		||||
   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__)
 | 
			
		||||
   /* Lowlevel Lv2 */
 | 
			
		||||
   sysFSStat buf;
 | 
			
		||||
| 
						 | 
				
			
			@ -994,12 +933,10 @@ int retro_vfs_stat_impl(const char *path, int32_t *size)
 | 
			
		|||
   if (string_is_empty(path))
 | 
			
		||||
      return 0;
 | 
			
		||||
 | 
			
		||||
   path_buf = strdup(path);
 | 
			
		||||
   if (!path_buf)
 | 
			
		||||
   if (!(path_buf = strdup(path)))
 | 
			
		||||
      return 0;
 | 
			
		||||
 | 
			
		||||
   len = strlen(path_buf);
 | 
			
		||||
   if (len > 0)
 | 
			
		||||
   if ((len = strlen(path_buf)) > 0)
 | 
			
		||||
      if (path_buf[len - 1] == '/')
 | 
			
		||||
         path_buf[len - 1] = '\0';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1035,7 +972,7 @@ int retro_vfs_stat_impl(const char *path, int32_t *size)
 | 
			
		|||
 | 
			
		||||
#if defined(VITA)
 | 
			
		||||
#define path_mkdir_error(ret) (((ret) == SCE_ERROR_ERRNO_EEXIST))
 | 
			
		||||
#elif defined(PSP) || defined(PS2) || defined(_3DS) || defined(WIIU) || defined(SWITCH) || defined(ORBIS)
 | 
			
		||||
#elif defined(PSP) || defined(PS2) || defined(_3DS) || defined(WIIU) || defined(SWITCH)
 | 
			
		||||
#define path_mkdir_error(ret) ((ret) == -1)
 | 
			
		||||
#else
 | 
			
		||||
#define path_mkdir_error(ret) ((ret) < 0 && errno == EEXIST)
 | 
			
		||||
| 
						 | 
				
			
			@ -1058,13 +995,11 @@ int retro_vfs_mkdir_impl(const char *dir)
 | 
			
		|||
#endif
 | 
			
		||||
#elif defined(IOS)
 | 
			
		||||
   int ret = mkdir(dir, 0755);
 | 
			
		||||
#elif defined(VITA) || defined(PSP)
 | 
			
		||||
#elif defined(VITA)
 | 
			
		||||
   int ret = sceIoMkdir(dir, 0777);
 | 
			
		||||
#elif defined(ORBIS)
 | 
			
		||||
   int ret = orbisMkdir(dir, 0755);
 | 
			
		||||
#elif defined(__QNX__)
 | 
			
		||||
   int ret = mkdir(dir, 0777);
 | 
			
		||||
#elif defined(GEKKO)
 | 
			
		||||
#elif defined(GEKKO) || defined(WIIU)
 | 
			
		||||
   /* On GEKKO platforms, mkdir() fails if
 | 
			
		||||
    * the path has a trailing slash. We must
 | 
			
		||||
    * therefore remove it. */
 | 
			
		||||
| 
						 | 
				
			
			@ -1111,16 +1046,13 @@ struct libretro_vfs_implementation_dir
 | 
			
		|||
   HANDLE directory;
 | 
			
		||||
   bool next;
 | 
			
		||||
   char path[PATH_MAX_LENGTH];
 | 
			
		||||
#elif defined(VITA) || defined(PSP)
 | 
			
		||||
#elif defined(VITA)
 | 
			
		||||
   SceUID directory;
 | 
			
		||||
   SceIoDirent entry;
 | 
			
		||||
#elif defined(__PSL1GHT__) || defined(__PS3__)
 | 
			
		||||
   int error;
 | 
			
		||||
   int directory;
 | 
			
		||||
   sysFSDirent entry;
 | 
			
		||||
#elif defined(ORBIS)
 | 
			
		||||
   int directory;
 | 
			
		||||
   struct dirent entry;
 | 
			
		||||
#else
 | 
			
		||||
   DIR *directory;
 | 
			
		||||
   const struct dirent *entry;
 | 
			
		||||
| 
						 | 
				
			
			@ -1131,7 +1063,7 @@ static bool dirent_check_error(libretro_vfs_implementation_dir *rdir)
 | 
			
		|||
{
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
   return (rdir->directory == INVALID_HANDLE_VALUE);
 | 
			
		||||
#elif defined(VITA) || defined(PSP) || defined(ORBIS)
 | 
			
		||||
#elif defined(VITA) || defined(ORBIS)
 | 
			
		||||
   return (rdir->directory < 0);
 | 
			
		||||
#elif defined(__PSL1GHT__) || defined(__PS3__)
 | 
			
		||||
   return (rdir->error != FS_SUCCEEDED);
 | 
			
		||||
| 
						 | 
				
			
			@ -1144,7 +1076,6 @@ libretro_vfs_implementation_dir *retro_vfs_opendir_impl(
 | 
			
		|||
      const char *name, bool include_hidden)
 | 
			
		||||
{
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
   unsigned path_len;
 | 
			
		||||
   char path_buf[1024];
 | 
			
		||||
   size_t copied      = 0;
 | 
			
		||||
#if defined(LEGACY_WIN32)
 | 
			
		||||
| 
						 | 
				
			
			@ -1155,28 +1086,25 @@ libretro_vfs_implementation_dir *retro_vfs_opendir_impl(
 | 
			
		|||
#endif
 | 
			
		||||
   libretro_vfs_implementation_dir *rdir;
 | 
			
		||||
 | 
			
		||||
   /*Reject null or empty string paths*/
 | 
			
		||||
   /* Reject NULL or empty string paths*/
 | 
			
		||||
   if (!name || (*name == 0))
 | 
			
		||||
      return NULL;
 | 
			
		||||
 | 
			
		||||
   /*Allocate RDIR struct. Tidied later with retro_closedir*/
 | 
			
		||||
   rdir = (libretro_vfs_implementation_dir*)calloc(1, sizeof(*rdir));
 | 
			
		||||
   if (!rdir)
 | 
			
		||||
   if (!(rdir = (libretro_vfs_implementation_dir*)
 | 
			
		||||
            calloc(1, sizeof(*rdir))))
 | 
			
		||||
      return NULL;
 | 
			
		||||
 | 
			
		||||
   rdir->orig_path       = strdup(name);
 | 
			
		||||
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
   path_buf[0]           = '\0';
 | 
			
		||||
   path_len              = strlen(name);
 | 
			
		||||
 | 
			
		||||
   copied                = strlcpy(path_buf, name, sizeof(path_buf));
 | 
			
		||||
 | 
			
		||||
   /* Non-NT platforms don't like extra slashes in the path */
 | 
			
		||||
   if (name[path_len - 1] != '\\')
 | 
			
		||||
      path_buf[copied++]   = '\\';
 | 
			
		||||
   if (path_buf[copied - 1] != '\\')
 | 
			
		||||
      path_buf [copied++]  = '\\';
 | 
			
		||||
 | 
			
		||||
   path_buf[copied]        = '*';
 | 
			
		||||
   path_buf[copied  ]      = '*';
 | 
			
		||||
   path_buf[copied+1]      = '\0';
 | 
			
		||||
 | 
			
		||||
#if defined(LEGACY_WIN32)
 | 
			
		||||
| 
						 | 
				
			
			@ -1193,15 +1121,13 @@ libretro_vfs_implementation_dir *retro_vfs_opendir_impl(
 | 
			
		|||
      free(path_wide);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#elif defined(VITA) || defined(PSP)
 | 
			
		||||
#elif defined(VITA)
 | 
			
		||||
   rdir->directory       = sceIoDopen(name);
 | 
			
		||||
#elif defined(_3DS)
 | 
			
		||||
   rdir->directory       = !string_is_empty(name) ? opendir(name) : NULL;
 | 
			
		||||
   rdir->entry           = NULL;
 | 
			
		||||
#elif defined(__PSL1GHT__) || defined(__PS3__)
 | 
			
		||||
   rdir->error           = sysFsOpendir(name, &rdir->directory);
 | 
			
		||||
#elif defined(ORBIS)
 | 
			
		||||
   rdir->directory       = orbisDopen(name);
 | 
			
		||||
#else
 | 
			
		||||
   rdir->directory       = opendir(name);
 | 
			
		||||
   rdir->entry           = NULL;
 | 
			
		||||
| 
						 | 
				
			
			@ -1233,14 +1159,12 @@ bool retro_vfs_readdir_impl(libretro_vfs_implementation_dir *rdir)
 | 
			
		|||
 | 
			
		||||
   rdir->next = true;
 | 
			
		||||
   return (rdir->directory != INVALID_HANDLE_VALUE);
 | 
			
		||||
#elif defined(VITA) || defined(PSP)
 | 
			
		||||
#elif defined(VITA)
 | 
			
		||||
   return (sceIoDread(rdir->directory, &rdir->entry) > 0);
 | 
			
		||||
#elif defined(__PSL1GHT__) || defined(__PS3__)
 | 
			
		||||
   uint64_t nread;
 | 
			
		||||
   rdir->error = sysFsReaddir(rdir->directory, &rdir->entry, &nread);
 | 
			
		||||
   return (nread != 0);
 | 
			
		||||
#elif defined(ORBIS)
 | 
			
		||||
   return (orbisDread(rdir->directory, &rdir->entry) > 0);
 | 
			
		||||
#else
 | 
			
		||||
   return ((rdir->entry = readdir(rdir->directory)) != NULL);
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -1259,7 +1183,7 @@ const char *retro_vfs_dirent_get_name_impl(libretro_vfs_implementation_dir *rdir
 | 
			
		|||
   if (name)
 | 
			
		||||
      free(name);
 | 
			
		||||
   return (char*)rdir->entry.cFileName;
 | 
			
		||||
#elif defined(VITA) || defined(PSP) || defined(ORBIS) || defined(__PSL1GHT__) || defined(__PS3__)
 | 
			
		||||
#elif defined(VITA) || defined(__PSL1GHT__) || defined(__PS3__)
 | 
			
		||||
   return rdir->entry.d_name;
 | 
			
		||||
#else
 | 
			
		||||
   if (!rdir || !rdir->entry)
 | 
			
		||||
| 
						 | 
				
			
			@ -1273,22 +1197,12 @@ bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *rdir)
 | 
			
		|||
#if defined(_WIN32)
 | 
			
		||||
   const WIN32_FIND_DATA *entry = (const WIN32_FIND_DATA*)&rdir->entry;
 | 
			
		||||
   return entry->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
 | 
			
		||||
#elif defined(PSP) || defined(VITA)
 | 
			
		||||
   const SceIoDirent *entry     = (const SceIoDirent*)&rdir->entry;
 | 
			
		||||
#if defined(PSP)
 | 
			
		||||
   return (entry->d_stat.st_attr & FIO_SO_IFDIR) == FIO_SO_IFDIR;
 | 
			
		||||
#elif defined(VITA)
 | 
			
		||||
   const SceIoDirent *entry     = (const SceIoDirent*)&rdir->entry;
 | 
			
		||||
   return SCE_S_ISDIR(entry->d_stat.st_mode);
 | 
			
		||||
#endif
 | 
			
		||||
#elif defined(__PSL1GHT__) || defined(__PS3__)
 | 
			
		||||
   sysFSDirent *entry          = (sysFSDirent*)&rdir->entry;
 | 
			
		||||
   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
 | 
			
		||||
   struct stat buf;
 | 
			
		||||
   char path[PATH_MAX_LENGTH];
 | 
			
		||||
| 
						 | 
				
			
			@ -1301,8 +1215,7 @@ bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *rdir)
 | 
			
		|||
      return false;
 | 
			
		||||
#endif
 | 
			
		||||
   /* dirent struct doesn't have d_type, do it the slow way ... */
 | 
			
		||||
   path[0] = '\0';
 | 
			
		||||
   fill_pathname_join(path, rdir->orig_path, retro_vfs_dirent_get_name_impl(rdir), sizeof(path));
 | 
			
		||||
   fill_pathname_join_special(path, rdir->orig_path, retro_vfs_dirent_get_name_impl(rdir), sizeof(path));
 | 
			
		||||
   if (stat(path, &buf) < 0)
 | 
			
		||||
      return false;
 | 
			
		||||
   return S_ISDIR(buf.st_mode);
 | 
			
		||||
| 
						 | 
				
			
			@ -1317,12 +1230,10 @@ int retro_vfs_closedir_impl(libretro_vfs_implementation_dir *rdir)
 | 
			
		|||
#if defined(_WIN32)
 | 
			
		||||
   if (rdir->directory != INVALID_HANDLE_VALUE)
 | 
			
		||||
      FindClose(rdir->directory);
 | 
			
		||||
#elif defined(VITA) || defined(PSP)
 | 
			
		||||
#elif defined(VITA)
 | 
			
		||||
   sceIoDclose(rdir->directory);
 | 
			
		||||
#elif defined(__PSL1GHT__) || defined(__PS3__)
 | 
			
		||||
   rdir->error = sysFsClosedir(rdir->directory);
 | 
			
		||||
#elif defined(ORBIS)
 | 
			
		||||
   orbisDclose(rdir->directory);
 | 
			
		||||
#else
 | 
			
		||||
   if (rdir->directory)
 | 
			
		||||
      closedir(rdir->directory);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue