Update libretro-common to latest upstream (2025)

Update vendored libretro-common from ~Oct 2021 to current upstream.
Brings 4+ years of bug fixes, new API support, platform compatibility
improvements, and security fixes.

Refs #105

Co-Authored-By: Claude Opus 4.6 <[email protected]>
This commit is contained in:
Joseph MattielloandClaude Opus 4.6 committed 2026-04-03 23:00:14 -04:00
1 parent e6d240005e
commit fa3462ae89
33 files changed
+11198 -5105

No files matched your search

+3 -5
View File
@@ -56,12 +56,11 @@ int retro_strcasecmp__(const char *a, const char *b)
char *retro_strdup__(const char *orig)
{
size_t len = strlen(orig) + 1;
char *ret = (char*)malloc(len);
size_t _len = strlen(orig) + 1;
char *ret = (char*)malloc(_len);
if (!ret)
return NULL;
strlcpy(ret, orig, len);
memcpy(ret, orig, _len);
return ret;
}
@@ -100,5 +99,4 @@ char *retro_strtok_r__(char *str, const char *delim, char **saveptr)
return first;
}
#endif
+13 -19
View File
@@ -35,12 +35,12 @@
static int c89_vscprintf_retro__(const char *fmt, va_list pargs)
{
int retval;
int _len;
va_list argcopy;
va_copy(argcopy, pargs);
retval = vsnprintf(NULL, 0, fmt, argcopy);
_len = vsnprintf(NULL, 0, fmt, argcopy);
va_end(argcopy);
return retval;
return _len;
}
#endif
@@ -48,36 +48,30 @@ static int c89_vscprintf_retro__(const char *fmt, va_list pargs)
int c99_vsnprintf_retro__(char *s, size_t len, const char *fmt, va_list ap)
{
int count = -1;
int _len = -1;
if (len != 0)
{
#if (_MSC_VER <= 1310)
count = _vsnprintf(s, len - 1, fmt, ap);
_len = _vsnprintf(s, len - 1, fmt, ap);
#else
count = _vsnprintf_s(s, len, len - 1, fmt, ap);
_len = _vsnprintf_s(s, len, len - 1, fmt, ap);
#endif
}
if (count == -1)
count = _vscprintf(fmt, ap);
if (_len == -1)
_len = _vscprintf(fmt, ap);
/* there was no room for a NULL, so truncate the last character */
if (count == len && len)
if (_len == len && len)
s[len - 1] = '\0';
return count;
return _len;
}
int c99_snprintf_retro__(char *s, size_t len, const char *fmt, ...)
{
int count;
int _len;
va_list ap;
va_start(ap, fmt);
count = c99_vsnprintf_retro__(s, len, fmt, ap);
_len = c99_vsnprintf_retro__(s, len, fmt, ap);
va_end(ap);
return count;
return _len;
}
#endif
+10 -12
View File
@@ -42,17 +42,15 @@ static int casencmp(const char *a, const char *b, size_t n)
char *strcasestr_retro__(const char *haystack, const char *needle)
{
size_t i, search_off;
size_t hay_len = strlen(haystack);
size_t needle_len = strlen(needle);
if (needle_len > hay_len)
return NULL;
search_off = hay_len - needle_len;
for (i = 0; i <= search_off; i++)
if (!casencmp(haystack + i, needle, needle_len))
return (char*)haystack + i;
size_t _len = strlen(needle);
size_t __len = strlen(haystack);
if (_len <= __len)
{
size_t i;
__len -= _len; /* offset */
for (i = 0; i <= __len; i++)
if (!casencmp(haystack + i, needle, _len))
return (char*)haystack + i;
}
return NULL;
}
+17 -33
View File
@@ -27,43 +27,27 @@
/* Implementation of strlcpy()/strlcat() based on OpenBSD. */
#ifndef __MACH__
size_t strlcpy(char *dest, const char *source, size_t size)
#if !(defined(__MACH__) && defined(__APPLE__))
size_t strlcpy(char *s, const char *in, size_t len)
{
size_t src_size = 0;
size_t n = size;
if (n)
while (--n && (*dest++ = *source++)) src_size++;
if (!n)
{
if (size) *dest = '\0';
while (*source++) src_size++;
}
return src_size;
size_t _len = strlen(in);
if (len)
{
size_t __len = _len < len - 1 ? _len : len - 1;
memcpy(s, in, __len);
s[__len] = '\0';
}
return _len;
}
size_t strlcat(char *dest, const char *source, size_t size)
size_t strlcat(char *s, const char *source, size_t len)
{
size_t len = strlen(dest);
dest += len;
if (len > size)
size = 0;
size_t _len = strlen(s);
s += _len;
if (_len > len)
len = 0;
else
size -= len;
return len + strlcpy(dest, source, size);
len -= _len;
return _len + strlcpy(s, source, len);
}
#endif
char *strldup(const char *s, size_t n)
{
char *dst = (char*)malloc(sizeof(char) * (n + 1));
strlcpy(dst, s, n);
return dst;
}
+16 -15
View File
@@ -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
+368 -198
View File
@@ -39,86 +39,197 @@
#define UTF8_WALKBYTE(string) (*((*(string))++))
static unsigned leading_ones(uint8_t c)
{
unsigned ones = 0;
while (c & 0x80)
{
ones++;
c <<= 1;
}
return ones;
}
/* Simple implementation. Assumes the sequence is
* properly synchronized and terminated. */
/* Lookup table replaces leading_ones() bit-counting loop.
* Index by high byte value >> 3 (32 entries) to get
* the number of leading 1-bits for any byte.
* Only values 0..7 are meaningful for UTF-8;
* entries for invalid prefixes are set to 0xFF. */
static const uint8_t utf8_lut[256] = {
/* 0x00..0x7F: 0 leading ones (ASCII) */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
/* 0x80..0xBF: 1 leading one (continuation byte) */
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
/* 0xC0..0xDF: 2 leading ones (2-byte sequence) */
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
/* 0xE0..0xEF: 3 leading ones (3-byte sequence) */
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
/* 0xF0..0xF7: 4 leading ones (4-byte sequence) */
4,4,4,4,4,4,4,4,
/* 0xF8..0xFB: 5 leading ones */
5,5,5,5,
/* 0xFC..0xFD: 6 leading ones */
6,6,
/* 0xFE..0xFF: 7+ leading ones (invalid) */
7,7
};
/**
* utf8_conv_utf32:
*
* Simple implementation. Assumes the sequence is
* properly synchronized and terminated.
*
* Optimized: replaced leading_ones() loop with LUT,
* fast-path for ASCII, and unrolled continuation-byte reads.
**/
size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
const char *in, size_t in_size)
{
unsigned i;
size_t ret = 0;
while (in_size && out_chars)
{
unsigned extra, shift;
uint32_t c;
uint8_t first = *in++;
unsigned ones = leading_ones(first);
uint8_t first;
unsigned ones;
if (ones > 6 || ones == 1) /* Invalid or desync. */
/* Fast path: batch ASCII characters */
while (in_size && out_chars && (uint8_t)*in < 0x80)
{
*out++ = (uint8_t)*in++;
in_size--;
out_chars--;
ret++;
}
if (!in_size || !out_chars)
break;
extra = ones ? ones - 1 : ones;
if (1 + extra > in_size) /* Overflow. */
first = (uint8_t)*in++;
ones = utf8_lut[first];
if (ones > 6 || ones < 2) /* Invalid or desync. */
break;
shift = (extra - 1) * 6;
c = (first & ((1 << (7 - ones)) - 1)) << (6 * extra);
/* ones includes the lead byte; we already consumed it,
* but need (ones - 1) more continuation bytes */
if (ones > in_size) /* Not enough data. */
break;
for (i = 0; i < extra; i++, in++, shift -= 6)
c |= (*in & 0x3f) << shift;
/* Decode based on sequence length to avoid inner loop */
c = first & ((1 << (7 - ones)) - 1);
switch (ones)
{
case 4:
c = (c << 6) | ((uint8_t)*in++ & 0x3F);
/* fall through */
case 3:
c = (c << 6) | ((uint8_t)*in++ & 0x3F);
/* fall through */
case 2:
c = (c << 6) | ((uint8_t)*in++ & 0x3F);
break;
default:
{
/* 5 or 6 byte sequences (ones == 5 or 6) */
unsigned i;
unsigned extra = ones - 1;
for (i = 0; i < extra; i++)
c = (c << 6) | ((uint8_t)*in++ & 0x3F);
break;
}
}
*out++ = c;
in_size -= 1 + extra;
*out++ = c;
in_size -= ones;
out_chars--;
ret++;
}
return ret;
}
/**
* utf16_conv_utf8:
*
* Leaf function.
*
* Optimized: separated counting-only path (out==NULL) from
* encoding path to eliminate per-byte branch on `out`.
* Added explicit fast-path for BMP 2-byte and 3-byte encodings.
**/
bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
const uint16_t *in, size_t in_size)
{
size_t out_pos = 0;
size_t in_pos = 0;
static const
uint8_t utf8_limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
size_t out_pos = 0;
size_t in_pos = 0;
if (!out)
{
/* Counting-only pass: no stores,
no per-byte `if (out)` branches */
for (;;)
{
uint32_t value;
if (in_pos == in_size)
{
*out_chars = out_pos;
return true;
}
value = in[in_pos++];
if (value < 0x80)
{
out_pos++;
continue;
}
if (value >= 0xD800 && value < 0xE000)
{
uint32_t c2;
if (value >= 0xDC00 || in_pos == in_size)
break;
c2 = in[in_pos++];
if (c2 < 0xDC00 || c2 >= 0xE000)
break;
value = (((value - 0xD800) << 10) | (c2 - 0xDC00)) + 0x10000;
}
if (value < 0x800)
out_pos += 2;
else if (value < 0x10000)
out_pos += 3;
else
out_pos += 4;
}
*out_chars = out_pos;
return false;
}
/* Encoding pass */
for (;;)
{
unsigned num_adds;
uint32_t value;
if (in_pos == in_size)
{
*out_chars = out_pos;
return true;
}
/* Batch ASCII run: avoid per-char branch into multi-byte path */
while (in_pos < in_size && in[in_pos] < 0x80)
out[out_pos++] = (uint8_t)in[in_pos++];
if (in_pos == in_size)
{
*out_chars = out_pos;
return true;
}
value = in[in_pos++];
if (value < 0x80)
{
if (out)
out[out_pos] = (char)value;
out_pos++;
continue;
}
if (value >= 0xD800 && value < 0xE000)
{
uint32_t c2;
if (value >= 0xDC00 || in_pos == in_size)
break;
c2 = in[in_pos++];
@@ -127,65 +238,91 @@ bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
value = (((value - 0xD800) << 10) | (c2 - 0xDC00)) + 0x10000;
}
for (num_adds = 1; num_adds < 5; num_adds++)
if (value < (((uint32_t)1) << (num_adds * 5 + 6)))
break;
if (out)
out[out_pos] = (char)(utf8_limits[num_adds - 1]
+ (value >> (6 * num_adds)));
out_pos++;
do
if (value < 0x800)
{
num_adds--;
if (out)
out[out_pos] = (char)(0x80
+ ((value >> (6 * num_adds)) & 0x3F));
out_pos++;
}while (num_adds != 0);
/* 2-byte sequence */
out[out_pos] = (uint8_t)(0xC0 | (value >> 6));
out[out_pos + 1] = (uint8_t)(0x80 | (value & 0x3F));
out_pos += 2;
}
else if (value < 0x10000)
{
/* 3-byte sequence */
out[out_pos] = (uint8_t)(0xE0 | (value >> 12));
out[out_pos + 1] = (uint8_t)(0x80 | ((value >> 6) & 0x3F));
out[out_pos + 2] = (uint8_t)(0x80 | (value & 0x3F));
out_pos += 3;
}
else
{
/* 4-byte sequence */
out[out_pos] = (uint8_t)(0xF0 | (value >> 18));
out[out_pos + 1] = (uint8_t)(0x80 | ((value >> 12) & 0x3F));
out[out_pos + 2] = (uint8_t)(0x80 | ((value >> 6) & 0x3F));
out[out_pos + 3] = (uint8_t)(0x80 | (value & 0x3F));
out_pos += 4;
}
}
*out_chars = out_pos;
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 @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 @len.
*
* Returns number of bytes. 's' is assumed valid UTF-8.
* Use only if 'chars' is considerably less than 'd_len'. */
size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars)
* @return Number of bytes.
**/
size_t utf8cpy(char *s, size_t len, const char *in, size_t chars)
{
const uint8_t *sb = (const uint8_t*)s;
size_t byte_count;
const uint8_t *sb = (const uint8_t*)in;
const uint8_t *sb_org = sb;
if (!s)
if (!in)
return 0;
while (*sb && chars-- > 0)
{
sb++;
while ((*sb & 0xC0) == 0x80)
sb++;
/* Use LUT to skip entire character at once
* instead of byte-by-byte continuation check */
unsigned ones = utf8_lut[*sb];
if (ones < 2)
sb++; /* ASCII or (invalid) standalone continuation */
else
sb += ones; /* Skip full multi-byte character */
}
if ((size_t)(sb - sb_org) > d_len-1 /* NUL */)
if ((size_t)(sb - sb_org) > len - 1)
{
sb = sb_org + d_len-1;
sb = sb_org + len - 1;
while ((*sb & 0xC0) == 0x80)
sb--;
}
memcpy(d, sb_org, sb-sb_org);
d[sb-sb_org] = '\0';
return sb-sb_org;
byte_count = (size_t)(sb - sb_org);
memcpy(s, sb_org, byte_count);
s[byte_count] = '\0';
return byte_count;
}
/**
* utf8skip:
*
* Leaf function.
*
* Optimized: use LUT to jump over entire multi-byte
* characters instead of scanning continuation bytes.
**/
const char *utf8skip(const char *str, size_t chars)
{
const uint8_t *strb = (const uint8_t*)str;
@@ -195,15 +332,33 @@ const char *utf8skip(const char *str, size_t chars)
do
{
strb++;
while ((*strb & 0xC0)==0x80)
unsigned ones;
if (!*strb)
break;
ones = utf8_lut[*strb];
if (ones < 2)
strb++;
chars--;
}while (chars);
else
{
/* Verify we don't walk past a NUL inside a multi-byte seq */
unsigned i;
for (i = 0; i < ones && strb[i]; i++)
;
strb += i;
}
} while (--chars);
return (const char*)strb;
}
/**
* utf8len:
*
* Leaf function.
*
* Optimized: use LUT to skip entire multi-byte sequences
* instead of testing each byte individually.
**/
size_t utf8len(const char *string)
{
size_t ret = 0;
@@ -213,97 +368,123 @@ size_t utf8len(const char *string)
while (*string)
{
if ((*string & 0xC0) != 0x80)
ret++;
string++;
unsigned ones = utf8_lut[(uint8_t)*string];
ret++;
/* ASCII (ones==0) or continuation byte (ones==1, shouldn't
* appear at sequence start in valid UTF-8). Either way,
* count it and advance one byte. */
if (ones < 2)
string++;
else /* Multi-byte lead: count one character, skip `ones` bytes */
string += ones;
}
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);
uint32_t ret = 0;
const uint8_t *s = (const uint8_t*)*string;
uint8_t first = *s++;
uint32_t ret;
if (first < 128)
return first;
ret = (ret << 6) | (UTF8_WALKBYTE(string) & 0x3F);
if (first >= 0xE0)
if (first < 0x80)
{
ret = (ret << 6) | (UTF8_WALKBYTE(string) & 0x3F);
if (first >= 0xF0)
{
ret = (ret << 6) | (UTF8_WALKBYTE(string) & 0x3F);
return ret | (first & 7) << 18;
}
return ret | (first & 15) << 12;
*string = (const char*)s;
return first;
}
return ret | (first & 31) << 6;
/* Use LUT + switch to decode, matching utf8_conv_utf32 style */
ret = first & ((1 << (7 - utf8_lut[first])) - 1);
switch (utf8_lut[first])
{
case 4:
ret = (ret << 6) | (*s++ & 0x3F);
/* fall through */
case 3:
ret = (ret << 6) | (*s++ & 0x3F);
/* fall through */
case 2:
ret = (ret << 6) | (*s++ & 0x3F);
break;
default:
break;
}
*string = (const char*)s;
return ret;
}
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);
const uint16_t *p = in;
/* Find length in a single scan */
while (*p != 0)
p++;
{
size_t in_len = (size_t)(p - in);
utf16_conv_utf8(NULL, dest_len, in, in_len);
*dest_len += 1;
if ((*utf_data = (uint8_t*)malloc(*dest_len)) != 0)
return utf16_conv_utf8(*utf_data, dest_len, in, 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 _len = 0;
uint8_t *utf16_data = NULL;
bool ret = utf16_to_char(&utf16_data, &_len, in);
if (ret)
{
utf16_data[dest_len] = 0;
utf16_data[_len] = 0;
strlcpy(s, (const char*)utf16_data, 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)
{
wchar_t *path_buf_wide = NULL;
int path_buf_wide_len = MultiByteToWideChar(cp_in, 0, str, -1, NULL, 0);
/* Windows 95 will return 0 from these functions with
/* Windows 95 will return 0 from these functions with
* a UTF8 codepage set without MSLU.
*
* From an unknown MSDN version (others omit this info):
* - CP_UTF8 Windows 98/Me, Windows NT 4.0 and later:
* - CP_UTF8 Windows 98/Me, Windows NT 4.0 and later:
* Translate using UTF-8. When this is set, dwFlags must be zero.
* - Windows 95: Under the Microsoft Layer for Unicode,
* - Windows 95: Under the Microsoft Layer for Unicode,
* MultiByteToWideChar also supports CP_UTF7 and CP_UTF8.
*/
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 +528,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. */
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)
{
#ifdef _WIN32
int len = 0;
int out_len = 0;
int _len = 0;
#else
size_t len = 0;
size_t out_len = 0;
size_t _len = 0;
#endif
wchar_t *buf = NULL;
@@ -393,69 +578,61 @@ 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. */
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)
{
#ifdef _WIN32
int len = 0;
int _len = 0;
#else
size_t len = 0;
size_t _len = 0;
#endif
char *buf = NULL;
@@ -465,42 +642,35 @@ 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,
_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,
0, str, -1, buf, len, NULL, NULL) < 0)
0, str, -1, buf, _len, NULL, NULL) < 0)
{
free(buf);
return NULL;
}
}
#else
/* NOTE: For now, assume non-Windows platforms'
/* 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)
if (wcstombs(buf, str, _len) == (size_t)-1)
{
free(buf);
return NULL;
File diff suppressed because it is too large. Load diff
+7 -10
View File
@@ -24,17 +24,14 @@
#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>
#include <string/stdstring.h>
#define VFS_FRONTEND
#include <vfs/vfs_implementation.h>
@@ -74,7 +71,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 +102,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 +117,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))
{
+2 -2
View File
@@ -38,12 +38,12 @@ extern "C" {
#ifndef snprintf
#define snprintf c99_snprintf_retro__
#endif
int c99_snprintf_retro__(char *outBuf, size_t size, const char *format, ...);
int c99_snprintf_retro__(char *s, size_t len, const char *format, ...);
#ifndef vsnprintf
#define vsnprintf c99_vsnprintf_retro__
#endif
int c99_vsnprintf_retro__(char *outBuf, size_t size, const char *format, va_list ap);
int c99_vsnprintf_retro__(char *s, size_t len, const char *format, va_list ap);
#endif
#ifdef __cplusplus
+35 -2
View File
@@ -25,31 +25,64 @@
#include <retro_common_api.h>
/**
* @file posix_string.h
*
* Portable reimplementations of various string functions
* that are normally provided by libc or POSIX.
*/
#ifdef _MSC_VER
#include <compat/msvc.h>
#endif
RETRO_BEGIN_DECLS
#ifdef _WIN32
#if defined(_WIN32) || defined(DOXYGEN)
#undef strtok_r
#define strtok_r(str, delim, saveptr) retro_strtok_r__(str, delim, saveptr)
/**
* Portable reimplementation of \c strtok_r().
* The original function will be used if it's available.
*
* @see https://man7.org/linux/man-pages/man3/strtok.3.html
*/
char *strtok_r(char *str, const char *delim, char **saveptr);
#endif
#ifdef _MSC_VER
#if defined(_MSC_VER) || defined(DOXYGEN)
#undef strcasecmp
#undef strdup
#define strcasecmp(a, b) retro_strcasecmp__(a, b)
#define strdup(orig) retro_strdup__(orig)
/**
* Portable reimplementation of \c strcasecmp().
* The original function will be used if it's available.
*
* @see https://man7.org/linux/man-pages/man3/strcasecmp.3.html
*/
int strcasecmp(const char *a, const char *b);
/**
* Portable reimplementation of \c strdup().
* The original function will be used if it's available.
*
* @see https://man7.org/linux/man-pages/man3/strdup.3.html
*/
char *strdup(const char *orig);
/* isblank is available since MSVC 2013 */
#if _MSC_VER < 1800
#undef isblank
#define isblank(c) retro_isblank__(c)
/**
* Portable reimplementation of \c isblank().
* The original function will be used if it's available.
*
* @see https://en.cppreference.com/w/c/string/byte/isblank
*/
int isblank(int c);
#endif
@@ -39,6 +39,14 @@ RETRO_BEGIN_DECLS
* since we prefer to use the actual name. */
#define strcasestr(haystack, needle) strcasestr_retro__(haystack, needle)
/**
* Portable reimplementation of \c strcasestr(3).
* If the original function is available
* (as determined by the presence of \c HAVE_STRCASESTR),
* it will be used instead.
*
* @see https://man7.org/linux/man-pages/man3/strstr.3.html
*/
char *strcasestr(const char *haystack, const char *needle);
RETRO_END_DECLS
+30 -3
View File
@@ -23,6 +23,15 @@
#ifndef __LIBRETRO_SDK_COMPAT_STRL_H
#define __LIBRETRO_SDK_COMPAT_STRL_H
/**
* @file strl.h
*
* Portable implementation of \c strlcpy(3) and \c strlcat(3).
* If these functions are available on the target platform,
* then the originals should be imported instead.
*
* @see https://linux.die.net/man/3/strlcpy
*/
#include <string.h>
#include <stddef.h>
@@ -34,7 +43,7 @@
RETRO_BEGIN_DECLS
#ifdef __MACH__
#if defined(__MACH__) && defined(__APPLE__)
#ifndef HAVE_STRL
#define HAVE_STRL
#endif
@@ -47,11 +56,29 @@ RETRO_BEGIN_DECLS
#define strlcat(dst, src, size) strlcat_retro__(dst, src, size)
size_t strlcpy(char *dest, const char *source, size_t size);
size_t strlcat(char *dest, const char *source, size_t size);
/**
* @brief Portable implementation of \c strlcpy(3).
* @see https://linux.die.net/man/3/strlcpy
*/
size_t strlcpy(char *s, const char *source, size_t len);
/**
* @brief Portable implementation of \c strlcat(3).
* @see https://linux.die.net/man/3/strlcpy
*/
size_t strlcat(char *s, const char *source, size_t len);
#endif
/**
* A version of \c strndup(3) that guarantees the result will be null-terminated.
*
* @param s The string to duplicate.
* @param n The maximum number of characters to copy from \c s.
* The result will allocate one more byte than this value.
* @return Pointer to the cloned string.
* Must be freed with \c free().
*/
char *strldup(const char *s, size_t n);
RETRO_END_DECLS
+74 -4
View File
@@ -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
+322 -170
View File
@@ -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,22 @@ char *path_remove_extension(char *path);
*
* Get basename from @path.
*
* Returns: basename from path.
* Hidden non-leaf function cost:
* - Calls path_get_archive_delim()
*
* @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.
*
* @return basename from path.
**/
const char *path_basename_nocompression(const char *path);
/**
@@ -134,21 +190,22 @@ const char *path_basename_nocompression(const char *path);
* Extracts base directory by mutating path.
* Keeps trailing '/'.
**/
void path_basedir(char *path);
size_t path_basedir(char *path);
/**
* path_parent_dir:
* @path : path
* @s : path
* @len : size of buffer
*
* 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);
size_t path_parent_dir(char *s, size_t len);
/**
* path_resolve_realpath:
* @buf : input and output buffer for path
* @s : input and output buffer for path
* @size : size of buffer
* @resolve_symlinks : whether to resolve symlinks or not
*
@@ -156,30 +213,33 @@ void path_parent_dir(char *path);
*
* Relative paths are rebased on the current working dir.
*
* Returns: @buf if successful, NULL otherwise.
* @return @s 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,
* e.g. on Android it is "/"
* Use of fill_pathname_resolve_relative() should be prefered
* Use of fill_pathname_resolve_relative() should be preferred
**/
char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks);
char *path_resolve_realpath(char *s, size_t len, bool resolve_symlinks);
/**
* path_relative_to:
* @out : buffer to write the relative path to
* @s : buffer to write the relative path to
* @path : path to be expressed relatively
* @base : relative to this
* @size : size of output buffer
* @len : size of output buffer
*
* Turns @path into a path relative to @base and writes it to @out.
* Turns @path into a path relative to @base and writes it to @s.
*
* @base is assumed to be a base directory, i.e. a path ending with '/' or '\'.
* Both @path and @base are assumed to be absolute paths without "." or "..".
*
* E.g. path /a/b/e/f.cgp with base /a/b/c/d/ turns into ../../e/f.cgp
*
* @return Length of the string copied into @s
**/
size_t path_relative_to(char *out, const char *path, const char *base, size_t size);
size_t path_relative_to(char *s, const char *path, const char *base,
size_t len);
/**
* path_is_absolute:
@@ -187,267 +247,339 @@ 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);
/**
* fill_pathname:
* @out_path : output path
* @s : output path
* @in_path : input path
* @replace : what to replace
* @size : buffer size of output path
* @len : buffer size of output path
*
* FIXME: Verify
*
* Replaces filename extension with 'replace' and outputs result to out_path.
* Replaces filename extension with 'replace' and outputs result to s.
* The extension here is considered to be the string from the last '.'
* to the end.
*
* Only '.'s after the last slash are considered as extensions.
* If no '.' is present, in_path and replace will simply be concatenated.
* 'size' is buffer size of 'out_path'.
* 'len' is buffer size of 's'.
* E.g.: in_path = "/foo/bar/baz/boo.c", replace = ".asm" =>
* out_path = "/foo/bar/baz/boo.asm"
* s = "/foo/bar/baz/boo.asm"
* E.g.: in_path = "/foo/bar/baz/boo.c", replace = "" =>
* out_path = "/foo/bar/baz/boo"
* s = "/foo/bar/baz/boo"
*
* Hidden non-leaf function cost:
* - calls strlcpy 2x
* - calls strrchr
* - calls strlcat
*
* @return Length of the string copied into @s
*/
void fill_pathname(char *out_path, const char *in_path,
const char *replace, size_t size);
size_t fill_pathname(char *s, const char *in_path,
const char *replace, size_t len);
/**
* fill_dated_filename:
* @out_filename : output filename
* @s : output filename
* @ext : extension of output filename
* @size : buffer size of output filename
* @len : buffer size of output filename
*
* Creates a 'dated' filename prefixed by 'RetroArch', and
* concatenates extension (@ext) to it.
*
* E.g.:
* out_filename = "RetroArch-{month}{day}-{Hours}{Minutes}.{@ext}"
* s = "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);
size_t fill_dated_filename(char *s, const char *ext, size_t len);
/**
* fill_str_dated_filename:
* @out_filename : output filename
* @s : output filename
* @in_str : input string
* @ext : extension of output filename
* @size : buffer size of output filename
* @len : buffer size of output filename
*
* Creates a 'dated' filename prefixed by the string @in_str, and
* concatenates extension (@ext) to it.
*
* E.g.:
* out_filename = "RetroArch-{year}{month}{day}-{Hour}{Minute}{Second}.{@ext}"
* s = "RetroArch-{year}{month}{day}-{Hour}{Minute}{Second}.{@ext}"
*
* Hidden non-leaf function cost:
* - Calls time
* - Calls rtime_localtime()
* - Calls strlcpy 2x
* - Calls string_is_empty()
* - Calls strftime
* - Calls strlcat
*
* @return Length of the string copied into @s
**/
void fill_str_dated_filename(char *out_filename,
const char *in_str, const char *ext, size_t size);
/**
* fill_pathname_noext:
* @out_path : output path
* @in_path : input path
* @replace : what to replace
* @size : buffer size of output path
*
* Appends a filename extension 'replace' to 'in_path', and outputs
* result in 'out_path'.
*
* Assumes in_path has no extension. If an extension is still
* present in 'in_path', it will be ignored.
*
*/
size_t fill_pathname_noext(char *out_path, const char *in_path,
const char *replace, size_t size);
size_t fill_str_dated_filename(char *s, const char *in_str, const char *ext, size_t len);
/**
* find_last_slash:
* @str : input path
* @str : 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.
*
* Returns: a pointer to the last slash in the input path.
* Leaf function.
*
* @return pointer to last slash/backslash found in @str.
**/
char *find_last_slash(const char *str);
/**
* fill_pathname_dir:
* @in_dir : input directory path
* @in_basename : input basename to be appended to @in_dir
* @s : input directory path
* @in_basename : input basename to be appended to @s
* @replace : replacement to be appended to @in_basename
* @size : size of buffer
* @len : size of buffer
*
* Appends basename of 'in_basename', to 'in_dir', along with 'replace'.
* Appends basename of 'in_basename', to 's', along with 'replace'.
* Basename of in_basename is the string after the last '/' or '\\',
* i.e the filename without directories.
*
* If in_basename has no '/' or '\\', the whole 'in_basename' will be used.
* 'size' is buffer size of 'in_dir'.
* 'len' is buffer size of 's'.
*
* E.g..: in_dir = "/tmp/some_dir", in_basename = "/some_content/foo.c",
* replace = ".asm" => in_dir = "/tmp/some_dir/foo.c.asm"
* E.g..: s = "/tmp/some_dir", in_basename = "/some_content/foo.c",
* replace = ".asm" => s = "/tmp/some_dir/foo.c.asm"
*
* Hidden non-leaf function cost:
* - Calls fill_pathname_slash()
* - Calls path_basename()
* - Calls strlcpy 2x
**/
size_t fill_pathname_dir(char *in_dir, const char *in_basename,
const char *replace, size_t size);
size_t fill_pathname_dir(char *s, const char *in_basename,
const char *replace, size_t len);
/**
* fill_pathname_base:
* @out : output path
* @s : output path
* @in_path : input path
* @size : size of output path
* @len : size of output path
*
* Copies basename of @in_path into @out_path.
* Copies basename of @in_path into @s.
*
* Hidden non-leaf function cost:
* - Calls path_basename()
* - Calls strlcpy
*
* @return length of the string copied into @s
**/
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);
size_t fill_pathname_base(char *s, const char *in_path, size_t len);
/**
* fill_pathname_basedir:
* @out_dir : output directory
* @s : output directory
* @in_path : input path
* @size : size of output directory
* @len : size of output directory
*
* Copies base directory of @in_path into @out_path.
* Copies base directory of @in_path into @s.
* If in_path is a path without any slashes (relative current directory),
* @out_path will get path "./".
* @s 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);
size_t fill_pathname_basedir(char *s, const char *in_path, size_t len);
/**
* fill_pathname_parent_dir_name:
* @out_dir : output directory
* @s : output string
* @in_dir : input directory
* @size : size of output directory
* @len : size of @s
*
* Copies only the parent directory name of @in_dir into @out_dir.
* Copies only the parent directory name of @in_dir into @s.
* 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
* - Can call strlcpy
*
* @return Length of the string copied into @s
**/
bool fill_pathname_parent_dir_name(char *out_dir,
const char *in_dir, size_t size);
size_t fill_pathname_parent_dir_name(char *s,
const char *in_dir, size_t len);
/**
* fill_pathname_parent_dir:
* @out_dir : output directory
* @s : output directory
* @in_dir : input directory
* @size : size of output directory
* @len : size of output directory
*
* Copies parent directory of @in_dir into @out_dir.
* Copies parent directory of @in_dir into @s.
* Assumes @in_dir is a directory. Keeps trailing '/'.
* If the path was already at the root directory, @out_dir will be an empty string.
* If the path was already at the root directory, @s will be an empty string.
*
* Hidden non-leaf function cost:
* - Can call strlcpy if (@s!= @in_dir)
* - Calls strlen if (@s == @in_dir)
* - Calls path_parent_dir()
*
* @return Length of the string copied into @s
**/
void fill_pathname_parent_dir(char *out_dir,
const char *in_dir, size_t size);
size_t fill_pathname_parent_dir(char *s,
const char *in_dir, size_t len);
/**
* fill_pathname_resolve_relative:
* @out_path : output path
* @s : output path
* @in_refpath : input reference path
* @in_path : input path
* @size : size of @out_path
* @len : size of @s
*
* Joins basedir of @in_refpath together with @in_path.
* If @in_path is an absolute path, out_path = in_path.
* If @in_path is an absolute path, s = in_path.
* E.g.: in_refpath = "/foo/bar/baz.a", in_path = "foobar.cg",
* out_path = "/foo/bar/foobar.cg".
* s = "/foo/bar/foobar.cg".
**/
void fill_pathname_resolve_relative(char *out_path, const char *in_refpath,
const char *in_path, size_t size);
void fill_pathname_resolve_relative(char *s, const char *in_refpath,
const char *in_path, size_t len);
/**
* fill_pathname_join:
* @out_path : output path
* @s : output path
* @dir : directory
* @path : path
* @size : size of output path
* @len : 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 at least once
* - calls fill_pathname_slash()
*
* Deprecated. Use fill_pathname_join_special() instead
* if you can ensure @dir != @s
*
* @return Length of the string copied into @s
**/
size_t fill_pathname_join(char *out_path, const char *dir,
const char *path, size_t size);
size_t fill_pathname_join(char *s, const char *dir,
const char *path, size_t len);
size_t fill_pathname_join_special_ext(char *out_path,
/**
* fill_pathname_join_special:
* @s : output path
* @dir : directory. Cannot be identical to @s
* @path : path
* @len : size of output path
*
*
* Specialized version of fill_pathname_join.
* Unlike fill_pathname_join(),
* @dir and @s CANNOT be identical.
*
* Joins a directory (@dir) and path (@path) together.
* Makes sure not to get two consecutive slashes
* between directory and path.
*
* Hidden non-leaf function cost:
* - calls strlcpy 2x
*
* @return Length of the string copied into @s
**/
size_t fill_pathname_join_special(char *s,
const char *dir, const char *path, size_t len);
size_t fill_pathname_join_special_ext(char *s,
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);
size_t len);
/**
* fill_pathname_join_delim:
* @out_path : output path
* @s : output path
* @dir : directory
* @path : path
* @delim : delimiter
* @size : size of output path
* @len : size of output 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(char *s, const char *dir,
const char *path, const char delim, size_t len);
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 *s,
const char *in_path, size_t len);
size_t fill_pathname_abbreviate_special(char *s,
const char *in_path, size_t len);
/**
* 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
*
* 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);
* 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
*
* @return Length of the string copied into @s
**/
size_t fill_pathname_abbreviated_or_relative(char *s,
const char *in_refpath, const char *in_path, size_t len);
void fill_short_pathname_representation_noext(char* out_rep,
const char *in_path, size_t size);
/**
* sanitize_path_part:
*
* @path_part : directory or filename
* @len : length of path_part
*
* Takes single part of a path eg. single filename
* or directory, and removes any special chars that are
* unavailable.
*
* @returns newly allocated string that has been sanitized.
* Caller is responsible for freeing the returned string.
**/
char *sanitize_path_part(const char *path_part, size_t len);
void fill_pathname_expand_special(char *out_path,
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 *s);
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);
void pathname_conform_slashes_to_os(char *path);
void pathname_make_slashes_portable(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 *s);
/**
* path_basedir:
@@ -456,7 +588,7 @@ void pathname_make_slashes_portable(char *path);
* Extracts base directory by mutating path.
* Keeps trailing '/'.
**/
void path_basedir_wrapper(char *path);
void path_basedir_wrapper(char *s);
/**
* path_char_is_slash:
@@ -464,8 +596,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 +609,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() '\\'
@@ -489,18 +621,22 @@ void path_basedir_wrapper(char *path);
/**
* fill_pathname_slash:
* @path : path
* @size : size of path
* @s : path
* @len : size of path
*
* Assumes path is a directory. Appends a slash
* if not already there.
* Hidden non-leaf function cost:
* - can call strlcat once if it returns false
* - calls strlen
**/
void fill_pathname_slash(char *path, size_t size);
size_t fill_pathname_slash(char *s, size_t len);
#if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
void fill_pathname_application_path(char *buf, size_t size);
void fill_pathname_application_dir(char *buf, size_t size);
void fill_pathname_home_dir(char *buf, size_t size);
size_t fill_pathname_application_path(char *s, size_t len);
size_t fill_pathname_application_dir(char *s, size_t len);
size_t fill_pathname_home_dir(char *s, size_t len);
#endif
/**
@@ -509,7 +645,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,10 +664,17 @@ 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);
/* Time format strings with AM-PM designation require special
* handling due to platform dependence
* @return Length of the string written to @s
*/
size_t strftime_am_pm(char *s, size_t len, const char* format,
const void* timeptr);
bool path_is_character_special(const char *path);
int path_stat(const char *path);
File diff suppressed because it is too large. Load diff
+7 -8
View File
@@ -1,4 +1,4 @@
/* Copyright (C) 2010-2018 The RetroArch team
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (retro_common.h).
@@ -23,15 +23,14 @@
#ifndef _LIBRETRO_COMMON_RETRO_COMMON_H
#define _LIBRETRO_COMMON_RETRO_COMMON_H
/*
This file is designed to normalize the libretro-common compiling environment.
It is not to be used in public API headers, as they should be designed as leanly as possible.
Nonetheless.. in the meantime, if you do something like use ssize_t, which is not fully portable,
in a public API, you may need this.
*/
/*!
* @internal This file is designed to normalize the libretro-common compiling environment.
* It is not to be used in public API headers, as they should be designed as leanly as possible.
* Nonetheless.. in the meantime, if you do something like use ssize_t, which is not fully portable,
* in a public API, you may need this.
*/
/* conditional compilation is handled inside here */
#include <compat/msvc.h>
#endif
+22 -2
View File
@@ -71,7 +71,7 @@ typedef __int64 ssize_t;
typedef int ssize_t;
#endif
#endif
#elif defined(__MACH__)
#elif defined(__MACH__) && defined(__APPLE__)
#include <sys/types.h>
#endif
@@ -101,11 +101,31 @@ typedef int ssize_t;
#define STRING_REP_UINT64 "%" PRIu64
#define STRING_REP_USIZE "%" PRIuPTR
/* Wrap a declaration in RETRO_DEPRECATED() to produce a compiler warning when
it's used. This is intended for developer machines, so it won't work on ancient
or obscure compilers */
#if defined(_MSC_VER)
#if _MSC_VER >= 1400 /* Visual C 2005 or later */
#define RETRO_DEPRECATED(decl) __declspec(deprecated) decl
#endif
#elif defined(__GNUC__)
#if __GNUC__ >= 3 /* GCC 3 or later */
#define RETRO_DEPRECATED(decl) decl __attribute__((deprecated))
#endif
#elif defined(__clang__)
#if __clang_major__ >= 3 /* clang 3 or later */
#define RETRO_DEPRECATED(decl) decl __attribute__((deprecated))
#endif
#endif
#ifndef RETRO_DEPRECATED /* Unsupported compilers */
#define RETRO_DEPRECATED(decl) decl
#endif
/*
I would like to see retro_inline.h moved in here; possibly boolean too.
rationale: these are used in public APIs, and it is easier to find problems
and write code that works the first time portably when theyre included uniformly
and write code that works the first time portably when they are included uniformly
than to do the analysis from scratch each time you think you need it, for each feature.
Moreover it helps force you to make hard decisions: if you EVER bring in boolean.h,
+286 -273
View File
@@ -31,12 +31,22 @@
#define SWAP16 _byteswap_ushort
#define SWAP32 _byteswap_ulong
#else
/**
* Swaps the byte order of a 16-bit unsigned integer.
* @param x The integer to byteswap.
* @return \c with its two bytes swapped.
*/
static INLINE uint16_t SWAP16(uint16_t x)
{
return ((x & 0x00ff) << 8) |
((x & 0xff00) >> 8);
}
/**
* Swaps the byte order of a 32-bit unsigned integer.
* @param x The integer to byteswap.
* @return \c with its bytes swapped.
*/
static INLINE uint32_t SWAP32(uint32_t x)
{
return ((x & 0x000000ff) << 24) |
@@ -61,6 +71,11 @@ static INLINE uint64_t SWAP64(uint64_t val)
| ((val & 0xff00000000000000) >> 56);
}
#else
/**
* Swaps the byte order of a 64-bit unsigned integer.
* @param x The integer to byteswap.
* @return \c with its bytes swapped.
*/
static INLINE uint64_t SWAP64(uint64_t val)
{
return ((val & 0x00000000000000ffULL) << 56)
@@ -74,31 +89,29 @@ static INLINE uint64_t SWAP64(uint64_t val)
}
#endif
#if defined (LSB_FIRST) || defined (MSB_FIRST)
# warning Defining MSB_FIRST and LSB_FIRST in compile options is deprecated
# undef LSB_FIRST
# undef MSB_FIRST
#endif
#ifdef _MSC_VER
#include <winsock2.h>
#endif
#ifdef _MSC_VER
#if _M_IX86 || _M_AMD64 || _M_ARM || _M_ARM64
/* MSVC pre-defines macros depending on target arch */
#if defined (_M_IX86) || defined (_M_AMD64) || defined (_M_ARM) || defined (_M_ARM64)
#ifndef LSB_FIRST
#define LSB_FIRST 1
#endif
#elif _M_PPC
#ifndef MSB_FIRST
#define MSB_FIRST 1
#endif
#else
/* MSVC can run on _M_ALPHA and _M_IA64 too, but they're both bi-endian; need to find what mode MSVC runs them at */
#error "unknown platform, can't determine endianness"
#endif
#else
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#ifndef MSB_FIRST
#define MSB_FIRST 1
#endif
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#ifndef LSB_FIRST
#define LSB_FIRST 1
#endif
#else
#error "Invalid endianness macros"
#endif
@@ -126,25 +139,20 @@ static INLINE uint64_t SWAP64(uint64_t val)
/**
* is_little_endian:
* Checks if the current CPU is little-endian.
*
* Checks if the system is little endian or big-endian.
*
* Returns: greater than 0 if little-endian,
* otherwise big-endian.
**/
* @return \c true on little-endian CPUs,
* \c false on big-endian CPUs.
*/
#define is_little_endian() RETRO_IS_LITTLE_ENDIAN
/**
* swap_if_big64:
* @val : unsigned 64-bit value
* Byte-swaps an unsigned 64-bit integer on big-endian CPUs.
*
* Byteswap unsigned 64-bit value if system is big-endian.
*
* Returns: Byteswapped value in case system is big-endian,
* otherwise returns same value.
**/
* @param val The value to byteswap if necessary.
* @return \c val byteswapped on big-endian CPUs,
* \c val unchanged on little-endian CPUs.
*/
#if RETRO_IS_BIG_ENDIAN
#define swap_if_big64(val) (SWAP64(val))
#elif RETRO_IS_LITTLE_ENDIAN
@@ -152,15 +160,12 @@ static INLINE uint64_t SWAP64(uint64_t val)
#endif
/**
* swap_if_big32:
* @val : unsigned 32-bit value
* Byte-swaps an unsigned 32-bit integer on big-endian CPUs.
*
* Byteswap unsigned 32-bit value if system is big-endian.
*
* Returns: Byteswapped value in case system is big-endian,
* otherwise returns same value.
**/
* @param val The value to byteswap if necessary.
* @return \c val byteswapped on big-endian CPUs,
* \c val unchanged on little-endian CPUs.
*/
#if RETRO_IS_BIG_ENDIAN
#define swap_if_big32(val) (SWAP32(val))
#elif RETRO_IS_LITTLE_ENDIAN
@@ -168,15 +173,12 @@ static INLINE uint64_t SWAP64(uint64_t val)
#endif
/**
* swap_if_little64:
* @val : unsigned 64-bit value
* Byte-swaps an unsigned 64-bit integer on little-endian CPUs.
*
* Byteswap unsigned 64-bit value if system is little-endian.
*
* Returns: Byteswapped value in case system is little-endian,
* otherwise returns same value.
**/
* @param val The value to byteswap if necessary.
* @return \c val byteswapped on little-endian CPUs,
* \c val unchanged on big-endian CPUs.
*/
#if RETRO_IS_BIG_ENDIAN
#define swap_if_little64(val) (val)
#elif RETRO_IS_LITTLE_ENDIAN
@@ -184,15 +186,12 @@ static INLINE uint64_t SWAP64(uint64_t val)
#endif
/**
* swap_if_little32:
* @val : unsigned 32-bit value
* Byte-swaps an unsigned 32-bit integer on little-endian CPUs.
*
* Byteswap unsigned 32-bit value if system is little-endian.
*
* Returns: Byteswapped value in case system is little-endian,
* otherwise returns same value.
**/
* @param val The value to byteswap if necessary.
* @return \c val byteswapped on little-endian CPUs,
* \c val unchanged on big-endian CPUs.
*/
#if RETRO_IS_BIG_ENDIAN
#define swap_if_little32(val) (val)
#elif RETRO_IS_LITTLE_ENDIAN
@@ -200,15 +199,12 @@ static INLINE uint64_t SWAP64(uint64_t val)
#endif
/**
* swap_if_big16:
* @val : unsigned 16-bit value
* Byte-swaps an unsigned 16-bit integer on big-endian systems.
*
* Byteswap unsigned 16-bit value if system is big-endian.
*
* Returns: Byteswapped value in case system is big-endian,
* otherwise returns same value.
**/
* @param val The value to byteswap if necessary.
* @return \c val byteswapped on big-endian systems,
* \c val unchanged on little-endian systems.
*/
#if RETRO_IS_BIG_ENDIAN
#define swap_if_big16(val) (SWAP16(val))
#elif RETRO_IS_LITTLE_ENDIAN
@@ -216,15 +212,12 @@ static INLINE uint64_t SWAP64(uint64_t val)
#endif
/**
* swap_if_little16:
* @val : unsigned 16-bit value
* Byte-swaps an unsigned 16-bit integer on little-endian systems.
*
* Byteswap unsigned 16-bit value if system is little-endian.
*
* Returns: Byteswapped value in case system is little-endian,
* otherwise returns same value.
**/
* @param val The value to byteswap if necessary.
* @return \c val byteswapped on little-endian systems,
* \c val unchanged on big-endian systems.
*/
#if RETRO_IS_BIG_ENDIAN
#define swap_if_little16(val) (val)
#elif RETRO_IS_LITTLE_ENDIAN
@@ -232,167 +225,143 @@ static INLINE uint64_t SWAP64(uint64_t val)
#endif
/**
* store32be:
* @addr : pointer to unsigned 32-bit buffer
* @data : unsigned 32-bit value to write
* Stores a 32-bit integer in at the given address, in big-endian order.
*
* Write data to address. Endian-safe. Byteswaps the data
* first if necessary before storing it.
**/
* @param addr The address to store the value at.
* Behavior is undefined if \c NULL or not aligned to a 32-bit boundary.
* @param data The value to store in \c addr.
* Will be byteswapped if on a little-endian CPU.
*/
static INLINE void store32be(uint32_t *addr, uint32_t data)
{
*addr = swap_if_little32(data);
}
/**
* load32be:
* @addr : pointer to unsigned 32-bit buffer
* Loads a 32-bit integer order from the given address, in big-endian order.
*
* Load value from address. Endian-safe.
*
* Returns: value from address, byte-swapped if necessary.
**/
* @param addr The address to load the value from.
* Behavior is undefined if \c NULL or not aligned to a 32-bit boundary.
* @return The value at \c addr, byteswapped if on a little-endian CPU.
*/
static INLINE uint32_t load32be(const uint32_t *addr)
{
return swap_if_little32(*addr);
}
/**
* retro_cpu_to_le16:
* @val : unsigned 16-bit value
* Converts the given unsigned 16-bit integer to little-endian order if necessary.
*
* Convert unsigned 16-bit value from system to little-endian.
*
* Returns: Little-endian represantation of val.
**/
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a big-endian CPU,
* unchanged otherwise.
*/
#define retro_cpu_to_le16(val) swap_if_big16(val)
/**
* retro_cpu_to_le32:
* @val : unsigned 32-bit value
* Converts the given unsigned 32-bit integer to little-endian order if necessary.
*
* Convert unsigned 32-bit value from system to little-endian.
*
* Returns: Little-endian represantation of val.
**/
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a big-endian CPU,
* unchanged otherwise.
*/
#define retro_cpu_to_le32(val) swap_if_big32(val)
/**
* retro_cpu_to_le64:
* @val : unsigned 64-bit value
* Converts the given unsigned 64-bit integer to little-endian order if necessary.
*
* Convert unsigned 64-bit value from system to little-endian.
*
* Returns: Little-endian represantation of val.
**/
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a big-endian CPU,
* unchanged otherwise.
*/
#define retro_cpu_to_le64(val) swap_if_big64(val)
/**
* retro_le_to_cpu16:
* @val : unsigned 16-bit value
* Converts the given unsigned 16-bit integer to host-native order if necessary.
*
* Convert unsigned 16-bit value from little-endian to native.
*
* Returns: Native represantation of little-endian val.
**/
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a big-endian CPU,
* unchanged otherwise.
*/
#define retro_le_to_cpu16(val) swap_if_big16(val)
/**
* retro_le_to_cpu32:
* @val : unsigned 32-bit value
* Converts the given unsigned 32-bit integer to host-native order if necessary.
*
* Convert unsigned 32-bit value from little-endian to native.
*
* Returns: Native represantation of little-endian val.
**/
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a big-endian CPU,
* unchanged otherwise.
*/
#define retro_le_to_cpu32(val) swap_if_big32(val)
/**
* retro_le_to_cpu16:
* @val : unsigned 64-bit value
* Converts the given unsigned 64-bit integer to host-native order if necessary.
*
* Convert unsigned 64-bit value from little-endian to native.
*
* Returns: Native represantation of little-endian val.
**/
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a big-endian CPU,
* unchanged otherwise.
*/
#define retro_le_to_cpu64(val) swap_if_big64(val)
/**
* retro_cpu_to_be16:
* @val : unsigned 16-bit value
* Converts the given unsigned 16-bit integer to big-endian order if necessary.
*
* Convert unsigned 16-bit value from system to big-endian.
*
* Returns: Big-endian represantation of val.
**/
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a little-endian CPU,
* unchanged otherwise.
*/
#define retro_cpu_to_be16(val) swap_if_little16(val)
/**
* retro_cpu_to_be32:
* @val : unsigned 32-bit value
* Converts the given unsigned 32-bit integer to big-endian order if necessary.
*
* Convert unsigned 32-bit value from system to big-endian.
*
* Returns: Big-endian represantation of val.
**/
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a little-endian CPU,
* unchanged otherwise.
*/
#define retro_cpu_to_be32(val) swap_if_little32(val)
/**
* retro_cpu_to_be64:
* @val : unsigned 64-bit value
* Converts the given unsigned 64-bit integer to big-endian order if necessary.
*
* Convert unsigned 64-bit value from system to big-endian.
*
* Returns: Big-endian represantation of val.
**/
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a little-endian CPU,
* unchanged otherwise.
*/
#define retro_cpu_to_be64(val) swap_if_little64(val)
/**
* retro_be_to_cpu16:
* @val : unsigned 16-bit value
* Converts the given unsigned 16-bit integer from big-endian to host-native order if necessary.
*
* Convert unsigned 16-bit value from big-endian to native.
*
* Returns: Native represantation of big-endian val.
**/
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a little-endian CPU,
* unchanged otherwise.
*/
#define retro_be_to_cpu16(val) swap_if_little16(val)
/**
* retro_be_to_cpu32:
* @val : unsigned 32-bit value
* Converts the given unsigned 32-bit integer from big-endian to host-native order if necessary.
*
* Convert unsigned 32-bit value from big-endian to native.
*
* Returns: Native represantation of big-endian val.
**/
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a little-endian CPU,
* unchanged otherwise.
*/
#define retro_be_to_cpu32(val) swap_if_little32(val)
/**
* retro_be_to_cpu64:
* @val : unsigned 64-bit value
* Converts the given unsigned 64-bit integer from big-endian to host-native order if necessary.
*
* Convert unsigned 64-bit value from big-endian to native.
*
* Returns: Native represantation of big-endian val.
**/
* @param val The value to convert if necessary.
* @return \c val byteswapped if on a little-endian CPU,
* unchanged otherwise.
*/
#define retro_be_to_cpu64(val) swap_if_little64(val)
#ifdef __GNUC__
/* This attribute means that the same memory may be referred through
pointers to different size of the object (aliasing). E.g. that u8 *
and u32 * may actually be pointing to the same object. */
/**
* This attribute indicates that pointers to this type may alias
* to pointers of any other type, similar to \c void* or \c char*.
*/
#define MAY_ALIAS __attribute__((__may_alias__))
#else
#define MAY_ALIAS
@@ -413,8 +382,22 @@ struct retro_unaligned_uint64_s
} MAY_ALIAS;
#pragma pack(pop)
/**
* A wrapper around a \c uint16_t that allows unaligned access
* where supported by the compiler.
*/
typedef struct retro_unaligned_uint16_s retro_unaligned_uint16_t;
/**
* A wrapper around a \c uint32_t that allows unaligned access
* where supported by the compiler.
*/
typedef struct retro_unaligned_uint32_s retro_unaligned_uint32_t;
/**
* A wrapper around a \c uint64_t that allows unaligned access
* where supported by the compiler.
*/
typedef struct retro_unaligned_uint64_s retro_unaligned_uint64_t;
/* L-value references to unaligned pointers. */
@@ -423,159 +406,189 @@ typedef struct retro_unaligned_uint64_s retro_unaligned_uint64_t;
#define retro_unaligned64(p) (((retro_unaligned_uint64_t *)p)->val)
/**
* retro_get_unaligned_16be:
* @addr : pointer to unsigned 16-bit value
* Reads a 16-bit unsigned integer from the given address
* and converts it from big-endian to host-native order (if necessary),
* regardless of the CPU's alignment requirements.
*
* Convert unsigned unaligned 16-bit value from big-endian to native.
*
* Returns: Native represantation of big-endian val.
**/
static INLINE uint16_t retro_get_unaligned_16be(void *addr) {
return retro_be_to_cpu16(retro_unaligned16(addr));
* @param addr The address of the integer to read.
* Does not need to be divisible by 2
* the way a \c uint16_t* usually would be.
* @return The first two bytes of \c addr as a 16-bit unsigned integer,
* byteswapped from big-endian to host-native order if necessary.
*/
static INLINE uint16_t retro_get_unaligned_16be(void *addr)
{
return retro_be_to_cpu16(retro_unaligned16(addr));
}
/**
* retro_get_unaligned_32be:
* @addr : pointer to unsigned 32-bit value
* Reads a 32-bit unsigned integer from the given address
* and converts it from big-endian to host-native order (if necessary),
* regardless of the CPU's alignment requirements.
*
* Convert unsigned unaligned 32-bit value from big-endian to native.
*
* Returns: Native represantation of big-endian val.
**/
static INLINE uint32_t retro_get_unaligned_32be(void *addr) {
return retro_be_to_cpu32(retro_unaligned32(addr));
* @param addr The address of the integer to read.
* Does not need to be divisible by 4
* the way a \c uint32_t* usually would be.
* @return The first four bytes of \c addr as a 32-bit unsigned integer,
* byteswapped from big-endian to host-native order if necessary.
*/
static INLINE uint32_t retro_get_unaligned_32be(void *addr)
{
return retro_be_to_cpu32(retro_unaligned32(addr));
}
/**
* retro_get_unaligned_64be:
* @addr : pointer to unsigned 64-bit value
* Reads a 64-bit unsigned integer from the given address
* and converts it from big-endian to host-native order (if necessary),
* regardless of the CPU's alignment requirements.
*
* Convert unsigned unaligned 64-bit value from big-endian to native.
*
* Returns: Native represantation of big-endian val.
**/
static INLINE uint64_t retro_get_unaligned_64be(void *addr) {
return retro_be_to_cpu64(retro_unaligned64(addr));
* @param addr The address of the integer to read.
* Does not need to be divisible by 8
* the way a \c uint64_t* usually would be.
* @return The first eight bytes of \c addr as a 64-bit unsigned integer,
* byteswapped from big-endian to host-native order if necessary.
*/
static INLINE uint64_t retro_get_unaligned_64be(void *addr)
{
return retro_be_to_cpu64(retro_unaligned64(addr));
}
/**
* retro_get_unaligned_16le:
* @addr : pointer to unsigned 16-bit value
* Reads a 16-bit unsigned integer from the given address
* and converts it from little-endian to host-native order (if necessary),
* regardless of the CPU's alignment requirements.
*
* Convert unsigned unaligned 16-bit value from little-endian to native.
*
* Returns: Native represantation of little-endian val.
**/
static INLINE uint16_t retro_get_unaligned_16le(void *addr) {
return retro_le_to_cpu16(retro_unaligned16(addr));
* @param addr The address of the integer to read.
* Does not need to be divisible by 2
* the way a \c uint16_t* usually would be.
* @return The first two bytes of \c addr as a 16-bit unsigned integer,
* byteswapped from little-endian to host-native order if necessary.
*/
static INLINE uint16_t retro_get_unaligned_16le(void *addr)
{
return retro_le_to_cpu16(retro_unaligned16(addr));
}
/**
* retro_get_unaligned_32le:
* @addr : pointer to unsigned 32-bit value
* Reads a 32-bit unsigned integer from the given address
* and converts it from little-endian to host-native order (if necessary),
* regardless of the CPU's alignment requirements.
*
* Convert unsigned unaligned 32-bit value from little-endian to native.
*
* Returns: Native represantation of little-endian val.
**/
static INLINE uint32_t retro_get_unaligned_32le(void *addr) {
return retro_le_to_cpu32(retro_unaligned32(addr));
* @param addr The address of the integer to read.
* Does not need to be divisible by 4
* the way a \c uint32_t* usually would be.
* @return The first four bytes of \c addr as a 32-bit unsigned integer,
* byteswapped from little-endian to host-native order if necessary.
*/
static INLINE uint32_t retro_get_unaligned_32le(void *addr)
{
return retro_le_to_cpu32(retro_unaligned32(addr));
}
/**
* retro_get_unaligned_64le:
* @addr : pointer to unsigned 64-bit value
* Reads a 64-bit unsigned integer from the given address
* and converts it from little-endian to host-native order (if necessary),
* regardless of the CPU's alignment requirements.
*
* Convert unsigned unaligned 64-bit value from little-endian to native.
*
* Returns: Native represantation of little-endian val.
**/
static INLINE uint64_t retro_get_unaligned_64le(void *addr) {
return retro_le_to_cpu64(retro_unaligned64(addr));
* @param addr The address of the integer to read.
* Does not need to be divisible by 8
* the way a \c uint64_t* usually would be.
* @return The first eight bytes of \c addr as a 64-bit unsigned integer,
* byteswapped from little-endian to host-native order if necessary.
*/
static INLINE uint64_t retro_get_unaligned_64le(void *addr)
{
return retro_le_to_cpu64(retro_unaligned64(addr));
}
/**
* retro_set_unaligned_16le:
* @addr : pointer to unsigned 16-bit value
* @val : value to store
* Writes a 16-bit unsigned integer to the given address
* (converted to little-endian order if necessary),
* regardless of the CPU's alignment requirements.
*
* Convert native value to unsigned unaligned 16-bit little-endian value
*
**/
static INLINE void retro_set_unaligned_16le(void *addr, uint16_t v) {
retro_unaligned16(addr) = retro_cpu_to_le16(v);
* @param addr The address to write the integer to.
* Does not need to be divisible by 2
* the way a \c uint16_t* usually would be.
* @param v The value to write.
*/
static INLINE void retro_set_unaligned_16le(void *addr, uint16_t v)
{
retro_unaligned16(addr) = retro_cpu_to_le16(v);
}
/**
* retro_set_unaligned_32le:
* @addr : pointer to unsigned 32-bit value
* @val : value to store
* Writes a 32-bit unsigned integer to the given address
* (converted to little-endian order if necessary),
* regardless of the CPU's alignment requirements.
*
* Convert native value to unsigned unaligned 32-bit little-endian value
*
**/
static INLINE void retro_set_unaligned_32le(void *addr, uint32_t v) {
retro_unaligned32(addr) = retro_cpu_to_le32(v);
* @param addr The address to write the integer to.
* Does not need to be divisible by 4
* the way a \c uint32_t* usually would be.
* @param v The value to write.
*/
static INLINE void retro_set_unaligned_32le(void *addr, uint32_t v)
{
retro_unaligned32(addr) = retro_cpu_to_le32(v);
}
/**
* retro_set_unaligned_32le:
* @addr : pointer to unsigned 32-bit value
* @val : value to store
* Writes a 64-bit unsigned integer to the given address
* (converted to little-endian order if necessary),
* regardless of the CPU's alignment requirements.
*
* Convert native value to unsigned unaligned 32-bit little-endian value
*
**/
static INLINE void retro_set_unaligned_64le(void *addr, uint64_t v) {
retro_unaligned64(addr) = retro_cpu_to_le64(v);
* @param addr The address to write the integer to.
* Does not need to be divisible by 8
* the way a \c uint64_t* usually would be.
* @param v The value to write.
*/
static INLINE void retro_set_unaligned_64le(void *addr, uint64_t v)
{
retro_unaligned64(addr) = retro_cpu_to_le64(v);
}
/**
* retro_set_unaligned_16be:
* @addr : pointer to unsigned 16-bit value
* @val : value to store
* Writes a 16-bit unsigned integer to the given address
* (converted to big-endian order if necessary),
* regardless of the CPU's alignment requirements.
*
* Convert native value to unsigned unaligned 16-bit big-endian value
*
**/
static INLINE void retro_set_unaligned_16be(void *addr, uint16_t v) {
retro_unaligned16(addr) = retro_cpu_to_be16(v);
* @param addr The address to write the integer to.
* Does not need to be divisible by 2
* the way a \c uint16_t* usually would be.
* @param v The value to write.
*/
static INLINE void retro_set_unaligned_16be(void *addr, uint16_t v)
{
retro_unaligned16(addr) = retro_cpu_to_be16(v);
}
/**
* retro_set_unaligned_32be:
* @addr : pointer to unsigned 32-bit value
* @val : value to store
* Writes a 32-bit unsigned integer to the given address
* (converted to big-endian order if necessary),
* regardless of the CPU's alignment requirements.
*
* Convert native value to unsigned unaligned 32-bit big-endian value
*
**/
static INLINE void retro_set_unaligned_32be(void *addr, uint32_t v) {
retro_unaligned32(addr) = retro_cpu_to_be32(v);
* @param addr The address to write the integer to.
* Does not need to be divisible by 4
* the way a \c uint32_t* usually would be.
* @param v The value to write.
*/
static INLINE void retro_set_unaligned_32be(void *addr, uint32_t v)
{
retro_unaligned32(addr) = retro_cpu_to_be32(v);
}
/**
* retro_set_unaligned_32be:
* @addr : pointer to unsigned 32-bit value
* @val : value to store
* Writes a 64-bit unsigned integer to the given address
* (converted to big-endian order if necessary),
* regardless of the CPU's alignment requirements.
*
* Convert native value to unsigned unaligned 32-bit big-endian value
*
**/
static INLINE void retro_set_unaligned_64be(void *addr, uint64_t v) {
retro_unaligned64(addr) = retro_cpu_to_be64(v);
* @param addr The address to write the integer to.
* Does not need to be divisible by 8
* the way a \c uint64_t* usually would be.
* @param v The value to write.
*/
static INLINE void retro_set_unaligned_64be(void *addr, uint64_t v)
{
retro_unaligned64(addr) = retro_cpu_to_be64(v);
}
@@ -32,9 +32,6 @@ need to be full of platform-specific workarounds.
*/
#if defined (__cplusplus)
#if 0
printf("This is C++, version %d.\n", __cplusplus);
#endif
/* The expected values would be
* 199711L, for ISO/IEC 14882:1998 or 14882:2003
*/
+6
View File
@@ -25,6 +25,12 @@
#ifndef INLINE
/**
* Cross-platform inline specifier.
*
* Expands to something like \c __inline or \c inline,
* depending on the compiler.
*/
#if defined(_WIN32) || defined(__INTEL_COMPILER)
#define INLINE __inline
#elif defined(__STDC_VERSION__) && __STDC_VERSION__>=199901L
+331 -7
View File
@@ -23,7 +23,7 @@
#ifndef __RARCH_MISCELLANEOUS_H
#define __RARCH_MISCELLANEOUS_H
#define RARCH_MAX_SUBSYSTEMS 10
#define RARCH_MAX_SUBSYSTEMS 20
#define RARCH_MAX_SUBSYSTEM_ROMS 10
#include <stdint.h>
@@ -49,6 +49,17 @@
#include <compat/msvc.h>
#endif
#ifdef IOS
#include <sys/param.h>
#endif
/**
* Computes the bitwise OR of two bit arrays.
*
* @param a[in,out] The first bit array, and the location of the result.
* @param b[in] The second bit array.
* @param count The length of each bit array, in 32-bit words.
*/
static INLINE void bits_or_bits(uint32_t *a, uint32_t *b, uint32_t count)
{
uint32_t i;
@@ -56,6 +67,14 @@ static INLINE void bits_or_bits(uint32_t *a, uint32_t *b, uint32_t count)
a[i] |= b[i];
}
/**
* Clears every bit in \c a that is set in \c b.
*
* @param a[in,out] The bit array to modify.
* @param b[in] The bit array to use for reference.
* @param count The length of each bit array, in 32-bit words
* (\em not bits or bytes).
*/
static INLINE void bits_clear_bits(uint32_t *a, uint32_t *b, uint32_t count)
{
uint32_t i;
@@ -63,6 +82,15 @@ static INLINE void bits_clear_bits(uint32_t *a, uint32_t *b, uint32_t count)
a[i] &= ~b[i];
}
/**
* Checks if any bits in \c ptr are set.
*
* @param ptr The bit array to check.
* @param count The length of the buffer pointed to by \c ptr, in 32-bit words
* (\em not bits or bytes).
* @return \c true if any bit in \c ptr is set,
* \c false if all bits are clear (zero).
*/
static INLINE bool bits_any_set(uint32_t* ptr, uint32_t count)
{
uint32_t i;
@@ -74,46 +102,261 @@ static INLINE bool bits_any_set(uint32_t* ptr, uint32_t count)
return false;
}
/**
* Checks if any bits in \c a are different from those in \c b.
*
* @param a The first bit array to compare.
* @param b The second bit array to compare.
* @param count The length of each bit array, in 32-bit words
* (\em not bits or bytes).
* @return \c true if \c and \c differ by at least one bit,
* \c false if they're both identical.
*/
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;
}
/**
* An upper limit for the length of a path (including the filename).
* If a path is longer than this, it may not work properly.
* This value may vary by platform.
*/
#if defined(_XBOX1) || defined(_3DS) || defined(PSP) || defined(PS2) || defined(GEKKO)|| defined(WIIU) || defined(__PSL1GHT__) || defined(__PS3__) || defined(HAVE_EMSCRIPTEN)
#ifndef PATH_MAX_LENGTH
#if defined(_XBOX1) || defined(_3DS) || defined(PSP) || defined(PS2) || defined(GEKKO)|| defined(WIIU) || defined(ORBIS) || defined(__PSL1GHT__) || defined(__PS3__)
#define PATH_MAX_LENGTH 512
#else
#define PATH_MAX_LENGTH 4096
#endif
#endif
#ifndef DIR_MAX_LENGTH
#define DIR_MAX_LENGTH 256
#endif
/**
* An upper limit for the length of a file or directory (excluding parent directories).
* If a path has a component longer than this, it may not work properly.
*/
#ifndef NAME_MAX_LENGTH
#define NAME_MAX_LENGTH 128
#endif
#else
#ifndef PATH_MAX_LENGTH
#define PATH_MAX_LENGTH 2048
#endif
#ifndef DIR_MAX_LENGTH
#define DIR_MAX_LENGTH 1024
#endif
/**
* An upper limit for the length of a file or directory (excluding parent directories).
* If a path has a component longer than this, it may not work properly.
*/
#ifndef NAME_MAX_LENGTH
#define NAME_MAX_LENGTH 256
#endif
#endif
#ifndef MAX
/**
* @return \c a or \c b, whichever is larger.
*/
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifndef MIN
/**
* @return \c a or \c b, whichever is smaller.
*/
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
/**
* Gets the number of elements in an array whose size is known at compile time.
* @param a An array of fixed length.
* @return The number of elements in \c a.
*/
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
/** @defgroup BITS Bit Arrays
*
* @{
*/
#define BITS_GET_ELEM(a, i) ((a).data[i])
#define BITS_GET_ELEM_PTR(a, i) ((a)->data[i])
/** @defgroup BIT_ Arbitrary-length Bit Arrays
*
* @{
*/
/**
* Sets a particular bit within a bit array to 1.
*
* @param a A \c uint8_t array,
* treated here as a bit vector.
* @param bit Index of the bit to set, where 0 is the least significant.
*/
#define BIT_SET(a, bit) ((a)[(bit) >> 3] |= (1 << ((bit) & 7)))
/**
* Clears a particular bit within a bit array.
*
* @param a A \c uint8_t array,
* treated here as a bit vector.
* @param bit Index of the bit to clear, where 0 is the least significant.
*/
#define BIT_CLEAR(a, bit) ((a)[(bit) >> 3] &= ~(1 << ((bit) & 7)))
/**
* Gets the value of a particular bit within a bit array.
*
* @param a A \c uint8_t array,
* treated here as a bit vector.
* @param bit Index of the bit to get, where 0 is the least significant.
* @return The value of the bit at the specified index.
*/
#define BIT_GET(a, bit) (((a)[(bit) >> 3] >> ((bit) & 7)) & 1)
/** @} */
/** @defgroup BIT16 16-bit Bit Arrays
*
* @{
*/
/**
* Sets a particular bit within a 16-bit integer to 1.
* @param a An unsigned 16-bit integer,
* treated as a bit array.
* @param bit Index of the bit to set, where 0 is the least significant and 15 is the most.
*/
#define BIT16_SET(a, bit) ((a) |= (1 << ((bit) & 15)))
/**
* Clears a particular bit within a 16-bit integer.
*
* @param a An unsigned 16-bit integer,
* treated as a bit array.
* @param bit Index of the bit to clear, where 0 is the least significant and 15 is the most.
*/
#define BIT16_CLEAR(a, bit) ((a) &= ~(1 << ((bit) & 15)))
/**
* Gets the value of a particular bit within a 16-bit integer.
*
* @param a An unsigned 16-bit integer,
* treated as a bit array.
* @param bit Index of the bit to get, where 0 is the least significant and 15 is the most.
* @return The value of the bit at the specified index.
*/
#define BIT16_GET(a, bit) (((a) >> ((bit) & 15)) & 1)
/**
* Clears all bits in a 16-bit bitmask.
*/
#define BIT16_CLEAR_ALL(a) ((a) = 0)
/** @} */
/** @defgroup BIT32 32-bit Bit Arrays
*
* @{
*/
/**
* Sets a particular bit within a 32-bit integer to 1.
*
* @param a An unsigned 32-bit integer,
* treated as a bit array.
* @param bit Index of the bit to set, where 0 is the least significant and 31 is the most.
*/
#define BIT32_SET(a, bit) ((a) |= (UINT32_C(1) << ((bit) & 31)))
/**
* Clears a particular bit within a 32-bit integer.
*
* @param a An unsigned 32-bit integer,
* treated as a bit array.
* @param bit Index of the bit to clear, where 0 is the least significant and 31 is the most.
*/
#define BIT32_CLEAR(a, bit) ((a) &= ~(UINT32_C(1) << ((bit) & 31)))
/**
* Gets the value of a particular bit within a 32-bit integer.
*
* @param a An unsigned 32-bit integer,
* treated as a bit array.
* @param bit Index of the bit to get, where 0 is the least significant and 31 is the most.
* @return The value of the bit at the specified index.
*/
#define BIT32_GET(a, bit) (((a) >> ((bit) & 31)) & 1)
/**
* Clears all bits in a 32-bit bitmask.
*
* @param a An unsigned 32-bit integer,
* treated as a bit array.
*/
#define BIT32_CLEAR_ALL(a) ((a) = 0)
/** @} */
/**
* @defgroup BIT64 64-bit Bit Arrays
* @{
*/
/**
* Sets a particular bit within a 64-bit integer to 1.
*
* @param a An unsigned 64-bit integer,
* treated as a bit array.
* @param bit Index of the bit to set, where 0 is the least significant and 63 is the most.
*/
#define BIT64_SET(a, bit) ((a) |= (UINT64_C(1) << ((bit) & 63)))
/**
* Clears a particular bit within a 64-bit integer.
*
* @param a An unsigned 64-bit integer,
* treated as a bit array.
* @param bit Index of the bit to clear, where 0 is the least significant and 63 is the most.
*/
#define BIT64_CLEAR(a, bit) ((a) &= ~(UINT64_C(1) << ((bit) & 63)))
/**
* Gets the value of a particular bit within a 64-bit integer.
*
* @param a An unsigned 64-bit integer,
* treated as a bit array.
* @param bit Index of the bit to get, where 0 is the least significant and 63 is the most.
* @return The value of the bit at the specified index.
*/
#define BIT64_GET(a, bit) (((a) >> ((bit) & 63)) & 1)
/**
* Clears all bits in a 64-bit bitmask.
*
* @param a An unsigned 64-bit integer,
* treated as a bit array.
*/
#define BIT64_CLEAR_ALL(a) ((a) = 0)
/** @} */
#define BIT128_SET(a, bit) ((a).data[(bit) >> 5] |= (UINT32_C(1) << ((bit) & 31)))
#define BIT128_CLEAR(a, bit) ((a).data[(bit) >> 5] &= ~(UINT32_C(1) << ((bit) & 31)))
#define BIT128_GET(a, bit) (((a).data[(bit) >> 5] >> ((bit) & 31)) & 1)
@@ -124,24 +367,98 @@ static INLINE bool bits_any_set(uint32_t* ptr, uint32_t count)
#define BIT128_GET_PTR(a, bit) BIT128_GET(*a, bit)
#define BIT128_CLEAR_ALL_PTR(a) BIT128_CLEAR_ALL(*a)
/**
* Sets a single bit from a 256-bit \c retro_bits_t to 1.
*
* @param a A 256-bit \c retro_bits_t.
* @param bit Index of the bit to set,
* where 0 is the least significant and 255 is the most.
*/
#define BIT256_SET(a, bit) BIT128_SET(a, bit)
/**
* Clears a single bit from a 256-bit \c retro_bits_t.
*
* @param a A 256-bit \c retro_bits_t.
* @param bit Index of the bit to clear,
* where 0 is the least significant and 255 is the most.
*/
#define BIT256_CLEAR(a, bit) BIT128_CLEAR(a, bit)
/**
* Gets the value of a single bit from a 256-bit \c retro_bits_t.
*
* @param a A 256-bit \c retro_bits_t.
* @param bit Index of the bit to get,
* where 0 is the least significant and 255 is the most.
* @return The value of the bit at the specified index.
*/
#define BIT256_GET(a, bit) BIT128_GET(a, bit)
/**
* Clears all bits in a 256-bit \c retro_bits_t.
*
* @param a A 256-bit \c retro_bits_t.
*/
#define BIT256_CLEAR_ALL(a) BIT128_CLEAR_ALL(a)
/** Variant of BIT256_SET() that takes a pointer to a \c retro_bits_t. */
#define BIT256_SET_PTR(a, bit) BIT256_SET(*a, bit)
/** Variant of BIT256_CLEAR() that takes a pointer to a \c retro_bits_t. */
#define BIT256_CLEAR_PTR(a, bit) BIT256_CLEAR(*a, bit)
/** Variant of BIT256_GET() that takes a pointer to a \c retro_bits_t. */
#define BIT256_GET_PTR(a, bit) BIT256_GET(*a, bit)
/** Variant of BIT256_CLEAR_ALL() that takes a pointer to a \c retro_bits_t. */
#define BIT256_CLEAR_ALL_PTR(a) BIT256_CLEAR_ALL(*a)
/**
* Sets a single bit from a 512-bit \c retro_bits_512_t to 1.
*
* @param a A 512-bit \c retro_bits_512_t.
* @param bit Index of the bit to set,
* where 0 is the least significant and 511 is the most.
*/
#define BIT512_SET(a, bit) BIT256_SET(a, bit)
/**
* Clears a single bit from a 512-bit \c retro_bits_512_t.
*
* @param a A 512-bit \c retro_bits_512_t.
* @param bit Index of the bit to clear,
* where 0 is the least significant and 511 is the most.
*/
#define BIT512_CLEAR(a, bit) BIT256_CLEAR(a, bit)
/**
* Gets the value of a single bit from a 512-bit \c retro_bits_512_t.
*
* @param a A 512-bit \c retro_bits_512_t.
* @param bit Index of the bit to get,
* where 0 is the least significant and 511 is the most.
* @return The value of the bit at the specified index.
*/
#define BIT512_GET(a, bit) BIT256_GET(a, bit)
/**
* Clears all bits in a 512-bit \c retro_bits_512_t.
*
* @param a A 512-bit \c retro_bits_512_t.
*/
#define BIT512_CLEAR_ALL(a) BIT256_CLEAR_ALL(a)
/** Variant of BIT512_SET() that takes a pointer to a \c retro_bits_512_t. */
#define BIT512_SET_PTR(a, bit) BIT512_SET(*a, bit)
/** Variant of BIT512_CLEAR() that takes a pointer to a \c retro_bits_512_t. */
#define BIT512_CLEAR_PTR(a, bit) BIT512_CLEAR(*a, bit)
/** Variant of BIT512_GET() that takes a pointer to a \c retro_bits_512_t. */
#define BIT512_GET_PTR(a, bit) BIT512_GET(*a, bit)
/** Variant of BIT512_CLEAR_ALL() that takes a pointer to a \c retro_bits_512_t. */
#define BIT512_CLEAR_ALL_PTR(a) BIT512_CLEAR_ALL(*a)
#define BITS_COPY16_PTR(a,bits) \
@@ -164,18 +481,23 @@ static INLINE bool bits_any_set(uint32_t* ptr, uint32_t count)
}
/* Helper macros and struct to keep track of many booleans. */
/* This struct has 256 bits. */
/** A 256-bit boolean array. */
typedef struct
{
/** @private 256 bits. Not intended for direct use. */
uint32_t data[8];
} retro_bits_t;
/* This struct has 512 bits. */
/** A 512-bit boolean array. */
typedef struct
{
/** @private 512 bits. Not intended for direct use. */
uint32_t data[16];
} retro_bits_512_t;
/** @} */
#ifdef _WIN32
# ifdef _WIN64
# define PRI_SIZET PRIu64
@@ -188,6 +510,8 @@ typedef struct
# endif
#elif defined(PS2)
# define PRI_SIZET "u"
#elif defined(__EMSCRIPTEN__)
# define PRI_SIZET "zu"
#else
# if (SIZE_MAX == 0xFFFF)
# define PRI_SIZET "hu"
+327 -8
View File
@@ -38,78 +38,397 @@
#include <stdarg.h>
#include <vfs/vfs_implementation.h>
/** @defgroup file_stream File Streams
*
* All functions in this header will use the VFS interface set in \ref filestream_vfs_init if possible,
* or else they will fall back to built-in equivalents.
*
* @note These functions are modeled after those in the C standard library
* (and may even use them internally),
* but identical behavior is not guaranteed.
*
* @{
*/
/**
* The minimum version of the VFS interface required by the \c filestream functions.
*/
#define FILESTREAM_REQUIRED_VFS_VERSION 2
RETRO_BEGIN_DECLS
/**
* Opaque handle to a file stream.
* @warning This is not interchangeable with \c FILE* or \c retro_vfs_file_handle.
*/
typedef struct RFILE RFILE;
#define FILESTREAM_REQUIRED_VFS_VERSION 2
/**
* Initializes the \c filestream functions to use the VFS interface provided by the frontend.
* Optional; if not called, all \c filestream functions
* will use libretro-common's built-in implementations.
*
* @param vfs_info The VFS interface returned by the frontend.
* If \c vfs_info::iface (but \em not \c vfs_info itself) is \c NULL,
* then libretro-common's built-in VFS implementation will be used.
*/
void filestream_vfs_init(const struct retro_vfs_interface_info* vfs_info);
/**
* Returns the size of the given file, in bytes.
*
* @param stream The open file to query.
* @return The size of \c stream in bytes,
* or -1 if there was an error.
* @see retro_vfs_size_t
*/
int64_t filestream_get_size(RFILE *stream);
/**
* Sets the size of the given file,
* truncating or extending it as necessary.
*
* @param stream The file to resize.
* @param length The new size of \c stream, in bytes.
* @return 0 if the resize was successful,
* or -1 if there was an error.
* @see retro_vfs_truncate_t
*/
int64_t filestream_truncate(RFILE *stream, int64_t length);
/**
* filestream_open:
* @path : path to file
* @mode : file mode to use when opening (read/write)
* @bufsize : optional buffer size (-1 or 0 to use default)
* Opens a file for reading or writing.
*
* Opens a file for reading or writing, depending on the requested mode.
* Returns a pointer to an RFILE if opened successfully, otherwise NULL.
**/
* @param path Path to the file to open.
* Should be in the format described in \ref GET_VFS_INTERFACE.
* @param mode The mode to open the file in.
* Should be one or more of the flags in \refitem RETRO_VFS_FILE_ACCESS OR'd together,
* and \c RETRO_VFS_FILE_ACCESS_READ or \c RETRO_VFS_FILE_ACCESS_WRITE
* (or both) must be included.
* @param hints Optional hints to pass to the frontend.
*
* @return The opened file, or \c NULL if there was an error.
* Must be cleaned up with \c filestream_close when no longer needed.
* @see retro_vfs_open_t
*/
RFILE* filestream_open(const char *path, unsigned mode, unsigned hints);
/**
* Sets the current position of the file stream.
* Use this to read specific sections of a file.
*
* @param stream The file to set the stream position of.
* @param offset The new stream position, in bytes.
* @param seek_position The position to seek from.
* Should be one of the values in \refitem RETRO_VFS_SEEK_POSITION.
* @return The new stream position in bytes relative to the beginning,
* or -1 if there was an error.
* @see RETRO_VFS_SEEK_POSITION
* @see retro_vfs_seek_t
*/
int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position);
/**
* Reads data from the given file into a buffer.
* If the read is successful,
* the file's stream position will advance by the number of bytes read.
*
* @param stream The file to read from.
* @param data The buffer in which to store the read data.
* @param len The size of \c data, in bytes.
* @return The number of bytes read,
* or -1 if there was an error.
* May be less than \c len, but never more.
* @see retro_vfs_read_t
*/
int64_t filestream_read(RFILE *stream, void *data, int64_t len);
/**
* Writes data from a buffer to the given file.
* If the write is successful,
* the file's stream position will advance by the number of bytes written.
*
* @param stream The file to write to.
* @param data The buffer containing the data to write.
* @param len The size of \c data, in bytes.
* @return The number of bytes written,
* or -1 if there was an error.
* May be less than \c len, but never more.
* @see retro_vfs_write_t
*/
int64_t filestream_write(RFILE *stream, const void *data, int64_t len);
/**
* Returns the current position of the given file in bytes.
*
* @param stream The file to return the stream position for.
* @return The current stream position in bytes relative to the beginning,
* or -1 if there was an error.
* @see retro_vfs_tell_t
*/
int64_t filestream_tell(RFILE *stream);
/**
* Rewinds the given file to the beginning.
* Equivalent to <tt>filestream_seek(stream, 0, RETRO_VFS_SEEK_POSITION_START)</tt>.
* @param stream The file to rewind.
* May be \c NULL, in which case this function does nothing.
*/
void filestream_rewind(RFILE *stream);
/**
* Closes the given file.
*
* @param stream The file to close.
* This should have been created with \c filestream_open.
* Behavior is undefined if \c NULL.
* @return 0 if the file was closed successfully,
* or -1 if there was an error.
* @post \c stream is no longer valid and should not be used,
* even if this function fails.
* @see retro_vfs_close_t
*/
int filestream_close(RFILE *stream);
/**
* Opens a file, reads its contents into a newly-allocated buffer,
* then closes it.
*
* @param path[in] Path to the file to read.
* Should be in the format described in \ref GET_VFS_INTERFACE.
* @param buf[out] A pointer to the address of the newly-allocated buffer.
* The buffer will contain the entirety of the file at \c path.
* Will be allocated with \c malloc and must be freed with \c free.
* @param len[out] Pointer to the size of the buffer in bytes.
* May be \c NULL, in which case the length is not written.
* Value is unspecified if this function fails.
* @return 1 if the file was read successfully,
* 0 if there was an error.
* @see filestream_write_file
*/
int64_t filestream_read_file(const char *path, void **buf, int64_t *len);
/**
* Reads a line of text from the given file,
* up to a given length.
*
* Will read to the next newline or until the buffer is full,
* whichever comes first.
*
* @param stream The file to read from.
* @param s The buffer to write the retrieved line to.
* Will contain at most \c len - 1 characters
* plus a null terminator.
* The newline character (if any) will not be included.
* The line delimiter must be Unix-style (\c '\n').
* Carriage returns (\c '\r') will not be treated specially.
* @param len The length of the buffer \c s, in bytes.
* @return \s if successful, \c NULL if there was an error.
*/
char* filestream_gets(RFILE *stream, char *s, size_t len);
/**
* Reads a single character from the given file.
*
* @param stream The file to read from.
* @return The character read, or -1 upon reaching the end of the file.
*/
int filestream_getc(RFILE *stream);
/**
* Reads formatted text from the given file,
* with arguments provided as a standard \c va_list.
*
* @param stream The file to read from.
* @param format The string to write, possibly including scanf-compatible format specifiers.
* @param args Argument list with zero or more elements
* whose values will be updated according to the semantics of \c format.
* @return The number of arguments in \c args that were successfully assigned,
* or -1 if there was an error.
* @see https://en.cppreference.com/w/c/io/fscanf
* @see https://en.cppreference.com/w/c/variadic
*/
int filestream_vscanf(RFILE *stream, const char* format, va_list *args);
/**
* Reads formatted text from the given file.
*
* @param stream The file to read from.
* @param format The string to write, possibly including scanf-compatible format specifiers.
* @param ... Zero or more arguments that will be updated according to the semantics of \c format.
* @return The number of arguments in \c ... that were successfully assigned,
* or -1 if there was an error.
* @see https://en.cppreference.com/w/c/io/fscanf
*/
int filestream_scanf(RFILE *stream, const char* format, ...);
/**
* Determines if there's any more data left to read from this file.
*
* @param stream The file to check the position of.
* @return -1 if this stream has reached the end of the file,
* 0 if not.
*/
int filestream_eof(RFILE *stream);
/**
* Writes the entirety of a given buffer to a file at a given path.
* Any file that already exists will be overwritten.
*
* @param path Path to the file that will be written to.
* @param data The buffer to write to \c path.
* @param size The size of \c data, in bytes.
* @return \c true if the file was written successfully,
* \c false if there was an error.
*/
bool filestream_write_file(const char *path, const void *data, int64_t size);
/**
* Writes a single character to the given file.
*
* @param stream The file to write to.
* @param c The character to write.
* @return The character written,
* or -1 if there was an error.
* Will return -1 if \c stream is \c NULL.
*/
int filestream_putc(RFILE *stream, int c);
/**
* Writes formatted text to the given file,
* with arguments provided as a standard \c va_list.
*
* @param stream The file to write to.
* @param format The string to write, possibly including printf-compatible format specifiers.
* @param args A list of arguments to be formatted and inserted in the resulting string.
* @return The number of characters written,
* or -1 if there was an error.
* @see https://en.cppreference.com/w/c/io/vfprintf
* @see https://en.cppreference.com/w/c/variadic
*/
int filestream_vprintf(RFILE *stream, const char* format, va_list args);
/**
* Writes formatted text to the given file.
*
* @param stream The file to write to.
* @param format The string to write, possibly including printf-compatible format specifiers.
* @param ... Zero or more arguments to be formatted and inserted into the resulting string.
* @return The number of characters written,
* or -1 if there was an error.
* @see https://en.cppreference.com/w/c/io/printf
*/
int filestream_printf(RFILE *stream, const char* format, ...);
/**
* Checks if there was an error in using the given file stream.
*
* @param stream The file stream to check for errors.
* @return \c true if there was an error in using this stream,
* \c false if not or if \c stream is \c NULL.
*/
int filestream_error(RFILE *stream);
/**
* Flushes pending writes to the operating system's file layer.
* There is no guarantee that pending writes will be written to disk immediately.
*
* @param stream The file to flush.
* @return 0 if the flush was successful,
* or -1 if there was an error.
* @see retro_vfs_flush_t
*/
int filestream_flush(RFILE *stream);
/**
* Deletes the file at the given path.
* If the file is open by any process,
* the behavior is platform-specific.
*
* @note This function might or might not delete directories recursively,
* depending on the platform and the underlying VFS implementation.
*
* @param path The file to delete.
* @return 0 if the file was deleted successfully,
* or -1 if there was an error.
* @see retro_vfs_remove_t
*/
int filestream_delete(const char *path);
/**
* Moves a file to a new location, with a new name.
*
* @param old_path Path to the file to rename.
* @param new_path The target name and location of the file.
* @return 0 if the file was renamed successfully,
* or -1 if there was an error.
* @see retro_vfs_rename_t
*/
int filestream_rename(const char *old_path, const char *new_path);
/**
* Copies a file to a new location.
*
* @param src_path Path to the file to rename.
* @param dst_path The target name and location of the file.
* @return 0 if the file was copied successfully,
* or -1 if there was an error.
*/
int filestream_copy(const char *src_path, const char *dst_path);
/**
* Compares and verifies files.
*
* @param src_path Path to the file.
* @param dst_path Path to the other file.
* @return 0 if the files are equal,
* or -1 if there was an error.
*/
int filestream_cmp(const char *src_path, const char *dst_path);
/**
* Get the path that was used to open a file.
*
* @param stream The file to get the path of.
* @return The path that was used to open \c stream,
* or \c NULL if there was an error.
* The string is owned by \c stream and must not be modified or freed by the caller.
*/
const char* filestream_get_path(RFILE *stream);
/**
* Determines if a file exists at the given path.
*
* @param path The path to check for existence.
* @return \c true if a file exists at \c path,
* \c false if not or if \c path is \c NULL or empty.
*/
bool filestream_exists(const char *path);
/* Returned pointer must be freed by the caller. */
/**
* Reads a line from the given file into a newly-allocated buffer.
*
* @param stream The file to read from.
* @return Pointer to the line read from \c stream,
* or \c NULL if there was an error.
* Must be freed with \c free when no longer needed.
*/
char* filestream_getline(RFILE *stream);
/**
* Returns the open file handle
* that was originally returned by the VFS interface.
*
* @param stream File handle returned by \c filestream_open.
* @return The file handle returned by the underlying VFS implementation.
*/
libretro_vfs_implementation_file* filestream_get_vfs_handle(RFILE *stream);
RETRO_END_DECLS
/** @} */
#endif
@@ -28,10 +28,23 @@
#include <retro_common_api.h>
#include <streams/file_stream.h>
/**
* @file file_stream_transforms.h
*
* Contains macros that redirect standard C I/O functions
* to libretro's own file stream API.
* Useful when porting an existing emulator to a core.
* To use these functions without overriding the standard I/O functions,
* define \c SKIP_STDIO_REDEFINES before including this header.
*
* @see https://man7.org/linux/man-pages/man3/stdio.3.html
*/
RETRO_BEGIN_DECLS
#ifndef SKIP_STDIO_REDEFINES
/** @see https://en.cppreference.com/w/c/io/FILE */
#define FILE RFILE
#undef fopen
@@ -66,34 +79,48 @@ RETRO_BEGIN_DECLS
#endif
/** @see https://en.cppreference.com/w/c/io/fopen */
RFILE* rfopen(const char *path, const char *mode);
/** @see https://en.cppreference.com/w/c/io/fclose */
int rfclose(RFILE* stream);
/** @see https://en.cppreference.com/w/c/io/ftell */
int64_t rftell(RFILE* stream);
/** @see https://en.cppreference.com/w/c/io/fseek */
int64_t rfseek(RFILE* stream, int64_t offset, int origin);
/** @see https://en.cppreference.com/w/c/io/fread */
int64_t rfread(void* buffer,
size_t elem_size, size_t elem_count, RFILE* stream);
char *rfgets(char *buffer, int maxCount, RFILE* stream);
/** @see https://en.cppreference.com/w/c/io/fgets */
char *rfgets(char *s, int maxCount, RFILE* stream);
/** @see https://en.cppreference.com/w/c/io/fgetc */
int rfgetc(RFILE* stream);
/** @see https://en.cppreference.com/w/c/io/fwrite */
int64_t rfwrite(void const* buffer,
size_t elem_size, size_t elem_count, RFILE* stream);
/** @see https://en.cppreference.com/w/c/io/fputc */
int rfputc(int character, RFILE * stream);
/** @see https://en.cppreference.com/w/c/io/fflush */
int64_t rfflush(RFILE * stream);
/** @see https://en.cppreference.com/w/c/io/fprintf */
int rfprintf(RFILE * stream, const char * format, ...);
/** @see https://en.cppreference.com/w/c/io/ferror */
int rferror(RFILE* stream);
/** @see https://en.cppreference.com/w/c/io/feof */
int rfeof(RFILE* stream);
/** @see https://en.cppreference.com/w/c/io/fscanf */
int rfscanf(RFILE * stream, const char * format, ...);
RETRO_END_DECLS
+210 -78
View File
@@ -37,18 +37,13 @@ RETRO_BEGIN_DECLS
#define STRLEN_CONST(x) ((sizeof((x))-1))
#define strcpy_literal(a, b) strcpy(a, b)
#define string_is_not_equal(a, b) !string_is_equal((a), (b))
#define string_is_not_equal_fast(a, b, size) (memcmp(a, b, size) != 0)
#define string_is_equal_fast(a, b, size) (memcmp(a, b, size) == 0)
#define TOLOWER(c) ((c) | (lr_char_props[(unsigned char)(c)] & 0x20))
#define TOUPPER(c) ((c) & ~(lr_char_props[(unsigned char)(c)] & 0x20))
/* C standard says \f \v are space, but this one disagrees */
#define ISSPACE(c) (lr_char_props[(unsigned char)(c)] & 0x80)
#define ISSPACE(c) (lr_char_props[(unsigned char)(c)] & 0x80)
#define ISDIGIT(c) (lr_char_props[(unsigned char)(c)] & 0x40)
#define ISALPHA(c) (lr_char_props[(unsigned char)(c)] & 0x20)
@@ -73,9 +68,9 @@ static INLINE bool string_is_equal(const char *a, const char *b)
}
static INLINE bool string_starts_with_size(const char *str, const char *prefix,
size_t size)
size_t len)
{
return (str && prefix) ? !strncmp(prefix, str, size) : false;
return (str && prefix) ? !strncmp(prefix, str, len) : false;
}
static INLINE bool string_starts_with(const char *str, const char *prefix)
@@ -83,30 +78,34 @@ static INLINE bool string_starts_with(const char *str, const char *prefix)
return (str && prefix) ? !strncmp(prefix, str, strlen(prefix)) : false;
}
static INLINE bool string_ends_with_size(const char *str, const char *suffix,
size_t str_len, size_t suffix_len)
static INLINE bool string_ends_with_size(const char *s, const char *suffix,
size_t len, size_t suffix_len)
{
return (str_len < suffix_len) ? false :
!memcmp(suffix, str + (str_len - suffix_len), suffix_len);
return (len < suffix_len) ? false :
!memcmp(suffix, s + (len - suffix_len), suffix_len);
}
static INLINE bool string_ends_with(const char *str, const char *suffix)
static INLINE bool string_ends_with(const char *s, const char *suffix)
{
if (!str || !suffix)
return false;
return string_ends_with_size(str, suffix, strlen(str), strlen(suffix));
return s && suffix && string_ends_with_size(s, suffix, strlen(s), 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' */
static INLINE size_t strlen_size(const char *str, size_t size)
* in the first 'size' characters, returns 'size'
**/
static INLINE size_t strlen_size(const char *str, size_t len)
{
size_t i = 0;
if (str)
while (i < size && str[i]) i++;
while (i < len && str[i]) i++;
return i;
}
@@ -130,25 +129,69 @@ 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, size_t in_len,
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 +199,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,
**/
size_t word_wrap(char *dst, size_t dst_size, const char *src, size_t src_len,
int line_width, int wideglyph_width, unsigned max_lines);
/*
* 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.
**/
size_t word_wrap_wideglyph(
char *dst, size_t dst_size,
const char *src, size_t src_len,
int line_width, int wideglyph_width,
unsigned max_lines);
/* Splits string into tokens seperated by 'delim'
/**
* string_tokenize:
*
* Splits string into tokens separated 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,28 +262,118 @@ 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);
**/
char *string_tokenize(char **str, const char *delim);
/* Removes every instance of character 'c' from 'str' */
void string_remove_all_chars(char *str, char c);
/**
* string_remove_all_chars:
* @s : input string (must be non-NULL, otherwise UB)
*
* Leaf function.
*
* Removes every instance of character @c from @s
*
* Returns the length of the resulting string.
**/
size_t string_remove_all_chars(char *s, 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);
/**
* 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);
void string_set(char **string, const char *src);
/**
* 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.
* Returns the length of the resulting string.
**/
size_t 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];
+18 -3
View File
@@ -34,13 +34,28 @@ RETRO_BEGIN_DECLS
/* TODO/FIXME: Move all generic time handling functions
* to this file */
/* Must be called before using rtime_localtime() */
/**
* Must be called before using \c rtime_localtime().
* May be called multiple times without ill effects,
* but must only be called from the main thread.
*/
void rtime_init(void);
/* Must be called upon program termination */
/**
* Must be called upon program or core termination.
* May be called multiple times without ill effects,
* but must only be called from the main thread.
*/
void rtime_deinit(void);
/* Thread-safe wrapper for localtime() */
/**
* Thread-safe wrapper around standard \c localtime(),
* which by itself is not guaranteed to be thread-safe.
* @param timep Pointer to a time_t object to convert.
* @param result Pointer to a tm object to store the result in.
* @return \c result.
* @see https://en.cppreference.com/w/c/chrono/localtime
*/
struct tm *rtime_localtime(const time_t *timep, struct tm *result);
RETRO_END_DECLS
+9 -2
View File
@@ -25,6 +25,7 @@
#include <retro_common_api.h>
#include <boolean.h>
#include <stdint.h>
#ifdef RARCH_INTERNAL
#ifndef VFS_FRONTEND
@@ -59,10 +60,12 @@ typedef struct
enum vfs_scheme
{
VFS_SCHEME_NONE = 0,
VFS_SCHEME_CDROM
VFS_SCHEME_CDROM,
VFS_SCHEME_SAF,
VFS_SCHEME_SMB
};
#ifndef __WINRT__
#if !(defined(__WINRT__) && defined(__cplusplus_winrt))
#ifdef VFS_FRONTEND
struct retro_vfs_file_handle
#else
@@ -85,6 +88,10 @@ struct libretro_vfs_implementation_file
int fd;
unsigned hints;
enum vfs_scheme scheme;
#ifdef HAVE_SMBCLIENT
intptr_t smb_fh;
intptr_t smb_ctx;
#endif
};
#endif
@@ -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
+239 -173
View File
@@ -25,7 +25,6 @@
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <errno.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -35,7 +34,8 @@
#include <compat/msvc.h>
#endif
#include <string/stdstring.h>
#include <retro_miscellaneous.h>
#include <file/file_path.h>
#include <streams/file_stream.h>
#define VFS_FRONTEND
#include <vfs/vfs_implementation.h>
@@ -45,8 +45,7 @@
struct RFILE
{
struct retro_vfs_file_handle *hfile;
bool error_flag;
bool eof_flag;
bool err_flag;
};
static retro_vfs_get_path_t filestream_get_path_cb = NULL;
@@ -83,7 +82,7 @@ void filestream_vfs_init(const struct retro_vfs_interface_info* vfs_info)
filestream_rename_cb = NULL;
if (
(vfs_info->required_interface_version <
(vfs_info->required_interface_version <
FILESTREAM_REQUIRED_VFS_VERSION)
|| !vfs_iface)
return;
@@ -109,18 +108,13 @@ bool filestream_exists(const char *path)
if (!path || !*path)
return false;
dummy = filestream_open(
path,
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;
@@ -137,7 +131,7 @@ int64_t filestream_get_size(RFILE *stream)
(libretro_vfs_implementation_file*)stream->hfile);
if (output == VFS_ERROR_RETURN_VALUE)
stream->error_flag = true;
stream->err_flag = true;
return output;
}
@@ -153,24 +147,18 @@ int64_t filestream_truncate(RFILE *stream, int64_t length)
(libretro_vfs_implementation_file*)stream->hfile, length);
if (output == VFS_ERROR_RETURN_VALUE)
stream->error_flag = true;
stream->err_flag = true;
return output;
}
/**
* filestream_open:
* @path : path to file
* @mode : file mode to use when opening (read/write)
* @hints :
*
* Opens a file for reading or writing, depending on the requested mode.
* Returns a pointer to an RFILE if opened successfully, otherwise NULL.
**/
RFILE* filestream_open(const char *path, unsigned mode, unsigned hints)
{
struct retro_vfs_file_handle *fp = NULL;
RFILE* output = NULL;
RFILE* output = (RFILE*)malloc(sizeof(RFILE));
if (!output)
return NULL;
if (filestream_open_cb)
fp = (struct retro_vfs_file_handle*)
@@ -180,12 +168,13 @@ RFILE* filestream_open(const char *path, unsigned mode, unsigned hints)
retro_vfs_file_open_impl(path, mode, hints);
if (!fp)
{
free(output);
return NULL;
}
output = (RFILE*)malloc(sizeof(RFILE));
output->error_flag = false;
output->eof_flag = false;
output->hfile = fp;
output->err_flag = false;
output->hfile = fp;
return output;
}
@@ -221,107 +210,135 @@ int filestream_getc(RFILE *stream)
return EOF;
}
int filestream_vscanf(RFILE *stream, const char* format, va_list *args)
int filestream_vscanf(RFILE *stream, const char *format, va_list *args)
{
char buf[4096];
char subfmt[64];
char subfmt[256];
va_list args_copy;
const char * bufiter = buf;
const char *bufiter = buf;
int ret = 0;
int64_t startpos = filestream_tell(stream);
int64_t maxlen = filestream_read(stream, buf, sizeof(buf)-1);
int64_t maxlen = filestream_read(stream, buf, sizeof(buf) - 1);
if (maxlen <= 0)
return EOF;
buf[maxlen] = '\0';
/* 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);
while (*format)
#endif
while (*format && *bufiter)
{
if (*format == '%')
{
int sublen;
char* subfmtiter = subfmt;
bool asterisk = false;
*subfmtiter++ = *format++; /* '%' */
int sublen = 0; /* Fix 4: initialize to 0 */
char *subfmtiter = subfmt;
char *subfmtend = subfmt + sizeof(subfmt) - 4; /* reserve room for %n\0 */
bool asterisk = false;
*subfmtiter++ = *format++; /* '%' */
/* handle %% literal percent */
if (*format == '%')
{
if (*bufiter != '%')
break;
bufiter++;
format++;
continue;
}
/* %[*][width][length]specifier */
if (*format == '*')
{
asterisk = true;
*subfmtiter++ = *format++;
}
while (ISDIGIT((unsigned char)*format))
*subfmtiter++ = *format++; /* width */
/* length */
/* width digits */
while (*format >= '0' && *format <= '9' && subfmtiter < subfmtend)
*subfmtiter++ = *format++;
/* length modifier */
if (*format == 'h' || *format == 'l')
{
if (format[1] == format[0])
if (format[1] == format[0] && subfmtiter < subfmtend)
*subfmtiter++ = *format++;
if (subfmtiter < subfmtend)
*subfmtiter++ = *format++;
*subfmtiter++ = *format++;
}
else if (
*format == 'j' ||
*format == 'z' ||
*format == 't' ||
*format == 'L')
else if (*format == 'j' || *format == 'z'
|| *format == 't' || *format == 'L')
{
*subfmtiter++ = *format++;
if (subfmtiter < subfmtend)
*subfmtiter++ = *format++;
}
/* specifier - always a single character (except ]) */
/* specifier */
if (*format == '[')
{
while (*format != ']')
/* Fix 2: bounds-check subfmt and guard against missing ']' */
*subfmtiter++ = *format++; /* '[' */
/* handle negation */
if (*format == '^' && subfmtiter < subfmtend)
*subfmtiter++ = *format++;
*subfmtiter++ = *format++;
/* handle literal ']' as first character in scanset */
if (*format == ']' && subfmtiter < subfmtend)
*subfmtiter++ = *format++;
while (*format && *format != ']' && subfmtiter < subfmtend)
*subfmtiter++ = *format++;
if (*format == ']')
*subfmtiter++ = *format++;
else
break; /* malformed format string — missing ']' */
}
else if (*format)
*subfmtiter++ = *format++;
else
*subfmtiter++ = *format++;
*subfmtiter++ = '%';
*subfmtiter++ = 'n';
*subfmtiter++ = '\0';
if (sizeof(void*) != sizeof(long*))
abort(); /* all pointers must have the same size */
break; /* format string ended after '%' + modifiers with no specifier */
/* append %n to measure consumed characters */
*subfmtiter++ = '%';
*subfmtiter++ = 'n';
*subfmtiter = '\0';
if (asterisk)
{
int v = sscanf(bufiter, subfmt, &sublen);
if (v == EOF)
return EOF;
if (v != 0)
break;
/* Fix 1: do NOT increment ret for suppressed assignments */
if (sublen == 0)
break; /* no input consumed — stop */
}
else
{
int v = sscanf(bufiter, subfmt, va_arg(args_copy, void*), &sublen);
if (v == EOF)
return EOF;
break;
if (v != 1)
break;
ret++; /* Fix 1: only increment for actual assignments */
}
ret++;
bufiter += sublen;
}
else if (isspace((unsigned char)*format))
{
/* a single whitespace in format matches zero or more in input */
while (isspace((unsigned char)*bufiter))
bufiter++;
format++;
/* skip all contiguous whitespace in format too */
while (isspace((unsigned char)*format))
format++;
}
else
{
@@ -331,11 +348,22 @@ int filestream_vscanf(RFILE *stream, const char* format, va_list *args)
format++;
}
}
va_end(args_copy);
filestream_seek(stream, startpos+(bufiter-buf),
filestream_seek(stream, startpos + (bufiter - buf),
RETRO_VFS_SEEK_POSITION_START);
return ret;
}
int filestream_scanf(RFILE *stream, const char* format, ...)
{
int ret;
va_list vl;
va_start(vl, format);
ret = filestream_vscanf(stream, format, &vl);
va_end(vl);
return ret;
}
@@ -351,30 +379,28 @@ int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position)
offset, seek_position);
if (output == VFS_ERROR_RETURN_VALUE)
stream->error_flag = true;
stream->eof_flag = false;
stream->err_flag = true;
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)
{
int64_t output;
if (filestream_size_cb)
if (filestream_tell_cb)
output = filestream_tell_cb(stream->hfile);
else
output = retro_vfs_file_tell_impl(
(libretro_vfs_implementation_file*)stream->hfile);
if (output == VFS_ERROR_RETURN_VALUE)
stream->error_flag = true;
stream->err_flag = true;
return output;
}
@@ -384,8 +410,7 @@ void filestream_rewind(RFILE *stream)
if (!stream)
return;
filestream_seek(stream, 0L, RETRO_VFS_SEEK_POSITION_START);
stream->error_flag = false;
stream->eof_flag = false;
stream->err_flag = false;
}
int64_t filestream_read(RFILE *stream, void *s, int64_t len)
@@ -399,9 +424,7 @@ int64_t filestream_read(RFILE *stream, void *s, int64_t len)
(libretro_vfs_implementation_file*)stream->hfile, s, len);
if (output == VFS_ERROR_RETURN_VALUE)
stream->error_flag = true;
if (output < len)
stream->eof_flag = true;
stream->err_flag = true;
return output;
}
@@ -417,7 +440,7 @@ int filestream_flush(RFILE *stream)
(libretro_vfs_implementation_file*)stream->hfile);
if (output == VFS_ERROR_RETURN_VALUE)
stream->error_flag = true;
stream->err_flag = true;
return output;
}
@@ -438,6 +461,77 @@ int filestream_rename(const char *old_path, const char *new_path)
return retro_vfs_file_rename_impl(old_path, new_path);
}
int filestream_copy(const char *src, const char *dst)
{
char buf[256] = {0};
int64_t n = 0;
int ret = 0;
char path_dst[PATH_MAX_LENGTH] = {0};
RFILE *fp_src = filestream_open(src, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
RFILE *fp_dst = filestream_open(dst, RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE);
if (!fp_src || !fp_dst)
ret = -1;
if (ret < 0)
goto close;
snprintf(path_dst, sizeof(path_dst), "%s", dst);
path_basedir(path_dst);
if (!path_is_directory(path_dst))
path_mkdir(path_dst);
while ((n = filestream_read(fp_src, buf, sizeof(buf))) > 0 && ret == 0)
{
if (filestream_write(fp_dst, buf, n) != n)
ret = -1;
}
close:
if (fp_src)
filestream_close(fp_src);
if (fp_dst)
filestream_close(fp_dst);
return ret;
}
int filestream_cmp(const char *src, const char *dst)
{
int ret = 0;
RFILE *fp_src = filestream_open(src, RETRO_VFS_FILE_ACCESS_READ,
RETRO_VFS_FILE_ACCESS_HINT_NONE);
RFILE *fp_dst = filestream_open(dst, RETRO_VFS_FILE_ACCESS_READ,
RETRO_VFS_FILE_ACCESS_HINT_NONE);
if (!fp_src || !fp_dst || filestream_get_size(fp_src) != filestream_get_size(fp_dst))
ret = -1;
if (ret >= 0)
{
char buf_src[256] = {0};
char buf_dst[256] = {0};
while ((filestream_read(fp_src, buf_src, sizeof(buf_src))) > 0 && ret == 0)
{
filestream_read(fp_dst, buf_dst, sizeof(buf_dst));
ret = memcmp(buf_src, buf_dst, sizeof(buf_src));
}
}
if (fp_src)
{
filestream_close(fp_src);
fp_src = NULL;
}
if (fp_dst)
{
filestream_close(fp_dst);
fp_dst = NULL;
}
return ret;
}
const char* filestream_get_path(RFILE *stream)
{
if (filestream_get_path_cb)
@@ -458,7 +552,7 @@ int64_t filestream_write(RFILE *stream, const void *s, int64_t len)
(libretro_vfs_implementation_file*)stream->hfile, s, len);
if (output == VFS_ERROR_RETURN_VALUE)
stream->error_flag = true;
stream->err_flag = true;
return output;
}
@@ -468,40 +562,36 @@ int filestream_putc(RFILE *stream, int c)
char c_char = (char)c;
if (!stream)
return EOF;
return filestream_write(stream, &c_char, 1) == 1
? (int)(unsigned char)c
return filestream_write(stream, &c_char, 1) == 1
? (int)(unsigned char)c
: EOF;
}
int filestream_vprintf(RFILE *stream, const char* format, va_list args)
{
static char buffer[8 * 1024];
int64_t num_chars = vsnprintf(buffer, sizeof(buffer),
int _len = vsnprintf(buffer, sizeof(buffer),
format, args);
if (num_chars < 0)
if (_len < 0)
return -1;
else if (num_chars == 0)
else if (_len == 0)
return 0;
return (int)filestream_write(stream, buffer, num_chars);
return (int)filestream_write(stream, buffer, _len);
}
int filestream_printf(RFILE *stream, const char* format, ...)
{
va_list vl;
int result;
int ret;
va_start(vl, format);
result = filestream_vprintf(stream, format, vl);
ret = filestream_vprintf(stream, format, vl);
va_end(vl);
return result;
return ret;
}
int filestream_error(RFILE *stream)
{
if (stream && stream->error_flag)
return 1;
return 0;
return (stream && stream->err_flag);
}
int filestream_close(RFILE *stream)
@@ -521,17 +611,6 @@ int filestream_close(RFILE *stream)
return output;
}
/**
* 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.
*
* Returns: non zero on success.
*/
int64_t filestream_read_file(const char *path, void **buf, int64_t *len)
{
int64_t ret = 0;
@@ -547,25 +626,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;
@@ -579,9 +653,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)
@@ -590,16 +663,6 @@ error:
return 0;
}
/**
* filestream_write_file:
* @path : path to file.
* @data : contents to write to the file.
* @size : size of the contents.
*
* Writes data to a file.
*
* Returns: true (1) on success, false (0) otherwise.
*/
bool filestream_write_file(const char *path, const void *data, int64_t size)
{
int64_t ret = 0;
@@ -608,57 +671,60 @@ 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)
char *filestream_getline(RFILE *stream)
{
char *newline_tmp = NULL;
size_t cur_size = 8;
char *newline = NULL;
char *newline_tmp = NULL;
size_t cur_size = 256;
size_t idx = 0;
int in = 0;
char *newline = (char*)malloc(9);
int in;
if (!stream || !newline)
if (!stream)
return NULL;
newline = (char*)malloc(cur_size + 1);
if (!newline)
return NULL;
in = filestream_getc(stream);
if (in == EOF)
{
if (newline)
free(newline);
free(newline);
return NULL;
}
in = filestream_getc(stream);
while (in != EOF && in != '\n')
{
newline[idx++] = (char)in;
if (idx == cur_size)
{
cur_size *= 2;
newline_tmp = (char*)realloc(newline, cur_size + 1);
cur_size *= 2;
newline_tmp = (char*)realloc(newline, cur_size + 1);
if (!newline_tmp)
{
free(newline);
return NULL;
}
newline = newline_tmp;
newline = newline_tmp;
}
newline[idx++] = in;
in = filestream_getc(stream);
in = filestream_getc(stream);
}
newline[idx] = '\0';
/* Shrink to fit if we overallocated significantly */
if (cur_size > idx + 64)
{
newline_tmp = (char*)realloc(newline, idx + 1);
if (newline_tmp)
newline = newline_tmp;
}
newline[idx] = '\0';
return newline;
}
@@ -32,31 +32,27 @@ RFILE* rfopen(const char *path, const char *mode)
unsigned int retro_mode = RETRO_VFS_FILE_ACCESS_READ;
bool position_to_end = false;
if (strstr(mode, "r"))
if (strchr(mode, 'r'))
{
retro_mode = RETRO_VFS_FILE_ACCESS_READ;
if (strstr(mode, "+"))
{
retro_mode = RETRO_VFS_FILE_ACCESS_READ_WRITE |
RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING;
}
if (strchr(mode, '+'))
retro_mode = RETRO_VFS_FILE_ACCESS_READ_WRITE
| RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING;
}
else if (strstr(mode, "w"))
else if (strchr(mode, 'w'))
{
retro_mode = RETRO_VFS_FILE_ACCESS_WRITE;
if (strstr(mode, "+"))
if (strchr(mode, '+'))
retro_mode = RETRO_VFS_FILE_ACCESS_READ_WRITE;
}
else if (strstr(mode, "a"))
else if (strchr(mode, 'a'))
{
retro_mode = RETRO_VFS_FILE_ACCESS_WRITE |
RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING;
retro_mode = RETRO_VFS_FILE_ACCESS_WRITE
| RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING;
position_to_end = true;
if (strstr(mode, "+"))
{
retro_mode = RETRO_VFS_FILE_ACCESS_READ_WRITE |
RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING;
}
if (strchr(mode, '+'))
retro_mode = RETRO_VFS_FILE_ACCESS_READ_WRITE
| RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING;
}
output = filestream_open(path, retro_mode,
@@ -69,17 +65,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,43 +105,64 @@ 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)
char *rfgets(char *s, int maxCount, RFILE* stream)
{
return filestream_gets(stream, buffer, maxCount);
if (!stream)
return NULL;
return filestream_gets(stream, s, 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;
int ret;
va_list vl;
if (!stream)
return -1;
va_start(vl, format);
result = filestream_vprintf(stream, format, vl);
ret = filestream_vprintf(stream, format, vl);
va_end(vl);
return result;
return ret;
}
int rferror(RFILE* stream)
@@ -150,10 +177,14 @@ int rfeof(RFILE* stream)
int rfscanf(RFILE * stream, const char * format, ...)
{
int result;
int ret;
va_list vl;
if (!stream)
return 0;
va_start(vl, format);
result = filestream_vscanf(stream, format, &vl);
ret = filestream_vscanf(stream, format, &vl);
va_end(vl);
return result;
return ret;
}
+340 -154
View File
@@ -24,6 +24,7 @@
#include <ctype.h>
#include <string.h>
#include <compat/strl.h>
#include <string/stdstring.h>
#include <encodings/utf.h>
@@ -47,18 +48,6 @@ const uint8_t lr_char_props[256] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* Fx */
};
char *string_init(const char *src)
{
return src ? strdup(src) : NULL;
}
void string_set(char **string, const char *src)
{
free(*string);
*string = string_init(src);
}
char *string_to_upper(char *s)
{
char *cs = (char *)s;
@@ -78,9 +67,13 @@ char *string_to_lower(char *s)
char *string_ucwords(char *s)
{
char *cs = (char *)s;
if (!s || *s == '\0')
return s;
for ( ; *cs != '\0'; cs++)
{
if (*cs == ' ')
if (*cs == ' ' && *(cs + 1) != '\0')
*(cs+1) = toupper((unsigned char)*(cs+1));
}
@@ -88,10 +81,13 @@ char *string_ucwords(char *s)
return s;
}
char *string_replace_substring(const char *in,
const char *pattern, const char *replacement)
char *string_replace_substring(
const char *in, size_t in_len,
const char *pattern, size_t pattern_len,
const char *replacement, size_t replacement_len)
{
size_t numhits, pattern_len, replacement_len, outlen;
size_t _len;
size_t numhits = 0;
const char *inat = NULL;
const char *inprev = NULL;
char *out = NULL;
@@ -102,9 +98,6 @@ char *string_replace_substring(const char *in,
if (!pattern || !replacement)
return strdup(in);
pattern_len = strlen(pattern);
replacement_len = strlen(replacement);
numhits = 0;
inat = in;
while ((inat = strstr(inat, pattern)))
@@ -113,10 +106,9 @@ char *string_replace_substring(const char *in,
numhits++;
}
outlen = strlen(in) - pattern_len*numhits + replacement_len*numhits;
out = (char *)malloc(outlen+1);
_len = in_len - pattern_len * numhits + replacement_len*numhits;
if (!out)
if (!(out = (char *)malloc(_len + 1)))
return NULL;
outat = out;
@@ -129,47 +121,53 @@ 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);
_len = (in + in_len) - inprev;
strlcpy(outat, inprev, _len + 1);
return out;
}
/* Remove leading whitespaces */
/**
* string_trim_whitespace_left:
*
* Remove leading whitespaces
**/
char *string_trim_whitespace_left(char *const s)
{
if (s && *s)
{
size_t len = strlen(s);
char *current = s;
while (*current && ISSPACE((unsigned char)*current))
{
while (*current && (*current == ' ' || *current == '\t' || *current == '\n' || *current == '\r' || *current == '\v' || *current == '\f'))
++current;
--len;
}
if (s != current)
memmove(s, current, len + 1);
{
size_t _len = strlen(current);
memmove(s, current, _len + 1);
}
}
return s;
}
/* Remove trailing whitespaces */
/**
* string_trim_whitespace_right:
*
* Remove trailing whitespaces
**/
char *string_trim_whitespace_right(char *const s)
{
if (s && *s)
{
size_t len = strlen(s);
char *current = s + len - 1;
size_t _len = strlen(s);
char *current = s + _len - 1;
while (current != s && ISSPACE((unsigned char)*current))
{
--current;
--len;
--_len;
}
current[ISSPACE((unsigned char)*current) ? 0 : 1] = '\0';
@@ -178,7 +176,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,88 +189,143 @@ 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:
* @s : pointer to destination buffer.
* @len : 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 @s and @len.
* This function assumes that all glyphs in the string
* have an on-screen pixel width similar to that of
* regular Latin characters - i.e. it will not wrap
* correctly any text containing so-called 'wide' Unicode
* characters (e.g. CJK languages, emojis, etc.).
**/
size_t word_wrap(
char *s, size_t len,
const char *src, size_t src_len,
int line_width, int wideglyph_width, unsigned max_lines)
{
char *lastspace = NULL;
char *last_space = NULL;
unsigned counter = 0;
unsigned lines = 1;
size_t src_len = strlen(src);
const char *src_end = src + src_len;
char *s_start = s;
size_t _len = len;
/* Prevent buffer overflow */
if (dst_size < src_len + 1)
return;
if (len < src_len + 1)
return 0;
/* Early return if src string length is less
* than line width */
if (src_len < line_width)
{
strcpy(dst, src);
return;
}
if (src_len < (size_t)line_width)
return strlcpy(s, src, len);
while (*src != '\0')
{
unsigned char_len;
char_len = (unsigned)(utf8skip(src, 1) - src);
unsigned char_len = (unsigned)(utf8skip(src, 1) - src);
counter++;
if (*src == ' ')
lastspace = dst; /* Remember the location of the whitespace */
last_space = s; /* Remember the location of the whitespace */
else if (*src == '\n')
{
/* If newlines embedded in the input,
* reset the index */
lines++;
counter = 0;
counter = 0;
/* Early return if remaining src string
* length is less than line width */
if (src_end - src <= line_width)
{
strcpy(dst, src);
return;
_len = len - (size_t)(s - s_start);
return strlcpy(s, src, _len);
}
}
}
while (char_len--)
*dst++ = *src++;
*s++ = *src++;
if (counter >= (unsigned)line_width)
{
counter = 0;
if (lastspace && (max_lines == 0 || lines < max_lines))
if (last_space && (max_lines == 0 || lines < max_lines))
{
/* Replace nearest (previous) whitespace
* with newline character */
*lastspace = '\n';
*last_space = '\n';
lines++;
src -= dst - lastspace - 1;
dst = lastspace + 1;
lastspace = NULL;
src -= s - last_space - 1;
s = last_space + 1;
last_space = NULL;
/* Early return if remaining src string
* length is less than line width */
if (src_end - src < line_width)
{
strcpy(dst, src);
return;
_len = len - (size_t)(s - s_start);
return strlcpy(s, src, _len);
}
}
}
}
*dst = '\0';
*s = '\0';
return 0;
}
void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src, int line_width, int wideglyph_width, unsigned max_lines)
/**
* word_wrap_wideglyph:
* @dst : pointer to destination buffer.
* @len : 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 @len.
* This function assumes that all glyphs in the string
* are:
* - EITHER 'non-wide' Unicode glyphs, with an on-screen
* pixel width similar to that of regular Latin characters
* - OR 'wide' Unicode glyphs (e.g. CJK languages, emojis, etc.)
* with an on-screen pixel width defined by @wideglyph_width
* Note that wrapping may occur in inappropriate locations
* if @src string contains 'wide' Unicode characters whose
* on-screen pixel width deviates greatly from the set
* @wideglyph_width value.
**/
size_t word_wrap_wideglyph(char *s, size_t len,
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;
char *s_start = s;
unsigned lines = 1;
/* 'line_width' means max numbers of characters per line,
* but this metric is only meaningful when dealing with
@@ -294,28 +351,24 @@ void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src, int line_w
unsigned counter_normalized = 0;
int line_width_normalized = line_width * 100;
int additional_counter_normalized = wideglyph_width - 100;
/* Early return if src string length is less
* than line width */
if (src_end - src < line_width)
{
strlcpy(dst, src, dst_size);
return;
}
return strlcpy(s, src, len);
while (*src != '\0')
{
unsigned char_len;
char_len = (unsigned)(utf8skip(src, 1) - src);
size_t remaining;
unsigned char_len = (unsigned)(utf8skip(src, 1) - src);
counter_normalized += 100;
/* Prevent buffer overflow */
if (char_len >= dst_size)
if (char_len >= len)
break;
if (*src == ' ')
lastspace = dst; /* Remember the location of the whitespace */
lastspace = s; /* Remember the location of the whitespace */
else if (*src == '\n')
{
/* If newlines embedded in the input,
@@ -327,21 +380,21 @@ void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src, int line_w
* length is less than line width */
if (src_end - src <= line_width)
{
strlcpy(dst, src, dst_size);
return;
remaining = len - (size_t)(s - s_start);
return strlcpy(s, src, remaining);
}
}
else if (char_len >= 3)
{
/* Remember the location of the first byte
* whose length as UTF-8 >= 3*/
lastwideglyph = dst;
lastwideglyph = s;
counter_normalized += additional_counter_normalized;
}
dst_size -= char_len;
len -= char_len;
while (char_len--)
*dst++ = *src++;
*s++ = *src++;
if (counter_normalized >= (unsigned)line_width_normalized)
{
@@ -349,21 +402,22 @@ void word_wrap_wideglyph(char *dst, size_t dst_size, const char *src, int line_w
if (max_lines != 0 && lines >= max_lines)
continue;
else if (lastwideglyph && (!lastspace || lastwideglyph > lastspace))
else if (lastwideglyph
&& (!lastspace || lastwideglyph > lastspace))
{
/* Insert newline character */
*lastwideglyph = '\n';
lines++;
src -= dst - lastwideglyph;
dst = lastwideglyph + 1;
lastwideglyph = NULL;
src -= s - lastwideglyph;
s = lastwideglyph + 1;
lastwideglyph = NULL;
/* Early return if remaining src string
* length is less than line width */
if (src_end - src <= line_width)
{
strlcpy(dst, src, dst_size);
return;
remaining = len - (size_t)(s - s_start);
return strlcpy(s, src, remaining);
}
}
else if (lastspace)
@@ -372,28 +426,32 @@ 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 -= s - lastspace - 1;
s = lastspace + 1;
lastspace = NULL;
/* Early return if remaining src string
* length is less than line width */
if (src_end - src < line_width)
{
strlcpy(dst, src, dst_size);
return;
remaining = len - (size_t)(s - s_start);
return strlcpy(s, src, remaining);
}
}
}
}
*dst = '\0';
*s = '\0';
return 0;
}
/* Splits string into tokens seperated by 'delim'
/**
* string_tokenize:
*
* Splits string into tokens separated 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,86 +464,94 @@ 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# */
char *str_ptr = NULL;
char *delim_ptr = NULL;
char *token = NULL;
size_t token_len = 0;
size_t _len = 0;
size_t delim_len = 0;
/* Sanity checks */
if (!str || string_is_empty(delim))
return NULL;
str_ptr = *str;
/* Note: we don't check string_is_empty() here,
* empty strings are valid */
if (!str_ptr)
if (!(str_ptr = *str))
return NULL;
/* Search for delimiter */
delim_ptr = strstr(str_ptr, delim);
delim_len = strlen(delim);
if (delim_ptr)
token_len = delim_ptr - str_ptr;
/* Search for delimiter */
if ((delim_ptr = strstr(str_ptr, delim)))
_len = delim_ptr - str_ptr;
else
token_len = strlen(str_ptr);
_len = strlen(str_ptr);
/* Allocate token string */
token = (char *)malloc((token_len + 1) * sizeof(char));
if (!token)
if (!(token = (char *)malloc((_len + 1) * sizeof(char))))
return NULL;
/* Copy token */
strlcpy(token, str_ptr, (token_len + 1) * sizeof(char));
token[token_len] = '\0';
strlcpy(token, str_ptr, (_len + 1) * sizeof(char));
token[_len] = '\0';
/* Update input string pointer */
*str = delim_ptr ? delim_ptr + strlen(delim) : NULL;
*str = delim_ptr ? delim_ptr + delim_len : NULL;
return token;
}
/* Removes every instance of character 'c' from 'str' */
void string_remove_all_chars(char *str, char c)
/**
* string_remove_all_chars:
* @s : input string (must be non-NULL, otherwise UB)
*
* Leaf function.
*
* Removes every instance of character @c from @s
*
* Returns the length of the resulting string.
**/
size_t string_remove_all_chars(char *s, char c)
{
char *read_ptr = NULL;
char *write_ptr = NULL;
if (string_is_empty(str))
return;
read_ptr = str;
write_ptr = str;
while (*read_ptr != '\0')
char *dst = s;
char *src = s;
while (*src)
{
*write_ptr = *read_ptr++;
write_ptr += (*write_ptr != c) ? 1 : 0;
/* Only write if the character is not the one to remove */
if (*src != c)
*dst++ = *src;
src++;
}
*write_ptr = '\0';
*dst = '\0';
return (size_t)(dst - s);
}
/* Replaces every instance of character 'find' in 'str'
* with character 'replace' */
void string_replace_all_chars(char *str, char find, char replace)
/**
* string_replace_all_chars:
* @s : 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 @s
* with character @replace
**/
void string_replace_all_chars(char *s, char find, char replace)
{
char *str_ptr = str;
if (string_is_empty(str))
return;
char *str_ptr = s;
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,28 +568,31 @@ 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')))
hex_str = str + 2;
if (string_is_empty(hex_str))
return 0;
/* Remove leading '0x', if present */
if ( str[0] == '0'
&& (str[1] == 'x' || str[1] == 'X'))
{
hex_str = str + 2;
if (string_is_empty(hex_str))
return 0;
}
/* Check for valid characters */
for (ptr = hex_str; *ptr != '\0'; ptr++)
@@ -534,3 +603,120 @@ 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 *s, char c)
{
for (; *s; s++)
if (ISSPACE((unsigned char)*s))
*s = 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 *s)
{
char *str_trimmed = s;
bool prev_is_space = false;
bool curr_is_space = false;
for (; *s; s++)
{
curr_is_space = ISSPACE((unsigned char)*s);
if (prev_is_space && curr_is_space)
continue;
*str_trimmed++ = *s;
prev_is_space = curr_is_space;
}
*str_trimmed = '\0';
}
/**
* string_remove_all_whitespace:
*
* Leaf function.
*
* Remove all spaces from the given string.
* Returns the length of the resulting string.
**/
size_t string_remove_all_whitespace(char *s, const char *str)
{
char *start = (char*)s;
for (; *str; str++)
if (!ISSPACE((unsigned char)*str))
*s++ = *str;
*s = '\0';
return s - start;
}
/**
* Retrieve the last occurrence 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 *s, const char *str)
{
for (; *str; str++)
if (*str > 0x1F && *str < 0x7F)
*s++ = *str;
*s = '\0';
}
-3
View File
@@ -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
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff