mirror of
https://github.com/libretro/virtualjaguar-libretro.git
synced 2026-11-07 04:36:00 -10:00
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:
1 parent
e6d240005e
commit
fa3462ae89
33 files changed
+11198
-5105
No files matched your search
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
+866
-754
File diff suppressed because it is too large.
Load diff
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -38,29 +38,99 @@ enum CodePage
|
||||
CODEPAGE_UTF8 = 65001 /* CP_UTF8 */
|
||||
};
|
||||
|
||||
/**
|
||||
* utf8_conv_utf32:
|
||||
*
|
||||
* Simple implementation. Assumes the sequence is
|
||||
* properly synchronized and terminated.
|
||||
**/
|
||||
size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
|
||||
const char *in, size_t in_size);
|
||||
|
||||
/**
|
||||
* utf16_conv_utf8:
|
||||
*
|
||||
* Leaf function.
|
||||
**/
|
||||
bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
|
||||
const uint16_t *in, size_t in_size);
|
||||
|
||||
/**
|
||||
* utf8len:
|
||||
*
|
||||
* Leaf function.
|
||||
**/
|
||||
size_t utf8len(const char *string);
|
||||
|
||||
/**
|
||||
* utf8cpy:
|
||||
*
|
||||
* Acts mostly like strlcpy.
|
||||
*
|
||||
* Copies the given number of UTF-8 characters,
|
||||
* but at most @d_len bytes.
|
||||
*
|
||||
* Always NULL terminates. Does not copy half a character.
|
||||
* @s is assumed valid UTF-8.
|
||||
* Use only if @chars is considerably less than @d_len.
|
||||
*
|
||||
* Hidden non-leaf function cost:
|
||||
* - Calls memcpy
|
||||
*
|
||||
* @return Number of bytes.
|
||||
**/
|
||||
size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars);
|
||||
|
||||
/**
|
||||
* utf8skip:
|
||||
*
|
||||
* Leaf function
|
||||
**/
|
||||
const char *utf8skip(const char *str, size_t chars);
|
||||
|
||||
/**
|
||||
* utf8_walk:
|
||||
*
|
||||
* Does not validate the input.
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* @return Returns garbage if it's not UTF-8.
|
||||
**/
|
||||
uint32_t utf8_walk(const char **string);
|
||||
|
||||
/**
|
||||
* utf16_to_char_string:
|
||||
**/
|
||||
bool utf16_to_char_string(const uint16_t *in, char *s, size_t len);
|
||||
|
||||
char* utf8_to_local_string_alloc(const char *str);
|
||||
/**
|
||||
* utf8_to_local_string_alloc:
|
||||
*
|
||||
* @return Returned pointer MUST be freed by the caller if non-NULL.
|
||||
**/
|
||||
char *utf8_to_local_string_alloc(const char *str);
|
||||
|
||||
char* local_to_utf8_string_alloc(const char *str);
|
||||
/**
|
||||
* local_to_utf8_string_alloc:
|
||||
*
|
||||
* @return Returned pointer MUST be freed by the caller if non-NULL.
|
||||
**/
|
||||
char *local_to_utf8_string_alloc(const char *str);
|
||||
|
||||
wchar_t* utf8_to_utf16_string_alloc(const char *str);
|
||||
/**
|
||||
* utf8_to_utf16_string_alloc:
|
||||
*
|
||||
* @return Returned pointer MUST be freed by the caller if non-NULL.
|
||||
**/
|
||||
wchar_t *utf8_to_utf16_string_alloc(const char *str);
|
||||
|
||||
char* utf16_to_utf8_string_alloc(const wchar_t *str);
|
||||
/**
|
||||
* utf16_to_utf8_string_alloc:
|
||||
*
|
||||
* @return Returned pointer MUST be freed by the caller if non-NULL.
|
||||
**/
|
||||
char *utf16_to_utf8_string_alloc(const wchar_t *str);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
|
||||
@@ -51,6 +51,28 @@ enum
|
||||
RARCH_FILE_UNSUPPORTED
|
||||
};
|
||||
|
||||
struct path_linked_list
|
||||
{
|
||||
char *path;
|
||||
struct path_linked_list *next;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new linked list with one item in it
|
||||
* The path on this item will be set to NULL
|
||||
**/
|
||||
struct path_linked_list* path_linked_list_new(void);
|
||||
|
||||
/* Free the entire linked list */
|
||||
void path_linked_list_free(struct path_linked_list *in_path_linked_list);
|
||||
|
||||
/**
|
||||
* Add a node to the linked list with this path
|
||||
* If the first node's path if it's not yet set,
|
||||
* set this instead
|
||||
**/
|
||||
void path_linked_list_add_path(struct path_linked_list *in_path_linked_list, char *path);
|
||||
|
||||
/**
|
||||
* path_is_compressed_file:
|
||||
* @path : path
|
||||
@@ -81,12 +103,12 @@ bool path_is_compressed_file(const char *path);
|
||||
* path_get_archive_delim:
|
||||
* @path : path
|
||||
*
|
||||
* Gets delimiter of an archive file. Only the first '#'
|
||||
* Find delimiter of an archive file. Only the first '#'
|
||||
* after a compression extension is considered.
|
||||
*
|
||||
* Returns: pointer to the delimiter in the path if it contains
|
||||
* a compressed file, otherwise NULL.
|
||||
*/
|
||||
* @return pointer to the delimiter in the path if it contains
|
||||
* a path inside a compressed file, otherwise NULL.
|
||||
**/
|
||||
const char *path_get_archive_delim(const char *path);
|
||||
|
||||
/**
|
||||
@@ -96,10 +118,28 @@ const char *path_get_archive_delim(const char *path);
|
||||
* Gets extension of file. Only '.'s
|
||||
* after the last slash are considered.
|
||||
*
|
||||
* Returns: extension part from the path.
|
||||
*/
|
||||
* Hidden non-leaf function cost:
|
||||
* - calls string_is_empty()
|
||||
* - calls strrchr
|
||||
*
|
||||
* @return extension part from the path.
|
||||
**/
|
||||
const char *path_get_extension(const char *path);
|
||||
|
||||
/**
|
||||
* path_get_extension_mutable:
|
||||
* @path : path
|
||||
*
|
||||
* Specialized version of path_get_extension(). Return
|
||||
* value is mutable.
|
||||
*
|
||||
* Gets extension of file. Only '.'s
|
||||
* after the last slash are considered.
|
||||
*
|
||||
* @return extension part from the path.
|
||||
**/
|
||||
char *path_get_extension_mutable(const char *path);
|
||||
|
||||
/**
|
||||
* path_remove_extension:
|
||||
* @path : path
|
||||
@@ -108,7 +148,10 @@ const char *path_get_extension(const char *path);
|
||||
* text after and including the last '.'.
|
||||
* Only '.'s after the last slash are considered.
|
||||
*
|
||||
* Returns:
|
||||
* Hidden non-leaf function cost:
|
||||
* - calls strrchr
|
||||
*
|
||||
* @return
|
||||
* 1) If path has an extension, returns path with the
|
||||
* extension removed.
|
||||
* 2) If there is no extension, returns NULL.
|
||||
@@ -122,9 +165,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);
|
||||
|
||||
+5996
-1949
File diff suppressed because it is too large.
Load diff
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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';
|
||||
}
|
||||
@@ -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
Reference in new issue
Block a user