Updated vfs.h to match current libretro common (#227)

* Updated winrt checks

* Updated to current libretro-common
This commit is contained in:
Alberto Fustinoni 2024-06-29 07:43:50 +09:00 committed by GitHub
parent ced21d6859
commit 9ebf08571e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 3640 additions and 2094 deletions

View File

@ -1346,9 +1346,9 @@ struct string_list* cdrom_get_available_drives(void)
if (string_starts_with_size(dir_list->elems[i].data, "/dev/sg", if (string_starts_with_size(dir_list->elems[i].data, "/dev/sg",
STRLEN_CONST("/dev/sg"))) STRLEN_CONST("/dev/sg")))
{ {
char drive_string[33];
libretro_vfs_implementation_file *stream; libretro_vfs_implementation_file *stream;
char drive_model[32] = {0}; char drive_model[32] = {0};
char drive_string[33] = {0};
union string_list_elem_attr attr = {0}; union string_list_elem_attr attr = {0};
int dev_index = 0; int dev_index = 0;
RFILE *file = filestream_open( RFILE *file = filestream_open(
@ -1380,9 +1380,9 @@ struct string_list* cdrom_get_available_drives(void)
attr.i = dev_index; attr.i = dev_index;
if (!string_is_empty(drive_model)) if (!string_is_empty(drive_model))
strlcat(drive_string, drive_model, sizeof(drive_string)); strlcpy(drive_string, drive_model, sizeof(drive_string));
else else
strlcat(drive_string, "Unknown Drive", sizeof(drive_string)); strlcpy(drive_string, "Unknown Drive", sizeof(drive_string));
string_list_append(list, drive_string, attr); string_list_append(list, drive_string, attr);
} }
@ -1404,7 +1404,7 @@ struct string_list* cdrom_get_available_drives(void)
if (string_split_noalloc(&mods, buf, "\n")) if (string_split_noalloc(&mods, buf, "\n"))
{ {
for (i = 0; i < mods.size; i++) for (i = 0; i < (int)mods.size; i++)
{ {
if (strcasestr(mods.elems[i].data, "sg ")) if (strcasestr(mods.elems[i].data, "sg "))
{ {
@ -1416,6 +1416,7 @@ struct string_list* cdrom_get_available_drives(void)
} }
} }
string_list_deinitialize(&mods); string_list_deinitialize(&mods);
free(buf);
#ifdef CDROM_DEBUG #ifdef CDROM_DEBUG
if (found) if (found)
@ -1445,13 +1446,13 @@ struct string_list* cdrom_get_available_drives(void)
DWORD drive_mask = GetLogicalDrives(); DWORD drive_mask = GetLogicalDrives();
int i; int i;
for (i = 0; i < sizeof(DWORD) * 8; i++) for (i = 0; i < (int)(sizeof(DWORD) * 8); i++)
{ {
char path[] = {"a:\\"}; char path[] = {"a:\\"};
char cdrom_path[] = {"cdrom://a:/drive-track01.bin"}; char cdrom_path[] = {"cdrom://a:/drive-track01.bin"};
path[0] += i; path[0] += i;
cdrom_path[8] += i; cdrom_path[8] += i;
/* this drive letter doesn't exist */ /* this drive letter doesn't exist */
if (!(drive_mask & (1 << i))) if (!(drive_mask & (1 << i)))
@ -1459,15 +1460,14 @@ struct string_list* cdrom_get_available_drives(void)
if (GetDriveType(path) != DRIVE_CDROM) if (GetDriveType(path) != DRIVE_CDROM)
continue; continue;
else
{ {
char drive_model[32] = {0}; char drive_string[33];
char drive_string[33] = {0}; libretro_vfs_implementation_file *stream;
bool is_cdrom = false;
char drive_model[32] = {0};
union string_list_elem_attr attr = {0}; union string_list_elem_attr attr = {0};
RFILE *file = filestream_open(cdrom_path, RETRO_VFS_FILE_ACCESS_READ, 0); RFILE *file = filestream_open(cdrom_path, RETRO_VFS_FILE_ACCESS_READ, 0);
libretro_vfs_implementation_file *stream;
bool is_cdrom = false;
if (!file) if (!file)
continue; continue;
@ -1481,9 +1481,9 @@ struct string_list* cdrom_get_available_drives(void)
attr.i = path[0]; attr.i = path[0];
if (!string_is_empty(drive_model)) if (!string_is_empty(drive_model))
strlcat(drive_string, drive_model, sizeof(drive_string)); strlcpy(drive_string, drive_model, sizeof(drive_string));
else else
strlcat(drive_string, "Unknown Drive", sizeof(drive_string)); strlcpy(drive_string, "Unknown Drive", sizeof(drive_string));
string_list_append(list, drive_string, attr); string_list_append(list, drive_string, attr);
} }
@ -1523,9 +1523,7 @@ bool cdrom_drive_has_media(const char drive)
if (file) if (file)
{ {
libretro_vfs_implementation_file *stream = filestream_get_vfs_handle(file); libretro_vfs_implementation_file *stream = filestream_get_vfs_handle(file);
bool has_media = false; bool has_media = cdrom_is_media_inserted(stream);
has_media = cdrom_is_media_inserted(stream);
filestream_close(file); filestream_close(file);
@ -1537,14 +1535,14 @@ bool cdrom_drive_has_media(const char drive)
bool cdrom_set_read_cache(libretro_vfs_implementation_file *stream, bool enabled) bool cdrom_set_read_cache(libretro_vfs_implementation_file *stream, bool enabled)
{ {
int i;
/* MMC Command: MODE SENSE (10) and MODE SELECT (10) */ /* MMC Command: MODE SENSE (10) and MODE SELECT (10) */
unsigned char cdb_sense_changeable[] = {0x5A, 0, 0x48, 0, 0, 0, 0, 0, 0x14, 0}; unsigned char cdb_sense_changeable[] = {0x5A, 0, 0x48, 0, 0, 0, 0, 0, 0x14, 0};
unsigned char cdb_sense[] = {0x5A, 0, 0x8, 0, 0, 0, 0, 0, 0x14, 0}; unsigned char cdb_sense[] = {0x5A, 0, 0x8, 0, 0, 0, 0, 0, 0x14, 0};
unsigned char cdb_select[] = {0x55, 0x10, 0, 0, 0, 0, 0, 0, 0x14, 0}; unsigned char cdb_select[] = {0x55, 0x10, 0, 0, 0, 0, 0, 0, 0x14, 0};
unsigned char buf[20] = {0}; unsigned char buf[20] = {0};
int rv, i; int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf),
cdb_sense_changeable, sizeof(cdb_sense_changeable), 0);
rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb_sense_changeable, sizeof(cdb_sense_changeable), 0);
#ifdef CDROM_DEBUG #ifdef CDROM_DEBUG
printf("[CDROM] mode sense changeable status code %d\n", rv); printf("[CDROM] mode sense changeable status code %d\n", rv);
@ -1580,9 +1578,7 @@ bool cdrom_set_read_cache(libretro_vfs_implementation_file *stream, bool enabled
printf("Mode sense data for caching mode page: "); printf("Mode sense data for caching mode page: ");
for (i = 0; i < (int)sizeof(buf); i++) for (i = 0; i < (int)sizeof(buf); i++)
{
printf("%02X ", buf[i]); printf("%02X ", buf[i]);
}
printf("\n"); printf("\n");
fflush(stdout); fflush(stdout);
@ -1595,7 +1591,7 @@ bool cdrom_set_read_cache(libretro_vfs_implementation_file *stream, bool enabled
if (enabled) if (enabled)
buf[10] &= ~1; buf[10] &= ~1;
else else
buf[10] |= 1; buf[10] |= 1;
rv = cdrom_send_command(stream, DIRECTION_OUT, buf, sizeof(buf), cdb_select, sizeof(cdb_select), 0); rv = cdrom_send_command(stream, DIRECTION_OUT, buf, sizeof(buf), cdb_select, sizeof(cdb_select), 0);
@ -1644,9 +1640,7 @@ bool cdrom_get_timeouts(libretro_vfs_implementation_file *stream, cdrom_group_ti
printf("Mode sense data for timeout groups: "); printf("Mode sense data for timeout groups: ");
for (i = 0; i < (int)sizeof(buf); i++) for (i = 0; i < (int)sizeof(buf); i++)
{
printf("%02X ", buf[i]); printf("%02X ", buf[i]);
}
printf("\n"); printf("\n");
@ -1668,8 +1662,8 @@ bool cdrom_get_timeouts(libretro_vfs_implementation_file *stream, cdrom_group_ti
bool cdrom_has_atip(libretro_vfs_implementation_file *stream) bool cdrom_has_atip(libretro_vfs_implementation_file *stream)
{ {
/* MMC Command: READ TOC/PMA/ATIP */ /* MMC Command: READ TOC/PMA/ATIP */
unsigned char cdb[] = {0x43, 0x2, 0x4, 0, 0, 0, 0, 0x9, 0x30, 0}; unsigned char cdb[] = {0x43, 0x2, 0x4, 0, 0, 0, 0, 0x9, 0x30, 0};
unsigned char buf[32] = {0}; unsigned char buf[32] = {0};
unsigned short atip_len = 0; unsigned short atip_len = 0;
int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0); int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
@ -1679,7 +1673,10 @@ bool cdrom_has_atip(libretro_vfs_implementation_file *stream)
atip_len = buf[0] << 8 | buf[1]; atip_len = buf[0] << 8 | buf[1];
#ifdef CDROM_DEBUG #ifdef CDROM_DEBUG
printf("ATIP Length %d, Disc Type %d, Disc Sub-Type %d\n", atip_len, (buf[6] >> 6) & 0x1, ((buf[6] >> 5) & 0x1) << 2 | ((buf[6] >> 4) & 0x1) << 1 | ((buf[6] >> 3) & 0x1) << 0); printf("ATIP Length %d, Disc Type %d, Disc Sub-Type %d\n",
atip_len,
(buf[6] >> 6) & 0x1,
((buf[6] >> 5) & 0x1) << 2 | ((buf[6] >> 4) & 0x1) << 1 | ((buf[6] >> 3) & 0x1) << 0);
#endif #endif
if (atip_len < 5) if (atip_len < 5)
@ -1690,30 +1687,23 @@ bool cdrom_has_atip(libretro_vfs_implementation_file *stream)
void cdrom_device_fillpath(char *path, size_t len, char drive, unsigned char track, bool is_cue) void cdrom_device_fillpath(char *path, size_t len, char drive, unsigned char track, bool is_cue)
{ {
size_t pos = 0;
if (!path || len == 0) if (!path || len == 0)
return; return;
if (is_cue) if (is_cue)
{ {
#ifdef _WIN32 #ifdef _WIN32
pos = strlcpy(path, "cdrom://", len); size_t pos = strlcpy(path, "cdrom://", len);
if (len > pos) if (len > pos)
path[pos++] = drive; path[pos++] = drive;
pos = strlcat(path, ":/drive.cue", len); pos = strlcat(path, ":/drive.cue", len);
#else #else
#ifdef __linux__ #ifdef __linux__
pos = strlcpy(path, "cdrom://drive", len); size_t pos = strlcpy(path, "cdrom://drive", len);
if (len > pos + 1) if (len > pos + 1)
{ {
path[pos++] = drive; path[pos++] = drive;
path[pos] = '\0'; path[pos] = '\0';
} }
pos = strlcat(path, ".cue", len); pos = strlcat(path, ".cue", len);
#endif #endif
#endif #endif
@ -1721,22 +1711,18 @@ void cdrom_device_fillpath(char *path, size_t len, char drive, unsigned char tra
else else
{ {
#ifdef _WIN32 #ifdef _WIN32
pos = strlcpy(path, "cdrom://", len); size_t pos = strlcpy(path, "cdrom://", len);
if (len > pos + 1) if (len > pos + 1)
{ {
path[pos++] = drive; path[pos++] = drive;
path[pos] = '\0'; path[pos] = '\0';
} }
pos += snprintf(path + pos, len - pos, ":/drive-track%02d.bin", track); pos += snprintf(path + pos, len - pos, ":/drive-track%02d.bin", track);
#else #else
#ifdef __linux__ #ifdef __linux__
pos = strlcpy(path, "cdrom://drive", len); size_t pos = strlcpy(path, "cdrom://drive", len);
if (len > pos) if (len > pos)
path[pos++] = drive; path[pos++] = drive;
pos += snprintf(path + pos, len - pos, "-track%02d.bin", track); pos += snprintf(path + pos, len - pos, "-track%02d.bin", track);
#endif #endif
#endif #endif

View File

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

View File

@ -37,22 +37,28 @@
void *fopen_utf8(const char * filename, const char * mode) void *fopen_utf8(const char * filename, const char * mode)
{ {
#if defined(LEGACY_WIN32) #if defined(LEGACY_WIN32)
FILE *ret = NULL;
char * filename_local = utf8_to_local_string_alloc(filename); char * filename_local = utf8_to_local_string_alloc(filename);
if (!filename_local)
return NULL;
ret = fopen(filename_local, mode);
if (filename_local) if (filename_local)
{
FILE *ret = fopen(filename_local, mode);
free(filename_local); free(filename_local);
return ret; return ret;
}
#else #else
wchar_t * filename_w = utf8_to_utf16_string_alloc(filename); wchar_t * filename_w = utf8_to_utf16_string_alloc(filename);
wchar_t * mode_w = utf8_to_utf16_string_alloc(mode); if (filename_w)
FILE* ret = _wfopen(filename_w, mode_w); {
free(filename_w); FILE *ret = NULL;
free(mode_w); wchar_t *mode_w = utf8_to_utf16_string_alloc(mode);
return ret; if (mode_w)
{
ret = _wfopen(filename_w, mode_w);
free(mode_w);
}
free(filename_w);
return ret;
}
#endif #endif
return NULL;
} }
#endif #endif

View File

@ -23,7 +23,6 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <encodings/crc32.h> #include <encodings/crc32.h>
#include <streams/file_stream.h>
#include <stdlib.h> #include <stdlib.h>
static const uint32_t crc32_table[256] = { static const uint32_t crc32_table[256] = {
@ -90,51 +89,3 @@ uint32_t encoding_crc32(uint32_t crc, const uint8_t *buf, size_t len)
return crc ^ 0xffffffff; return crc ^ 0xffffffff;
} }
#define CRC32_BUFFER_SIZE 1048576
#define CRC32_MAX_MB 64
/**
* Calculate a CRC32 from the first part of the given file.
* "first part" being the first (CRC32_BUFFER_SIZE * CRC32_MAX_MB)
* bytes.
*
* Returns: the crc32, or 0 if there was an error.
*/
uint32_t file_crc32(uint32_t crc, const char *path)
{
unsigned i;
RFILE *file = NULL;
unsigned char *buf = NULL;
if (!path)
return 0;
file = filestream_open(path, RETRO_VFS_FILE_ACCESS_READ, 0);
if (!file)
goto error;
buf = (unsigned char*)malloc(CRC32_BUFFER_SIZE);
if (!buf)
goto error;
for (i = 0; i < CRC32_MAX_MB; i++)
{
int64_t nread = filestream_read(file, buf, CRC32_BUFFER_SIZE);
if (nread < 0)
goto error;
crc = encoding_crc32(crc, buf, (size_t)nread);
if (filestream_eof(file))
break;
}
free(buf);
filestream_close(file);
return crc;
error:
if (buf)
free(buf);
if (file)
filestream_close(file);
return 0;
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -63,20 +63,34 @@ static INLINE int compat_ctz(unsigned x)
return __builtin_ctz(x); return __builtin_ctz(x);
#elif _MSC_VER >= 1400 && !defined(_XBOX) && !defined(__WINRT__) #elif _MSC_VER >= 1400 && !defined(_XBOX) && !defined(__WINRT__)
unsigned long r = 0; unsigned long r = 0;
_BitScanReverse((unsigned long*)&r, x); _BitScanForward((unsigned long*)&r, x);
return (int)r; return (int)r;
#else #else
/* Only checks at nibble granularity, int count = 0;
* because that's what we need. */ if (!(x & 0xffff))
if (x & 0x000f) {
return 0; x >>= 16;
if (x & 0x00f0) count |= 16;
return 4; }
if (x & 0x0f00) if (!(x & 0xff))
return 8; {
if (x & 0xf000) x >>= 8;
return 12; count |= 8;
return 16; }
if (!(x & 0xf))
{
x >>= 4;
count |= 4;
}
if (!(x & 0x3))
{
x >>= 2;
count |= 2;
}
if (!(x & 0x1))
count |= 1;
return count;
#endif #endif
} }

View File

@ -31,7 +31,6 @@
Jean-loup Gailly Mark Adler Jean-loup Gailly Mark Adler
jloup@gzip.org madler@alumni.caltech.edu jloup@gzip.org madler@alumni.caltech.edu
The data format used by the zlib library is described by RFCs (Request for The data format used by the zlib library is described by RFCs (Request for
Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
(zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
@ -220,7 +219,6 @@ typedef gz_header FAR *gz_headerp;
#define zlib_version zlibVersion() #define zlib_version zlibVersion()
/* for compatibility with versions < 1.0.2 */ /* for compatibility with versions < 1.0.2 */
/* basic functions */ /* basic functions */
const char * zlibVersion (void); const char * zlibVersion (void);
@ -252,7 +250,6 @@ typedef gz_header FAR *gz_headerp;
this will be done by deflate(). this will be done by deflate().
*/ */
int deflate (z_streamp strm, int flush); int deflate (z_streamp strm, int flush);
/* /*
deflate compresses as much data as possible, and stops when the input deflate compresses as much data as possible, and stops when the input
@ -359,7 +356,6 @@ typedef gz_header FAR *gz_headerp;
space to continue compressing. space to continue compressing.
*/ */
int deflateEnd (z_streamp strm); int deflateEnd (z_streamp strm);
/* /*
All dynamically allocated data structures for this stream are freed. All dynamically allocated data structures for this stream are freed.
@ -373,7 +369,6 @@ typedef gz_header FAR *gz_headerp;
deallocated). deallocated).
*/ */
/* /*
int inflateInit (z_streamp strm); int inflateInit (z_streamp strm);
@ -398,7 +393,6 @@ typedef gz_header FAR *gz_headerp;
until inflate() is called. until inflate() is called.
*/ */
int inflate (z_streamp strm, int flush); int inflate (z_streamp strm, int flush);
/* /*
inflate decompresses as much data as possible, and stops when the input inflate decompresses as much data as possible, and stops when the input
@ -514,7 +508,6 @@ typedef gz_header FAR *gz_headerp;
recovery of the data is desired. recovery of the data is desired.
*/ */
int inflateEnd (z_streamp strm); int inflateEnd (z_streamp strm);
/* /*
All dynamically allocated data structures for this stream are freed. All dynamically allocated data structures for this stream are freed.
@ -526,7 +519,6 @@ typedef gz_header FAR *gz_headerp;
static string (which must not be deallocated). static string (which must not be deallocated).
*/ */
/* Advanced functions */ /* Advanced functions */
/* /*
@ -1204,8 +1196,8 @@ typedef int (*out_func) (void FAR *, unsigned char FAR *, unsigned);
compress() or compress2() call to allocate the destination buffer. compress() or compress2() call to allocate the destination buffer.
*/ */
int uncompress (unsigned char *dest, uint32_t *destLen, int uncompress (Bytef *dest, uLongf *destLen,
const unsigned char *source, uint32_t sourceLen); const Bytef *source, uLongf sourceLen);
/* /*
Decompresses the source buffer into the destination buffer. sourceLen is Decompresses the source buffer into the destination buffer. sourceLen is
the byte length of the source buffer. Upon entry, destLen is the total size the byte length of the source buffer. Upon entry, destLen is the total size
@ -1634,7 +1626,6 @@ uint32_t adler32 (uint32_t adler, const uint8_t *buf, size_t len);
len2. len2.
*/ */
/* various hacks, don't look :) */ /* various hacks, don't look :) */
/* deflateInit and inflateInit are macros to allow checking the zlib version /* deflateInit and inflateInit are macros to allow checking the zlib version
@ -1751,8 +1742,9 @@ uint32_t adler32 (uint32_t adler, const uint8_t *buf, size_t len);
const char * zError (int); const char * zError (int);
int inflateSyncPoint (z_streamp); int inflateSyncPoint (z_streamp);
const uint32_t * get_crc_table(void); const uint32_t * get_crc_table(void);
int inflateUndermine (z_streamp, int); int inflateUndermine (z_streamp, int);
int inflateValidate (z_streamp, int);
int inflateResetKeep (z_streamp); int inflateResetKeep (z_streamp);
int deflateResetKeep (z_streamp); int deflateResetKeep (z_streamp);
#if defined(_WIN32) && !defined(Z_SOLO) #if defined(_WIN32) && !defined(Z_SOLO)

View File

@ -109,6 +109,8 @@ extern char z_errmsg[10][21]; /* indexed by 2-zlib_error */
#if defined(VAXC) || defined(VMS) #if defined(VAXC) || defined(VMS)
# define OS_CODE 0x02 # define OS_CODE 0x02
# define F_OPEN(name, mode) \
fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512")
#endif #endif
#if defined(ATARI) || defined(atarist) #if defined(ATARI) || defined(atarist)
@ -184,7 +186,11 @@ extern char z_errmsg[10][21]; /* indexed by 2-zlib_error */
# define OS_CODE 0x03 /* assume Unix */ # define OS_CODE 0x03 /* assume Unix */
#endif #endif
/* functions */ #ifndef F_OPEN
# define F_OPEN(name, mode) fopen((name), (mode))
#endif
/* functions */
#if defined(pyr) || defined(Z_SOLO) #if defined(pyr) || defined(Z_SOLO)
# define NO_MEMCPY # define NO_MEMCPY

View File

@ -31,7 +31,6 @@
RETRO_BEGIN_DECLS RETRO_BEGIN_DECLS
uint32_t encoding_crc32(uint32_t crc, const uint8_t *buf, size_t len); uint32_t encoding_crc32(uint32_t crc, const uint8_t *buf, size_t len);
uint32_t file_crc32(uint32_t crc, const char *path);
RETRO_END_DECLS RETRO_END_DECLS

View File

@ -38,29 +38,99 @@ enum CodePage
CODEPAGE_UTF8 = 65001 /* CP_UTF8 */ CODEPAGE_UTF8 = 65001 /* CP_UTF8 */
}; };
/**
* utf8_conv_utf32:
*
* Simple implementation. Assumes the sequence is
* properly synchronized and terminated.
**/
size_t utf8_conv_utf32(uint32_t *out, size_t out_chars, size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
const char *in, size_t in_size); const char *in, size_t in_size);
/**
* utf16_conv_utf8:
*
* Leaf function.
**/
bool utf16_conv_utf8(uint8_t *out, size_t *out_chars, bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
const uint16_t *in, size_t in_size); const uint16_t *in, size_t in_size);
/**
* utf8len:
*
* Leaf function.
**/
size_t utf8len(const char *string); size_t utf8len(const char *string);
/**
* utf8cpy:
*
* Acts mostly like strlcpy.
*
* Copies the given number of UTF-8 characters,
* but at most @d_len bytes.
*
* Always NULL terminates. Does not copy half a character.
* @s is assumed valid UTF-8.
* Use only if @chars is considerably less than @d_len.
*
* Hidden non-leaf function cost:
* - Calls memcpy
*
* @return Number of bytes.
**/
size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars); size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars);
/**
* utf8skip:
*
* Leaf function
**/
const char *utf8skip(const char *str, size_t chars); const char *utf8skip(const char *str, size_t chars);
/**
* utf8_walk:
*
* Does not validate the input.
*
* Leaf function.
*
* @return Returns garbage if it's not UTF-8.
**/
uint32_t utf8_walk(const char **string); uint32_t utf8_walk(const char **string);
/**
* utf16_to_char_string:
**/
bool utf16_to_char_string(const uint16_t *in, char *s, size_t len); bool utf16_to_char_string(const uint16_t *in, char *s, size_t len);
char* utf8_to_local_string_alloc(const char *str); /**
* utf8_to_local_string_alloc:
*
* @return Returned pointer MUST be freed by the caller if non-NULL.
**/
char *utf8_to_local_string_alloc(const char *str);
char* local_to_utf8_string_alloc(const char *str); /**
* local_to_utf8_string_alloc:
*
* @return Returned pointer MUST be freed by the caller if non-NULL.
**/
char *local_to_utf8_string_alloc(const char *str);
wchar_t* utf8_to_utf16_string_alloc(const char *str); /**
* utf8_to_utf16_string_alloc:
*
* @return Returned pointer MUST be freed by the caller if non-NULL.
**/
wchar_t *utf8_to_utf16_string_alloc(const char *str);
char* utf16_to_utf8_string_alloc(const wchar_t *str); /**
* utf16_to_utf8_string_alloc:
*
* @return Returned pointer MUST be freed by the caller if non-NULL.
**/
char *utf16_to_utf8_string_alloc(const wchar_t *str);
RETRO_END_DECLS RETRO_END_DECLS

View File

@ -51,6 +51,28 @@ enum
RARCH_FILE_UNSUPPORTED RARCH_FILE_UNSUPPORTED
}; };
struct path_linked_list
{
char *path;
struct path_linked_list *next;
};
/**
* Create a new linked list with one item in it
* The path on this item will be set to NULL
**/
struct path_linked_list* path_linked_list_new(void);
/* Free the entire linked list */
void path_linked_list_free(struct path_linked_list *in_path_linked_list);
/**
* Add a node to the linked list with this path
* If the first node's path if it's not yet set,
* set this instead
**/
void path_linked_list_add_path(struct path_linked_list *in_path_linked_list, char *path);
/** /**
* path_is_compressed_file: * path_is_compressed_file:
* @path : path * @path : path
@ -81,12 +103,12 @@ bool path_is_compressed_file(const char *path);
* path_get_archive_delim: * path_get_archive_delim:
* @path : path * @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. * after a compression extension is considered.
* *
* Returns: pointer to the delimiter in the path if it contains * @return pointer to the delimiter in the path if it contains
* a compressed file, otherwise NULL. * a path inside a compressed file, otherwise NULL.
*/ **/
const char *path_get_archive_delim(const char *path); 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 * Gets extension of file. Only '.'s
* after the last slash are considered. * 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); const char *path_get_extension(const char *path);
/**
* path_get_extension_mutable:
* @path : path
*
* Specialized version of path_get_extension(). Return
* value is mutable.
*
* Gets extension of file. Only '.'s
* after the last slash are considered.
*
* @return extension part from the path.
**/
char *path_get_extension_mutable(const char *path);
/** /**
* path_remove_extension: * path_remove_extension:
* @path : path * @path : path
@ -108,7 +148,10 @@ const char *path_get_extension(const char *path);
* text after and including the last '.'. * text after and including the last '.'.
* Only '.'s after the last slash are considered. * Only '.'s after the last slash are considered.
* *
* Returns: * Hidden non-leaf function cost:
* - calls strrchr
*
* @return
* 1) If path has an extension, returns path with the * 1) If path has an extension, returns path with the
* extension removed. * extension removed.
* 2) If there is no extension, returns NULL. * 2) If there is no extension, returns NULL.
@ -122,10 +165,28 @@ char *path_remove_extension(char *path);
* *
* Get basename from @path. * Get basename from @path.
* *
* Returns: basename from path. * Hidden non-leaf function cost:
* - Calls path_get_archive_delim()
* - can call find_last_slash() once if it returns NULL
*
* @return basename from path.
**/ **/
const char *path_basename(const char *path); const char *path_basename(const char *path);
/**
* path_basename_nocompression:
* @path : path
*
* Specialized version of path_basename().
* Get basename from @path.
*
* Hidden non-leaf function cost:
* - Calls find_last_slash()
*
* @return basename from path.
**/
const char *path_basename_nocompression(const char *path);
/** /**
* path_basedir: * path_basedir:
* @path : path * @path : path
@ -138,12 +199,13 @@ void path_basedir(char *path);
/** /**
* path_parent_dir: * path_parent_dir:
* @path : path * @path : path
* @len : length of @path
* *
* Extracts parent directory by mutating path. * Extracts parent directory by mutating path.
* Assumes that path is a directory. Keeps trailing '/'. * Assumes that path is a directory. Keeps trailing '/'.
* If the path was already at the root directory, returns empty string * If the path was already at the root directory, returns empty string
**/ **/
void path_parent_dir(char *path); void path_parent_dir(char *path, size_t len);
/** /**
* path_resolve_realpath: * path_resolve_realpath:
@ -155,7 +217,7 @@ void path_parent_dir(char *path);
* *
* Relative paths are rebased on the current working dir. * Relative paths are rebased on the current working dir.
* *
* Returns: @buf if successful, NULL otherwise. * @return @buf if successful, NULL otherwise.
* Note: Not implemented on consoles * Note: Not implemented on consoles
* Note: Symlinks are only resolved on Unix-likes * Note: Symlinks are only resolved on Unix-likes
* Note: The current working dir might not be what you expect, * Note: The current working dir might not be what you expect,
@ -177,8 +239,11 @@ char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks);
* Both @path and @base are assumed to be absolute paths without "." or "..". * Both @path and @base are assumed to be absolute paths without "." or "..".
* *
* E.g. path /a/b/e/f.cgp with base /a/b/c/d/ turns into ../../e/f.cgp * E.g. path /a/b/e/f.cgp with base /a/b/c/d/ turns into ../../e/f.cgp
*
* @return Length of the string copied into @out
**/ **/
size_t path_relative_to(char *out, const char *path, const char *base, size_t size); size_t path_relative_to(char *out, const char *path, const char *base,
size_t size);
/** /**
* path_is_absolute: * path_is_absolute:
@ -186,7 +251,7 @@ size_t path_relative_to(char *out, const char *path, const char *base, size_t si
* *
* Checks if @path is an absolute path or a relative path. * 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); bool path_is_absolute(const char *path);
@ -210,8 +275,15 @@ bool path_is_absolute(const char *path);
* out_path = "/foo/bar/baz/boo.asm" * out_path = "/foo/bar/baz/boo.asm"
* E.g.: in_path = "/foo/bar/baz/boo.c", replace = "" => * E.g.: in_path = "/foo/bar/baz/boo.c", replace = "" =>
* out_path = "/foo/bar/baz/boo" * out_path = "/foo/bar/baz/boo"
*
* Hidden non-leaf function cost:
* - calls strlcpy 2x
* - calls strrchr
* - calls strlcat
*
* @return Length of the string copied into @out
*/ */
void fill_pathname(char *out_path, const char *in_path, size_t fill_pathname(char *out_path, const char *in_path,
const char *replace, size_t size); const char *replace, size_t size);
/** /**
@ -225,6 +297,12 @@ void fill_pathname(char *out_path, const char *in_path,
* *
* E.g.: * E.g.:
* out_filename = "RetroArch-{month}{day}-{Hours}{Minutes}.{@ext}" * out_filename = "RetroArch-{month}{day}-{Hours}{Minutes}.{@ext}"
*
* Hidden non-leaf function cost:
* - Calls rtime_localtime()
* - Calls strftime
* - Calls strlcat
*
**/ **/
size_t fill_dated_filename(char *out_filename, size_t fill_dated_filename(char *out_filename,
const char *ext, size_t size); const char *ext, size_t size);
@ -241,34 +319,33 @@ size_t fill_dated_filename(char *out_filename,
* *
* E.g.: * E.g.:
* out_filename = "RetroArch-{year}{month}{day}-{Hour}{Minute}{Second}.{@ext}" * out_filename = "RetroArch-{year}{month}{day}-{Hour}{Minute}{Second}.{@ext}"
*
* Hidden non-leaf function cost:
* - Calls time
* - Calls rtime_localtime()
* - Calls strlcpy 2x
* - Calls string_is_empty()
* - Calls strftime
* - Calls strlcat
*
* @return Length of the string copied into @out_path
**/ **/
void fill_str_dated_filename(char *out_filename, size_t fill_str_dated_filename(char *out_filename,
const char *in_str, const char *ext, size_t size); const char *in_str, const char *ext, size_t size);
/**
* fill_pathname_noext:
* @out_path : output path
* @in_path : input path
* @replace : what to replace
* @size : buffer size of output path
*
* Appends a filename extension 'replace' to 'in_path', and outputs
* result in 'out_path'.
*
* Assumes in_path has no extension. If an extension is still
* present in 'in_path', it will be ignored.
*
*/
size_t fill_pathname_noext(char *out_path, const char *in_path,
const char *replace, size_t size);
/** /**
* find_last_slash: * find_last_slash:
* @str : input path * @str : path
* @size : size of path
* *
* Gets a pointer to the last slash in the input path. * Find last slash in path. Tries to find
* a backslash on Windows too which takes precedence
* over regular slash.
* Hidden non-leaf function cost:
* - calls strrchr
* *
* Returns: a pointer to the last slash in the input path. * @return pointer to last slash/backslash found in @str.
**/ **/
char *find_last_slash(const char *str); char *find_last_slash(const char *str);
@ -288,6 +365,11 @@ char *find_last_slash(const char *str);
* *
* E.g..: in_dir = "/tmp/some_dir", in_basename = "/some_content/foo.c", * E.g..: in_dir = "/tmp/some_dir", in_basename = "/some_content/foo.c",
* replace = ".asm" => in_dir = "/tmp/some_dir/foo.c.asm" * replace = ".asm" => in_dir = "/tmp/some_dir/foo.c.asm"
*
* Hidden non-leaf function cost:
* - Calls fill_pathname_slash()
* - Calls path_basename()
* - Calls strlcpy 2x
**/ **/
size_t fill_pathname_dir(char *in_dir, const char *in_basename, size_t fill_pathname_dir(char *in_dir, const char *in_basename,
const char *replace, size_t size); const char *replace, size_t size);
@ -299,16 +381,15 @@ size_t fill_pathname_dir(char *in_dir, const char *in_basename,
* @size : size of output path * @size : size of output path
* *
* Copies basename of @in_path into @out_path. * Copies basename of @in_path into @out_path.
*
* Hidden non-leaf function cost:
* - Calls path_basename()
* - Calls strlcpy
*
* @return length of the string copied into @out
**/ **/
size_t fill_pathname_base(char *out_path, const char *in_path, size_t size); size_t fill_pathname_base(char *out_path, const char *in_path, size_t size);
void fill_pathname_base_noext(char *out_dir,
const char *in_path, size_t size);
size_t fill_pathname_base_ext(char *out,
const char *in_path, const char *ext,
size_t size);
/** /**
* fill_pathname_basedir: * fill_pathname_basedir:
* @out_dir : output directory * @out_dir : output directory
@ -318,12 +399,13 @@ size_t fill_pathname_base_ext(char *out,
* Copies base directory of @in_path into @out_path. * Copies base directory of @in_path into @out_path.
* If in_path is a path without any slashes (relative current directory), * If in_path is a path without any slashes (relative current directory),
* @out_path will get path "./". * @out_path will get path "./".
*
* Hidden non-leaf function cost:
* - Calls strlcpy
* - Calls path_basedir()
**/ **/
void fill_pathname_basedir(char *out_path, const char *in_path, size_t size); void fill_pathname_basedir(char *out_path, const char *in_path, size_t size);
void fill_pathname_basedir_noext(char *out_dir,
const char *in_path, size_t size);
/** /**
* fill_pathname_parent_dir_name: * fill_pathname_parent_dir_name:
* @out_dir : output directory * @out_dir : output directory
@ -332,7 +414,13 @@ void fill_pathname_basedir_noext(char *out_dir,
* *
* Copies only the parent directory name of @in_dir into @out_dir. * Copies only the parent directory name of @in_dir into @out_dir.
* The two buffers must not overlap. Removes trailing '/'. * The two buffers must not overlap. Removes trailing '/'.
* Returns true on success, false if a slash was not found in the path. *
* Hidden non-leaf function cost:
* - Calls strdup
* - Calls find_last_slash() x times
* - Can call strlcpy
*
* @return true on success, false if a slash was not found in the path.
**/ **/
bool fill_pathname_parent_dir_name(char *out_dir, bool fill_pathname_parent_dir_name(char *out_dir,
const char *in_dir, size_t size); const char *in_dir, size_t size);
@ -346,6 +434,11 @@ bool fill_pathname_parent_dir_name(char *out_dir,
* Copies parent directory of @in_dir into @out_dir. * Copies parent directory of @in_dir into @out_dir.
* Assumes @in_dir is a directory. Keeps trailing '/'. * Assumes @in_dir is a directory. Keeps trailing '/'.
* If the path was already at the root directory, @out_dir will be an empty string. * If the path was already at the root directory, @out_dir will be an empty string.
*
* Hidden non-leaf function cost:
* - Can call strlcpy if (@out_dir != @in_dir)
* - Calls strlen if (@out_dir == @in_dir)
* - Calls path_parent_dir()
**/ **/
void fill_pathname_parent_dir(char *out_dir, void fill_pathname_parent_dir(char *out_dir,
const char *in_dir, size_t size); const char *in_dir, size_t size);
@ -373,30 +466,51 @@ void fill_pathname_resolve_relative(char *out_path, const char *in_refpath,
* @size : size of output path * @size : size of output path
* *
* Joins a directory (@dir) and path (@path) together. * Joins a directory (@dir) and path (@path) together.
* Makes sure not to get two consecutive slashes * Makes sure not to get two consecutive slashes
* between directory and path. * between directory and path.
*
* Hidden non-leaf function cost:
* - calls strlcpy at least once
* - calls fill_pathname_slash()
*
* Deprecated. Use fill_pathname_join_special() instead
* if you can ensure @dir != @out_path
*
* @return Length of the string copied into @out_path
**/ **/
size_t fill_pathname_join(char *out_path, const char *dir, size_t fill_pathname_join(char *out_path, const char *dir,
const char *path, size_t size); const char *path, size_t size);
/**
* fill_pathname_join_special:
* @out_path : output path
* @dir : directory. Cannot be identical to @out_path
* @path : path
* @size : size of output path
*
*
* Specialized version of fill_pathname_join.
* Unlike fill_pathname_join(),
* @dir and @out_path CANNOT be identical.
*
* Joins a directory (@dir) and path (@path) together.
* Makes sure not to get two consecutive slashes
* between directory and path.
*
* Hidden non-leaf function cost:
* - calls strlcpy 2x
* - calls find_last_slash()
*
* @return Length of the string copied into @out_path
**/
size_t fill_pathname_join_special(char *out_path,
const char *dir, const char *path, size_t size);
size_t fill_pathname_join_special_ext(char *out_path, size_t fill_pathname_join_special_ext(char *out_path,
const char *dir, const char *path, const char *dir, const char *path,
const char *last, const char *ext, const char *last, const char *ext,
size_t size); size_t size);
size_t fill_pathname_join_concat_noext(char *out_path,
const char *dir, const char *path,
const char *concat,
size_t size);
size_t fill_pathname_join_concat(char *out_path,
const char *dir, const char *path,
const char *concat,
size_t size);
void fill_pathname_join_noext(char *out_path,
const char *dir, const char *path, size_t size);
/** /**
* fill_pathname_join_delim: * fill_pathname_join_delim:
* @out_path : output path * @out_path : output path
@ -407,40 +521,58 @@ void fill_pathname_join_noext(char *out_path,
* *
* Joins a directory (@dir) and path (@path) together * Joins a directory (@dir) and path (@path) together
* using the given delimiter (@delim). * using the given delimiter (@delim).
*
* Hidden non-leaf function cost:
* - can call strlen
* - can call strlcpy
* - can call strlcat
**/ **/
size_t fill_pathname_join_delim(char *out_path, const char *dir, size_t fill_pathname_join_delim(char *out_path, const char *dir,
const char *path, const char delim, size_t size); const char *path, const char delim, size_t size);
size_t fill_pathname_join_delim_concat(char *out_path, const char *dir, size_t fill_pathname_expand_special(char *out_path,
const char *path, const char delim, const char *concat, const char *in_path, size_t size);
size_t size);
size_t fill_pathname_abbreviate_special(char *out_path,
const char *in_path, size_t size);
/** /**
* fill_short_pathname_representation: * fill_pathname_abbreviated_or_relative:
* @out_rep : output representation
* @in_path : input path
* @size : size of output representation
* *
* Generates a short representation of path. It should only * Fills the supplied path with either the abbreviated path or
* be used for displaying the result; the output representation is not * the relative path, which ever one has less depth / number of slashes
* 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 * If lengths of abbreviated and relative paths are the same,
* the main image file. * the relative path will be used
* @in_path can be an absolute, relative or abbreviated path
* *
* E.g.: "/path/to/game.img" -> game.img * @return Length of the string copied into @out_path
* "/path/to/myarchive.7z#folder/to/game.img" -> game.img **/
*/ size_t fill_pathname_abbreviated_or_relative(char *out_path,
size_t fill_short_pathname_representation(char* out_rep, const char *in_refpath, const char *in_path, size_t size);
const char *in_path, size_t size);
void fill_short_pathname_representation_noext(char* out_rep, /**
const char *in_path, size_t size); * pathname_conform_slashes_to_os:
*
* @path : path
*
* Leaf function.
*
* Changes the slashes to the correct kind for the os
* So forward slash on linux and backslash on Windows
**/
void pathname_conform_slashes_to_os(char *path);
void fill_pathname_expand_special(char *out_path, /**
const char *in_path, size_t size); * pathname_make_slashes_portable:
* @path : path
void fill_pathname_abbreviate_special(char *out_path, *
const char *in_path, size_t size); * Leaf function.
*
* Change all slashes to forward so they are more
* portable between Windows and Linux
**/
void pathname_make_slashes_portable(char *path);
/** /**
* path_basedir: * path_basedir:
@ -457,8 +589,8 @@ void path_basedir_wrapper(char *path);
* *
* Checks if character (@c) is a slash. * 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 #ifdef _WIN32
#define PATH_CHAR_IS_SLASH(c) (((c) == '/') || ((c) == '\\')) #define PATH_CHAR_IS_SLASH(c) (((c) == '/') || ((c) == '\\'))
#else #else
@ -470,8 +602,8 @@ void path_basedir_wrapper(char *path);
* *
* Gets the default slash separator. * Gets the default slash separator.
* *
* Returns: default slash separator. * @return default slash separator.
*/ **/
#ifdef _WIN32 #ifdef _WIN32
#define PATH_DEFAULT_SLASH() "\\" #define PATH_DEFAULT_SLASH() "\\"
#define PATH_DEFAULT_SLASH_C() '\\' #define PATH_DEFAULT_SLASH_C() '\\'
@ -487,8 +619,13 @@ void path_basedir_wrapper(char *path);
* *
* Assumes path is a directory. Appends a slash * Assumes path is a directory. Appends a slash
* if not already there. * if not already there.
* Hidden non-leaf function cost:
* - calls find_last_slash()
* - can call strlcat once if it returns false
* - calls strlen
**/ **/
void fill_pathname_slash(char *path, size_t size); size_t fill_pathname_slash(char *path, size_t size);
#if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL) #if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
void fill_pathname_application_path(char *buf, size_t size); void fill_pathname_application_path(char *buf, size_t size);
@ -502,7 +639,16 @@ void fill_pathname_home_dir(char *buf, size_t size);
* *
* Create directory on filesystem. * 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); bool path_mkdir(const char *dir);
@ -512,10 +658,15 @@ bool path_mkdir(const char *dir);
* *
* Checks if path is a directory. * 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); bool path_is_directory(const char *path);
/* Time format strings with AM-PM designation require special
* handling due to platform dependence */
void strftime_am_pm(char *s, size_t len, const char* format,
const void* timeptr);
bool path_is_character_special(const char *path); bool path_is_character_special(const char *path);
int path_stat(const char *path); int path_stat(const char *path);

View File

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

View File

@ -42,7 +42,7 @@ RETRO_BEGIN_DECLS
* *
* Create a directory listing, appending to an existing list * Create a directory listing, appending to an existing list
* *
* Returns: true success, false in case of error. * @return Returns true on success, otherwise false.
**/ **/
bool dir_list_append(struct string_list *list, const char *dir, const char *ext, bool dir_list_append(struct string_list *list, const char *dir, const char *ext,
bool include_dirs, bool include_hidden, bool include_compressed, bool recursive); bool include_dirs, bool include_hidden, bool include_compressed, bool recursive);
@ -58,15 +58,18 @@ bool dir_list_append(struct string_list *list, const char *dir, const char *ext,
* *
* Create a directory listing. * Create a directory listing.
* *
* Returns: pointer to a directory listing of type 'struct string_list *' on success, * @return pointer to a directory listing of type 'struct string_list *' on success,
* NULL in case of error. Has to be freed manually. * NULL in case of error. Has to be freed manually.
**/ **/
struct string_list *dir_list_new(const char *dir, const char *ext, struct string_list *dir_list_new(const char *dir, const char *ext,
bool include_dirs, bool include_hidden, bool include_compressed, bool recursive); bool include_dirs, bool include_hidden, bool include_compressed, bool recursive);
/* Warning: 'list' must zero initialised before /**
* calling this function, otherwise memory leaks/ * dir_list_initialize:
* undefined behaviour will occur */ *
* NOTE: @list must zero initialised before
* calling this function, otherwise UB.
**/
bool dir_list_initialize(struct string_list *list, bool dir_list_initialize(struct string_list *list,
const char *dir, const char *dir,
const char *ext, bool include_dirs, const char *ext, bool include_dirs,
@ -79,16 +82,23 @@ bool dir_list_initialize(struct string_list *list,
* @dir_first : move the directories in the listing to the top? * @dir_first : move the directories in the listing to the top?
* *
* Sorts a directory listing. * Sorts a directory listing.
*
**/ **/
void dir_list_sort(struct string_list *list, bool dir_first); void dir_list_sort(struct string_list *list, bool dir_first);
/**
* dir_list_sort_ignore_ext:
* @list : pointer to the directory listing.
* @dir_first : move the directories in the listing to the top?
*
* Sorts a directory listing. File extensions are ignored.
**/
void dir_list_sort_ignore_ext(struct string_list *list, bool dir_first);
/** /**
* dir_list_free: * dir_list_free:
* @list : pointer to the directory listing * @list : pointer to the directory listing
* *
* Frees a directory listing. * Frees a directory listing.
*
**/ **/
void dir_list_free(struct string_list *list); void dir_list_free(struct string_list *list);

View File

@ -59,7 +59,7 @@ struct string_list
* *
* Searches for an element (@elem) inside the string list. * Searches for an element (@elem) inside the string list.
* *
* Returns: true (1) if element could be found, otherwise false (0). * @return Number of elements found, otherwise 0.
*/ */
int string_list_find_elem(const struct string_list *list, const char *elem); int string_list_find_elem(const struct string_list *list, const char *elem);
@ -100,8 +100,8 @@ bool string_split_noalloc(struct string_list *list,
* Includes empty strings - i.e. two adjacent delimiters will resolve * Includes empty strings - i.e. two adjacent delimiters will resolve
* to a string list element of "". * to a string list element of "".
* *
* Returns: new string list if successful, otherwise NULL. * @return New string list if successful, otherwise NULL.
*/ **/
struct string_list *string_separate(char *str, const char *delim); struct string_list *string_separate(char *str, const char *delim);
bool string_separate_noalloc(struct string_list *list, bool string_separate_noalloc(struct string_list *list,
@ -116,8 +116,8 @@ bool string_list_initialize(struct string_list *list);
* *
* Creates a new string list. Has to be freed manually. * Creates a new string list. Has to be freed manually.
* *
* Returns: new string list if successful, otherwise NULL. * @return New string list if successful, otherwise NULL.
*/ **/
struct string_list *string_list_new(void); struct string_list *string_list_new(void);
/** /**
@ -127,8 +127,12 @@ struct string_list *string_list_new(void);
* @attr : attributes of new element. * @attr : attributes of new element.
* *
* Appends a new element to the string list. * Appends a new element to the string list.
* Hidden non-leaf function cost:
* - Calls string_list_capacity()
* - Calls strdup
* *
* Returns: true (1) if successful, otherwise false (0). * @return true if successful, otherwise false.
**/ **/
bool string_list_append(struct string_list *list, const char *elem, bool string_list_append(struct string_list *list, const char *elem,
union string_list_elem_attr attr); union string_list_elem_attr attr);
@ -142,7 +146,12 @@ bool string_list_append(struct string_list *list, const char *elem,
* *
* Appends a new element to the string list. * Appends a new element to the string list.
* *
* Returns: true (1) if successful, otherwise false (0). * Hidden non-leaf function cost:
* - Calls string_list_capacity()
* - Calls malloc
* - Calls strlcpy
*
* @return true if successful, otherwise false.
**/ **/
bool string_list_append_n(struct string_list *list, const char *elem, bool string_list_append_n(struct string_list *list, const char *elem,
unsigned length, union string_list_elem_attr attr); unsigned length, union string_list_elem_attr attr);
@ -152,7 +161,7 @@ bool string_list_append_n(struct string_list *list, const char *elem,
* @list : pointer to string list object * @list : pointer to string list object
* *
* Frees a string list. * Frees a string list.
*/ **/
void string_list_free(struct string_list *list); void string_list_free(struct string_list *list);
/** /**
@ -164,7 +173,13 @@ void string_list_free(struct string_list *list);
* *
* A string list will be joined/concatenated as a * A string list will be joined/concatenated as a
* string to @buffer, delimited by @delim. * string to @buffer, delimited by @delim.
*/ *
* NOTE: @buffer must be NULL-terminated.
*
* Hidden non-leaf function cost:
* - Calls strlen_size()
* - Calls strlcat x times in a loop
**/
void string_list_join_concat(char *buffer, size_t size, void string_list_join_concat(char *buffer, size_t size,
const struct string_list *list, const char *sep); const struct string_list *list, const char *sep);
@ -175,6 +190,10 @@ void string_list_join_concat(char *buffer, size_t size,
* @str : value for the element. * @str : value for the element.
* *
* Set value of element inside string list. * Set value of element inside string list.
*
* Hidden non-leaf function cost:
* - Calls free
* - Calls strdup
**/ **/
void string_list_set(struct string_list *list, unsigned idx, void string_list_set(struct string_list *list, unsigned idx,
const char *str); const char *str);

View File

@ -26,7 +26,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#if defined(PSP) || defined(PS2) || defined(GEKKO) || defined(VITA) || defined(_XBOX) || defined(_3DS) || defined(WIIU) || defined(SWITCH) || defined(HAVE_LIBNX) #if defined(PSP) || defined(PS2) || defined(GEKKO) || defined(VITA) || defined(_XBOX) || defined(_3DS) || defined(WIIU) || defined(SWITCH) || defined(HAVE_LIBNX) || defined(__PS3__) || defined(__PSL1GHT__)
/* No mman available */ /* No mman available */
#elif defined(_WIN32) && !defined(_XBOX) #elif defined(_WIN32) && !defined(_XBOX)
#include <windows.h> #include <windows.h>

View File

@ -27,9 +27,7 @@
#ifdef RARCH_INTERNAL #ifdef RARCH_INTERNAL
#include <stdio.h> #include <stdio.h>
#define retro_assert(cond) do { \ #define retro_assert(cond) ((void)( (cond) || (printf("Assertion failed at %s:%d.\n", __FILE__, __LINE__), abort(), 0) ))
if (!(cond)) { printf("Assertion failed at %s:%d.\n", __FILE__, __LINE__); abort(); } \
} while(0)
#else #else
#define retro_assert(cond) assert(cond) #define retro_assert(cond) assert(cond)
#endif #endif

View File

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

View File

@ -25,7 +25,6 @@
#include <libretro.h> #include <libretro.h>
#include <retro_common_api.h> #include <retro_common_api.h>
#include <retro_miscellaneous.h>
#include <boolean.h> #include <boolean.h>

View File

@ -74,31 +74,29 @@ static INLINE uint64_t SWAP64(uint64_t val)
} }
#endif #endif
#if defined (LSB_FIRST) || defined (MSB_FIRST)
# warning Defining MSB_FIRST and LSB_FIRST in compile options is deprecated
# undef LSB_FIRST
# undef MSB_FIRST
#endif
#if defined(_MSC_VER) && !defined(_XBOX)
#include <winsock2.h>
#endif
#ifdef _MSC_VER #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 #define LSB_FIRST 1
#endif
#elif _M_PPC #elif _M_PPC
#ifndef MSB_FIRST
#define MSB_FIRST 1 #define MSB_FIRST 1
#endif
#else #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 */ /* 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" #error "unknown platform, can't determine endianness"
#endif #endif
#else #else
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#ifndef MSB_FIRST
#define MSB_FIRST 1 #define MSB_FIRST 1
#endif
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#ifndef LSB_FIRST
#define LSB_FIRST 1 #define LSB_FIRST 1
#endif
#else #else
#error "Invalid endianness macros" #error "Invalid endianness macros"
#endif #endif

View File

@ -91,4 +91,100 @@ static INLINE uint32_t prev_pow2(uint32_t v)
return v - (v >> 1); return v - (v >> 1);
} }
/**
* clamp:
* @v : initial value
*
* Get the clamped value based on initial value.
*
* Returns: clamped value (derived from @v).
**/
static INLINE float clamp_value(float v, float min, float max)
{
return v <= min ? min : v >= max ? max : v;
}
/**
* saturate_value:
* @v : initial value
*
* Get the clamped 0.0-1.0 value based on initial value.
*
* Returns: clamped 0.0-1.0 value (derived from @v).
**/
static INLINE float saturate_value(float v)
{
return clamp_value(v, 0.0f, 1.0f);
}
/**
* dot_product:
* @a : left hand vector value
* @b : right hand vector value
*
* Get the dot product of the two passed in vectors.
*
* Returns: dot product value (derived from @a and @b).
**/
static INLINE float dot_product(const float* a, const float* b)
{
return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]);
}
/**
* convert_rgb_to_yxy:
* @rgb : in RGB colour space value
* @Yxy : out Yxy colour space value
*
* Convert from RGB colour space to Yxy colour space.
*
* Returns: Yxy colour space value (derived from @rgb).
**/
static INLINE void convert_rgb_to_yxy(const float* rgb, float* Yxy)
{
float inv;
float xyz[3];
float one[3] = {1.0, 1.0, 1.0};
float rgb_xyz[3][3] = {
{0.4124564, 0.3575761, 0.1804375},
{0.2126729, 0.7151522, 0.0721750},
{0.0193339, 0.1191920, 0.9503041}
};
xyz[0] = dot_product(rgb_xyz[0], rgb);
xyz[1] = dot_product(rgb_xyz[1], rgb);
xyz[2] = dot_product(rgb_xyz[2], rgb);
inv = 1.0f / dot_product(xyz, one);
Yxy[0] = xyz[1];
Yxy[1] = xyz[0] * inv;
Yxy[2] = xyz[1] * inv;
}
/**
* convert_yxy_to_rgb:
* @rgb : in Yxy colour space value
* @Yxy : out rgb colour space value
*
* Convert from Yxy colour space to rgb colour space.
*
* Returns: rgb colour space value (derived from @Yxy).
**/
static INLINE void convert_yxy_to_rgb(const float* Yxy, float* rgb)
{
float xyz[3];
float xyz_rgb[3][3] = {
{3.2404542, -1.5371385, -0.4985314},
{-0.9692660, 1.8760108, 0.0415560},
{0.0556434, -0.2040259, 1.0572252}
};
xyz[0] = Yxy[0] * Yxy[1] / Yxy[2];
xyz[1] = Yxy[0];
xyz[2] = Yxy[0] * (1.0 - Yxy[1] - Yxy[2]) / Yxy[2];
rgb[0] = dot_product(xyz_rgb[0], xyz);
rgb[1] = dot_product(xyz_rgb[1], xyz);
rgb[2] = dot_product(xyz_rgb[2], xyz);
}
#endif #endif

View File

@ -49,6 +49,10 @@
#include <compat/msvc.h> #include <compat/msvc.h>
#endif #endif
#ifdef IOS
#include <sys/param.h>
#endif
static INLINE void bits_or_bits(uint32_t *a, uint32_t *b, uint32_t count) static INLINE void bits_or_bits(uint32_t *a, uint32_t *b, uint32_t count)
{ {
uint32_t i; uint32_t i;
@ -74,14 +78,29 @@ static INLINE bool bits_any_set(uint32_t* ptr, uint32_t count)
return false; return false;
} }
static INLINE bool bits_any_different(uint32_t *a, uint32_t *b, uint32_t count)
{
uint32_t i;
for (i = 0; i < count; i++)
{
if (a[i] != b[i])
return true;
}
return false;
}
#ifndef PATH_MAX_LENGTH #ifndef PATH_MAX_LENGTH
#if defined(_XBOX1) || defined(_3DS) || defined(PSP) || defined(PS2) || defined(GEKKO)|| defined(WIIU) || defined(ORBIS) #if defined(_XBOX1) || defined(_3DS) || defined(PSP) || defined(PS2) || defined(GEKKO)|| defined(WIIU) || defined(__PSL1GHT__) || defined(__PS3__)
#define PATH_MAX_LENGTH 512 #define PATH_MAX_LENGTH 512
#else #else
#define PATH_MAX_LENGTH 4096 #define PATH_MAX_LENGTH 4096
#endif #endif
#endif #endif
#ifndef NAME_MAX_LENGTH
#define NAME_MAX_LENGTH 256
#endif
#ifndef MAX #ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif #endif
@ -134,6 +153,16 @@ static INLINE bool bits_any_set(uint32_t* ptr, uint32_t count)
#define BIT256_GET_PTR(a, bit) BIT256_GET(*a, bit) #define BIT256_GET_PTR(a, bit) BIT256_GET(*a, bit)
#define BIT256_CLEAR_ALL_PTR(a) BIT256_CLEAR_ALL(*a) #define BIT256_CLEAR_ALL_PTR(a) BIT256_CLEAR_ALL(*a)
#define BIT512_SET(a, bit) BIT256_SET(a, bit)
#define BIT512_CLEAR(a, bit) BIT256_CLEAR(a, bit)
#define BIT512_GET(a, bit) BIT256_GET(a, bit)
#define BIT512_CLEAR_ALL(a) BIT256_CLEAR_ALL(a)
#define BIT512_SET_PTR(a, bit) BIT512_SET(*a, bit)
#define BIT512_CLEAR_PTR(a, bit) BIT512_CLEAR(*a, bit)
#define BIT512_GET_PTR(a, bit) BIT512_GET(*a, bit)
#define BIT512_CLEAR_ALL_PTR(a) BIT512_CLEAR_ALL(*a)
#define BITS_COPY16_PTR(a,bits) \ #define BITS_COPY16_PTR(a,bits) \
{ \ { \
BIT128_CLEAR_ALL_PTR(a); \ BIT128_CLEAR_ALL_PTR(a); \
@ -146,6 +175,13 @@ static INLINE bool bits_any_set(uint32_t* ptr, uint32_t count)
BITS_GET_ELEM_PTR(a, 0) = (bits); \ BITS_GET_ELEM_PTR(a, 0) = (bits); \
} }
#define BITS_COPY64_PTR(a,bits) \
{ \
BIT128_CLEAR_ALL_PTR(a); \
BITS_GET_ELEM_PTR(a, 0) = (bits); \
BITS_GET_ELEM_PTR(a, 1) = (bits >> 32); \
}
/* Helper macros and struct to keep track of many booleans. */ /* Helper macros and struct to keep track of many booleans. */
/* This struct has 256 bits. */ /* This struct has 256 bits. */
typedef struct typedef struct
@ -153,6 +189,12 @@ typedef struct
uint32_t data[8]; uint32_t data[8];
} retro_bits_t; } retro_bits_t;
/* This struct has 512 bits. */
typedef struct
{
uint32_t data[16];
} retro_bits_512_t;
#ifdef _WIN32 #ifdef _WIN32
# ifdef _WIN64 # ifdef _WIN64
# define PRI_SIZET PRIu64 # define PRI_SIZET PRIu64

View File

@ -27,6 +27,8 @@
#if defined(XENON) #if defined(XENON)
#include <time/time.h> #include <time/time.h>
#elif !defined(__PSL1GHT__) && defined(__PS3__)
#include <sys/timer.h>
#elif defined(GEKKO) || defined(__PSL1GHT__) || defined(__QNX__) #elif defined(GEKKO) || defined(__PSL1GHT__) || defined(__QNX__)
#include <unistd.h> #include <unistd.h>
#elif defined(WIIU) #elif defined(WIIU)
@ -37,6 +39,8 @@
#include <psp2/kernel/threadmgr.h> #include <psp2/kernel/threadmgr.h>
#elif defined(_3DS) #elif defined(_3DS)
#include <3ds.h> #include <3ds.h>
#elif defined(EMSCRIPTEN)
#include <emscripten/emscripten.h>
#else #else
#include <time.h> #include <time.h>
#endif #endif
@ -81,7 +85,7 @@ static int nanosleepDOS(const struct timespec *rqtp, struct timespec *rmtp)
* *
* Sleeps for a specified amount of milliseconds (@msec). * Sleeps for a specified amount of milliseconds (@msec).
**/ **/
#if defined(PSP) || defined(VITA) #if defined(VITA)
#define retro_sleep(msec) (sceKernelDelayThread(1000 * (msec))) #define retro_sleep(msec) (sceKernelDelayThread(1000 * (msec)))
#elif defined(_3DS) #elif defined(_3DS)
#define retro_sleep(msec) (svcSleepThread(1000000 * (s64)(msec))) #define retro_sleep(msec) (svcSleepThread(1000000 * (s64)(msec)))
@ -91,14 +95,18 @@ static int nanosleepDOS(const struct timespec *rqtp, struct timespec *rmtp)
#define retro_sleep(msec) (Sleep((msec))) #define retro_sleep(msec) (Sleep((msec)))
#elif defined(XENON) #elif defined(XENON)
#define retro_sleep(msec) (udelay(1000 * (msec))) #define retro_sleep(msec) (udelay(1000 * (msec)))
#elif !defined(__PSL1GHT__) && defined(__PS3__)
#define retro_sleep(msec) (sys_timer_usleep(1000 * (msec)))
#elif defined(GEKKO) || defined(__PSL1GHT__) || defined(__QNX__) #elif defined(GEKKO) || defined(__PSL1GHT__) || defined(__QNX__)
#define retro_sleep(msec) (usleep(1000 * (msec))) #define retro_sleep(msec) (usleep(1000 * (msec)))
#elif defined(WIIU) #elif defined(WIIU)
#define retro_sleep(msec) (OSSleepTicks(ms_to_ticks((msec)))) #define retro_sleep(msec) (OSSleepTicks(ms_to_ticks((msec))))
#elif defined(EMSCRIPTEN)
#define retro_sleep(msec) (emscripten_sleep(msec))
#else #else
static INLINE void retro_sleep(unsigned msec) static INLINE void retro_sleep(unsigned msec)
{ {
struct timespec tv = {0}; struct timespec tv;
tv.tv_sec = msec / 1000; tv.tv_sec = msec / 1000;
tv.tv_nsec = (msec % 1000) * 1000000; tv.tv_nsec = (msec % 1000) * 1000000;
nanosleep(&tv, NULL); nanosleep(&tv, NULL);

View File

@ -59,7 +59,7 @@ int64_t filestream_truncate(RFILE *stream, int64_t length);
* @bufsize : optional buffer size (-1 or 0 to use default) * @bufsize : optional buffer size (-1 or 0 to use default)
* *
* Opens a file for reading or writing, depending on the requested mode. * Opens a file for reading or writing, depending on the requested mode.
* Returns a pointer to an RFILE if opened successfully, otherwise NULL. * @return A pointer to an RFILE if opened successfully, otherwise NULL.
**/ **/
RFILE* filestream_open(const char *path, unsigned mode, unsigned hints); RFILE* filestream_open(const char *path, unsigned mode, unsigned hints);
@ -75,16 +75,39 @@ void filestream_rewind(RFILE *stream);
int filestream_close(RFILE *stream); int filestream_close(RFILE *stream);
/**
* filestream_read_file:
* @path : path to file.
* @buf : buffer to allocate and read the contents of the
* file into. Needs to be freed manually.
* @len : optional output integer containing bytes read.
*
* Read the contents of a file into @buf.
*
* @return Non-zero on success.
*/
int64_t filestream_read_file(const char *path, void **buf, int64_t *len); int64_t filestream_read_file(const char *path, void **buf, int64_t *len);
char* filestream_gets(RFILE *stream, char *s, size_t len); char* filestream_gets(RFILE *stream, char *s, size_t len);
int filestream_getc(RFILE *stream); int filestream_getc(RFILE *stream);
int filestream_vscanf(RFILE *stream, const char* format, va_list *args);
int filestream_scanf(RFILE *stream, const char* format, ...); int filestream_scanf(RFILE *stream, const char* format, ...);
int filestream_eof(RFILE *stream); int filestream_eof(RFILE *stream);
/**
* filestream_write_file:
* @path : path to file.
* @data : contents to write to the file.
* @size : size of the contents.
*
* Writes data to a file.
*
* @return true on success, otherwise false.
**/
bool filestream_write_file(const char *path, const void *data, int64_t size); bool filestream_write_file(const char *path, const void *data, int64_t size);
int filestream_putc(RFILE *stream, int c); int filestream_putc(RFILE *stream, int c);
@ -105,7 +128,11 @@ const char* filestream_get_path(RFILE *stream);
bool filestream_exists(const char *path); bool filestream_exists(const char *path);
/* Returned pointer must be freed by the caller. */ /**
* filestream_getline:
*
* Returned pointer must be freed by the caller.
**/
char* filestream_getline(RFILE *stream); char* filestream_getline(RFILE *stream);
libretro_vfs_implementation_file* filestream_get_vfs_handle(RFILE *stream); libretro_vfs_implementation_file* filestream_get_vfs_handle(RFILE *stream);

View File

@ -44,20 +44,20 @@ RETRO_BEGIN_DECLS
#define string_is_not_equal_fast(a, b, size) (memcmp(a, b, size) != 0) #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 string_is_equal_fast(a, b, size) (memcmp(a, b, size) == 0)
#define TOLOWER(c) (c | (lr_char_props[c] & 0x20)) #define TOLOWER(c) ((c) | (lr_char_props[(unsigned char)(c)] & 0x20))
#define TOUPPER(c) (c & ~(lr_char_props[c] & 0x20)) #define TOUPPER(c) ((c) & ~(lr_char_props[(unsigned char)(c)] & 0x20))
/* C standard says \f \v are space, but this one disagrees */ /* C standard says \f \v are space, but this one disagrees */
#define ISSPACE(c) (lr_char_props[c] & 0x80) #define ISSPACE(c) (lr_char_props[(unsigned char)(c)] & 0x80)
#define ISDIGIT(c) (lr_char_props[c] & 0x40) #define ISDIGIT(c) (lr_char_props[(unsigned char)(c)] & 0x40)
#define ISALPHA(c) (lr_char_props[c] & 0x20) #define ISALPHA(c) (lr_char_props[(unsigned char)(c)] & 0x20)
#define ISLOWER(c) (lr_char_props[c] & 0x04) #define ISLOWER(c) (lr_char_props[(unsigned char)(c)] & 0x04)
#define ISUPPER(c) (lr_char_props[c] & 0x02) #define ISUPPER(c) (lr_char_props[(unsigned char)(c)] & 0x02)
#define ISALNUM(c) (lr_char_props[c] & 0x60) #define ISALNUM(c) (lr_char_props[(unsigned char)(c)] & 0x60)
#define ISUALPHA(c) (lr_char_props[c] & 0x28) #define ISUALPHA(c) (lr_char_props[(unsigned char)(c)] & 0x28)
#define ISUALNUM(c) (lr_char_props[c] & 0x68) #define ISUALNUM(c) (lr_char_props[(unsigned char)(c)] & 0x68)
#define IS_XDIGIT(c) (lr_char_props[c] & 0x01) #define IS_XDIGIT(c) (lr_char_props[(unsigned char)(c)] & 0x01)
/* Deprecated alias, all callers should use string_is_equal_case_insensitive instead */ /* Deprecated alias, all callers should use string_is_equal_case_insensitive instead */
#define string_is_equal_noncase string_is_equal_case_insensitive #define string_is_equal_noncase string_is_equal_case_insensitive
@ -92,16 +92,20 @@ static INLINE bool string_ends_with_size(const char *str, const char *suffix,
static INLINE bool string_ends_with(const char *str, const char *suffix) static INLINE bool string_ends_with(const char *str, const char *suffix)
{ {
if (!str || !suffix) return str && suffix && string_ends_with_size(str, suffix, strlen(str), strlen(suffix));
return false;
return string_ends_with_size(str, suffix, strlen(str), strlen(suffix));
} }
/* Returns the length of 'str' (c.f. strlen()), but only /**
* strlen_size:
*
* Leaf function.
*
* @return the length of 'str' (c.f. strlen()), but only
* checks the first 'size' characters * checks the first 'size' characters
* - If 'str' is NULL, returns 0 * - If 'str' is NULL, returns 0
* - If 'str' is not NULL and no '\0' character is found * - If 'str' is not NULL and no '\0' character is found
* in the first 'size' characters, returns 'size' */ * in the first 'size' characters, returns 'size'
**/
static INLINE size_t strlen_size(const char *str, size_t size) static INLINE size_t strlen_size(const char *str, size_t size)
{ {
size_t i = 0; size_t i = 0;
@ -130,32 +134,126 @@ static INLINE bool string_is_equal_case_insensitive(const char *a,
return (result == 0); return (result == 0);
} }
static INLINE bool string_starts_with_case_insensitive(const char *str,
const char *prefix)
{
int result = 0;
const unsigned char *p1 = (const unsigned char*)str;
const unsigned char *p2 = (const unsigned char*)prefix;
if (!str || !prefix)
return false;
if (p1 == p2)
return true;
while ((result = tolower (*p1++) - tolower (*p2)) == 0)
if (*p2++ == '\0')
break;
return (result == 0 || *p2 == '\0');
}
char *string_to_upper(char *s); char *string_to_upper(char *s);
char *string_to_lower(char *s); char *string_to_lower(char *s);
char *string_ucwords(char *s); char *string_ucwords(char *s);
char *string_replace_substring(const char *in, const char *pattern, char *string_replace_substring(const char *in,
const char *by); 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); char *string_trim_whitespace_left(char *const s);
/* Remove trailing whitespaces */ /**
* string_trim_whitespace_right:
*
* Remove trailing whitespaces
**/
char *string_trim_whitespace_right(char *const s); char *string_trim_whitespace_right(char *const s);
/* Remove leading and trailing whitespaces */ /**
* string_trim_whitespace:
*
* Remove leading and trailing whitespaces
**/
char *string_trim_whitespace(char *const s); char *string_trim_whitespace(char *const s);
/* max_lines == 0 means no limit */ /**
char *word_wrap(char *buffer, const char *string, * word_wrap:
int line_width, bool unicode, unsigned max_lines); * @dst : pointer to destination buffer.
* @dst_size : size of destination buffer.
* @src : pointer to input string.
* @src_len : length of @src
* @line_width : max number of characters per line.
* @wideglyph_width : not used, but is necessary to keep
* compatibility with word_wrap_wideglyph().
* @max_lines : max lines of destination string.
* 0 means no limit.
*
* Wraps string specified by 'src' to destination buffer
* specified by 'dst' and 'dst_size'.
* This function assumes that all glyphs in the string
* have an on-screen pixel width similar to that of
* regular Latin characters - i.e. it will not wrap
* correctly any text containing so-called 'wide' Unicode
* characters (e.g. CJK languages, emojis, etc.).
**/
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);
/* Splits string into tokens seperated by 'delim' /**
* word_wrap_wideglyph:
* @dst : pointer to destination buffer.
* @dst_size : size of destination buffer.
* @src : pointer to input string.
* @src_len : length of @src
* @line_width : max number of characters per line.
* @wideglyph_width : effective width of 'wide' Unicode glyphs.
* the value here is normalised relative to the
* typical on-screen pixel width of a regular
* Latin character:
* - a regular Latin character is defined to
* have an effective width of 100
* - wideglyph_width = 100 * (wide_character_pixel_width / latin_character_pixel_width)
* - e.g. if 'wide' Unicode characters in 'src'
* have an on-screen pixel width twice that of
* regular Latin characters, wideglyph_width
* would be 200
* @max_lines : max lines of destination string.
* 0 means no limit.
*
* Wraps string specified by @src to destination buffer
* specified by @dst and @dst_size.
* This function assumes that all glyphs in the string
* are:
* - EITHER 'non-wide' Unicode glyphs, with an on-screen
* pixel width similar to that of regular Latin characters
* - OR 'wide' Unicode glyphs (e.g. CJK languages, emojis, etc.)
* with an on-screen pixel width defined by @wideglyph_width
* Note that wrapping may occur in inappropriate locations
* if @src string contains 'wide' Unicode characters whose
* on-screen pixel width deviates greatly from the set
* @wideglyph_width value.
**/
size_t word_wrap_wideglyph(
char *dst, size_t dst_size,
const char *src, size_t src_len,
int line_width, int wideglyph_width,
unsigned max_lines);
/**
* string_tokenize:
*
* Splits string into tokens seperated by @delim
* > Returned token string must be free()'d * > Returned token string must be free()'d
* > Returns NULL if token is not found * > Returns NULL if token is not found
* > After each call, 'str' is set to the position after the * > After each call, @str is set to the position after the
* last found token * last found token
* > Tokens *include* empty strings * > Tokens *include* empty strings
* Usage example: * Usage example:
@ -168,29 +266,120 @@ char *word_wrap(char *buffer, const char *string,
* free(token); * free(token);
* token = NULL; * token = NULL;
* } * }
*/ **/
char* string_tokenize(char **str, const char *delim); char* string_tokenize(char **str, const char *delim);
/* Removes every instance of character 'c' from 'str' */ /**
* string_remove_all_chars:
* @str : input string (must be non-NULL, otherwise UB)
*
* Leaf function.
*
* Removes every instance of character @c from @str
**/
void string_remove_all_chars(char *str, char c); void string_remove_all_chars(char *str, char c);
/* Replaces every instance of character 'find' in 'str' /**
* 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); 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); unsigned string_to_unsigned(const char *str);
/* Converts hexadecimal string to unsigned integer. /**
* string_hex_to_unsigned:
* @str : input string (must be non-NULL, otherwise UB)
*
* Converts hexadecimal string to unsigned integer.
* Handles optional leading '0x'. * Handles optional leading '0x'.
* Returns 0 if string is invalid */ *
* @return 0 if string is invalid, otherwise > 0
**/
unsigned string_hex_to_unsigned(const char *str); unsigned string_hex_to_unsigned(const char *str);
char *string_init(const char *src); char *string_init(const char *src);
void string_set(char **string, const char *src); void string_set(char **string, const char *src);
/**
* string_count_occurrences_single_character:
*
* Leaf function.
*
* Get the total number of occurrences of character @c in @str.
*
* @return Total number of occurrences of character @c
*/
int string_count_occurrences_single_character(const char *str, char c);
/**
* string_replace_whitespace_with_single_character:
*
* Leaf function.
*
* Replaces all spaces with given character @c.
**/
void string_replace_whitespace_with_single_character(char *str, char c);
/**
* string_replace_multi_space_with_single_space:
*
* Leaf function.
*
* Replaces multiple spaces with a single space in a string.
**/
void string_replace_multi_space_with_single_space(char *str);
/**
* string_remove_all_whitespace:
*
* Leaf function.
*
* Remove all spaces from the given string.
**/
void string_remove_all_whitespace(char *str_trimmed, const char *str);
/* Retrieve the last occurance of the given character in a string. */
int string_index_last_occurance(const char *str, char c);
/**
* string_find_index_substring_string:
* @str : input string (must be non-NULL, otherwise UB)
* @substr : substring to find in @str
*
* Hidden non-leaf function cost:
* - Calls strstr
*
* Find the position of substring @substr in string @str.
**/
int string_find_index_substring_string(const char *str, const char *substr);
/**
* string_copy_only_ascii:
*
* Leaf function.
*
* Strips non-ASCII characters from a string.
**/
void string_copy_only_ascii(char *str_stripped, const char *str);
extern const unsigned char lr_char_props[256]; extern const unsigned char lr_char_props[256];
RETRO_END_DECLS RETRO_END_DECLS

View File

@ -62,7 +62,7 @@ enum vfs_scheme
VFS_SCHEME_CDROM VFS_SCHEME_CDROM
}; };
#ifndef __WINRT__ #if !(defined(__WINRT__) && defined(__cplusplus_winrt))
#ifdef VFS_FRONTEND #ifdef VFS_FRONTEND
struct retro_vfs_file_handle struct retro_vfs_file_handle
#else #else

View File

@ -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); int retro_vfs_closedir_impl(libretro_vfs_implementation_dir *dirstream);
#ifdef __WINRT__
void uwp_set_acl(const wchar_t* path, const wchar_t* AccessString);
#endif
RETRO_END_DECLS RETRO_END_DECLS
#endif #endif

View File

@ -46,6 +46,22 @@ static int qstrcmp_plain(const void *a_, const void *b_)
return strcasecmp(a->data, b->data); return strcasecmp(a->data, b->data);
} }
static int qstrcmp_plain_noext(const void *a_, const void *b_)
{
const struct string_list_elem *a = (const struct string_list_elem*)a_;
const struct string_list_elem *b = (const struct string_list_elem*)b_;
const char *ext_a = path_get_extension(a->data);
size_t l_a = string_is_empty(ext_a) ? strlen(a->data) : (ext_a - a->data - 1);
const char *ext_b = path_get_extension(b->data);
size_t l_b = string_is_empty(ext_b) ? strlen(b->data) : (ext_b - b->data - 1);
int rv = strncasecmp(a->data, b->data, MIN(l_a, l_b));
if (rv == 0 && l_a != l_b)
return (int)(l_a - l_b);
return rv;
}
static int qstrcmp_dir(const void *a_, const void *b_) static int qstrcmp_dir(const void *a_, const void *b_)
{ {
const struct string_list_elem *a = (const struct string_list_elem*)a_; const struct string_list_elem *a = (const struct string_list_elem*)a_;
@ -59,13 +75,25 @@ static int qstrcmp_dir(const void *a_, const void *b_)
return strcasecmp(a->data, b->data); return strcasecmp(a->data, b->data);
} }
static int qstrcmp_dir_noext(const void *a_, const void *b_)
{
const struct string_list_elem *a = (const struct string_list_elem*)a_;
const struct string_list_elem *b = (const struct string_list_elem*)b_;
int a_type = a->attr.i;
int b_type = b->attr.i;
/* Sort directories before files. */
if (a_type != b_type)
return b_type - a_type;
return qstrcmp_plain_noext(a, b);
}
/** /**
* dir_list_sort: * dir_list_sort:
* @list : pointer to the directory listing. * @list : pointer to the directory listing.
* @dir_first : move the directories in the listing to the top? * @dir_first : move the directories in the listing to the top?
* *
* Sorts a directory listing. * Sorts a directory listing.
*
**/ **/
void dir_list_sort(struct string_list *list, bool dir_first) void dir_list_sort(struct string_list *list, bool dir_first)
{ {
@ -74,12 +102,25 @@ void dir_list_sort(struct string_list *list, bool dir_first)
dir_first ? qstrcmp_dir : qstrcmp_plain); dir_first ? qstrcmp_dir : qstrcmp_plain);
} }
/**
* dir_list_sort_ignore_ext:
* @list : pointer to the directory listing.
* @dir_first : move the directories in the listing to the top?
*
* Sorts a directory listing. File extensions are ignored.
**/
void dir_list_sort_ignore_ext(struct string_list *list, bool dir_first)
{
if (list)
qsort(list->elems, list->size, sizeof(struct string_list_elem),
dir_first ? qstrcmp_dir_noext : qstrcmp_plain_noext);
}
/** /**
* dir_list_free: * dir_list_free:
* @list : pointer to the directory listing * @list : pointer to the directory listing
* *
* Frees a directory listing. * Frees a directory listing.
*
**/ **/
void dir_list_free(struct string_list *list) void dir_list_free(struct string_list *list)
{ {
@ -105,7 +146,7 @@ bool dir_list_deinitialize(struct string_list *list)
* *
* Add files within a directory to an existing string list * Add files within a directory to an existing string list
* *
* Returns: -1 on error, 0 on success. * @return -1 on error, 0 on success.
**/ **/
static int dir_list_read(const char *dir, static int dir_list_read(const char *dir,
struct string_list *list, struct string_list *ext_list, struct string_list *list, struct string_list *ext_list,
@ -139,11 +180,15 @@ static int dir_list_read(const char *dir,
continue; continue;
} }
file_path[0] = '\0'; fill_pathname_join_special(file_path, dir, name, sizeof(file_path));
fill_pathname_join(file_path, dir, name, sizeof(file_path));
if (retro_dirent_is_dir(entry, NULL)) if (retro_dirent_is_dir(entry, NULL))
{ {
/* Exclude this frequent hidden dir on platforms which can not handle hidden attribute */
#ifndef _WIN32
if (!include_hidden && strcmp(name, "System Volume Information") == 0)
continue;
#endif
if (recursive) if (recursive)
dir_list_read(file_path, list, ext_list, include_dirs, dir_list_read(file_path, list, ext_list, include_dirs,
include_hidden, include_compressed, recursive); include_hidden, include_compressed, recursive);
@ -206,7 +251,7 @@ error:
* *
* Create a directory listing, appending to an existing list * Create a directory listing, appending to an existing list
* *
* Returns: true success, false in case of error. * @return Returns true on success, otherwise false.
**/ **/
bool dir_list_append(struct string_list *list, bool dir_list_append(struct string_list *list,
const char *dir, const char *dir,
@ -241,7 +286,7 @@ bool dir_list_append(struct string_list *list,
* *
* Create a directory listing. * Create a directory listing.
* *
* Returns: pointer to a directory listing of type 'struct string_list *' on success, * @return pointer to a directory listing of type 'struct string_list *' on success,
* NULL in case of error. Has to be freed manually. * NULL in case of error. Has to be freed manually.
**/ **/
struct string_list *dir_list_new(const char *dir, struct string_list *dir_list_new(const char *dir,
@ -264,17 +309,20 @@ struct string_list *dir_list_new(const char *dir,
return list; return list;
} }
/* Warning: 'list' must zero initialised before /**
* calling this function, otherwise memory leaks/ * dir_list_initialize:
* undefined behaviour will occur */ *
* NOTE: @list must zero initialised before
* calling this function, otherwise UB.
**/
bool dir_list_initialize(struct string_list *list, bool dir_list_initialize(struct string_list *list,
const char *dir, const char *dir,
const char *ext, bool include_dirs, const char *ext, bool include_dirs,
bool include_hidden, bool include_compressed, bool include_hidden, bool include_compressed,
bool recursive) bool recursive)
{ {
if (!list || !string_list_initialize(list)) if (list && string_list_initialize(list))
return false; return dir_list_append(list, dir, ext, include_dirs,
return dir_list_append(list, dir, ext, include_dirs,
include_hidden, include_compressed, recursive); include_hidden, include_compressed, recursive);
return false;
} }

View File

@ -62,7 +62,7 @@ static bool string_list_deinitialize_internal(struct string_list *list)
* *
* Change maximum capacity of string list's size. * Change maximum capacity of string list's size.
* *
* Returns: true (1) if successful, otherwise false (0). * @return true if successful, otherwise false.
**/ **/
static bool string_list_capacity(struct string_list *list, size_t cap) static bool string_list_capacity(struct string_list *list, size_t cap)
{ {
@ -85,7 +85,7 @@ static bool string_list_capacity(struct string_list *list, size_t cap)
* @list : pointer to string list object * @list : pointer to string list object
* *
* Frees a string list. * Frees a string list.
*/ **/
void string_list_free(struct string_list *list) void string_list_free(struct string_list *list)
{ {
if (!list) if (!list)
@ -113,8 +113,8 @@ bool string_list_deinitialize(struct string_list *list)
* *
* Creates a new string list. Has to be freed manually. * Creates a new string list. Has to be freed manually.
* *
* Returns: new string list if successful, otherwise NULL. * @return New string list if successful, otherwise NULL.
*/ **/
struct string_list *string_list_new(void) struct string_list *string_list_new(void)
{ {
struct string_list_elem * struct string_list_elem *
@ -164,7 +164,7 @@ bool string_list_initialize(struct string_list *list)
* *
* Appends a new element to the string list. * Appends a new element to the string list.
* *
* Returns: true (1) if successful, otherwise false (0). * @return true if successful, otherwise false.
**/ **/
bool string_list_append(struct string_list *list, const char *elem, bool string_list_append(struct string_list *list, const char *elem,
union string_list_elem_attr attr) union string_list_elem_attr attr)
@ -182,8 +182,7 @@ bool string_list_append(struct string_list *list, const char *elem,
(list->cap > 0) ? (list->cap * 2) : 32)) (list->cap > 0) ? (list->cap * 2) : 32))
return false; return false;
data_dup = strdup(elem); if (!(data_dup = strdup(elem)))
if (!data_dup)
return false; return false;
list->elems[list->size].data = data_dup; list->elems[list->size].data = data_dup;
@ -202,7 +201,7 @@ bool string_list_append(struct string_list *list, const char *elem,
* *
* Appends a new element to the string list. * Appends a new element to the string list.
* *
* Returns: true (1) if successful, otherwise false (0). * @return true if successful, otherwise false.
**/ **/
bool string_list_append_n(struct string_list *list, const char *elem, bool string_list_append_n(struct string_list *list, const char *elem,
unsigned length, union string_list_elem_attr attr) unsigned length, union string_list_elem_attr attr)
@ -213,9 +212,7 @@ bool string_list_append_n(struct string_list *list, const char *elem,
!string_list_capacity(list, list->cap * 2)) !string_list_capacity(list, list->cap * 2))
return false; return false;
data_dup = (char*)malloc(length + 1); if (!(data_dup = (char*)malloc(length + 1)))
if (!data_dup)
return false; return false;
strlcpy(data_dup, elem, length + 1); strlcpy(data_dup, elem, length + 1);
@ -251,7 +248,7 @@ void string_list_set(struct string_list *list,
* *
* A string list will be joined/concatenated as a * A string list will be joined/concatenated as a
* string to @buffer, delimited by @delim. * string to @buffer, delimited by @delim.
*/ **/
void string_list_join_concat(char *buffer, size_t size, void string_list_join_concat(char *buffer, size_t size,
const struct string_list *list, const char *delim) const struct string_list *list, const char *delim)
{ {
@ -261,7 +258,7 @@ void string_list_join_concat(char *buffer, size_t size,
/* If buffer is already 'full', nothing /* If buffer is already 'full', nothing
* further can be added * further can be added
* > This condition will also be triggered * > This condition will also be triggered
* if buffer is not NUL-terminated, * if buffer is not NULL-terminated,
* in which case any attempt to increment * in which case any attempt to increment
* buffer or decrement size would lead to * buffer or decrement size would lead to
* undefined behaviour */ * undefined behaviour */
@ -298,8 +295,7 @@ struct string_list *string_split(const char *str, const char *delim)
if (!list) if (!list)
return NULL; return NULL;
copy = strdup(str); if (!(copy = strdup(str)))
if (!copy)
goto error; goto error;
tmp = strtok_r(copy, delim, &save); tmp = strtok_r(copy, delim, &save);
@ -334,8 +330,7 @@ bool string_split_noalloc(struct string_list *list,
if (!list) if (!list)
return false; return false;
copy = strdup(str); if (!(copy = strdup(str)))
if (!copy)
return false; return false;
tmp = strtok_r(copy, delim, &save); tmp = strtok_r(copy, delim, &save);
@ -367,8 +362,8 @@ bool string_split_noalloc(struct string_list *list,
* Includes empty strings - i.e. two adjacent delimiters will resolve * Includes empty strings - i.e. two adjacent delimiters will resolve
* to a string list element of "". * to a string list element of "".
* *
* Returns: new string list if successful, otherwise NULL. * @return New string list if successful, otherwise NULL.
*/ **/
struct string_list *string_separate(char *str, const char *delim) struct string_list *string_separate(char *str, const char *delim)
{ {
char *token = NULL; char *token = NULL;
@ -377,15 +372,13 @@ struct string_list *string_separate(char *str, const char *delim)
/* Sanity check */ /* Sanity check */
if (!str || string_is_empty(delim)) if (!str || string_is_empty(delim))
goto error; return NULL;
if (!(list = string_list_new()))
return NULL;
str_ptr = &str; str_ptr = &str;
list = string_list_new(); token = string_tokenize(str_ptr, delim);
if (!list)
goto error;
token = string_tokenize(str_ptr, delim);
while (token) while (token)
{ {
union string_list_elem_attr attr; union string_list_elem_attr attr;
@ -393,22 +386,17 @@ struct string_list *string_separate(char *str, const char *delim)
attr.i = 0; attr.i = 0;
if (!string_list_append(list, token, attr)) if (!string_list_append(list, token, attr))
goto error; {
free(token);
string_list_free(list);
return NULL;
}
free(token); free(token);
token = NULL;
token = string_tokenize(str_ptr, delim); token = string_tokenize(str_ptr, delim);
} }
return list; return list;
error:
if (token)
free(token);
if (list)
string_list_free(list);
return NULL;
} }
bool string_separate_noalloc( bool string_separate_noalloc(
@ -446,101 +434,97 @@ bool string_separate_noalloc(
/** /**
* string_list_find_elem: * string_list_find_elem:
* @list : pointer to string list *
* @elem : element to find inside the string list. * @param list
* Pointer to string list
* @param elem
* Element to find inside the string list.
* *
* Searches for an element (@elem) inside the string list. * Searches for an element (@elem) inside the string list.
* *
* Returns: true (1) if element could be found, otherwise false (0). * @return Number of elements found, otherwise 0.
*/ */
int string_list_find_elem(const struct string_list *list, const char *elem) int string_list_find_elem(const struct string_list *list, const char *elem)
{ {
size_t i; if (list)
if (!list)
return false;
for (i = 0; i < list->size; i++)
{ {
if (string_is_equal_noncase(list->elems[i].data, elem)) size_t i;
return (int)(i + 1); for (i = 0; i < list->size; i++)
{
if (string_is_equal_noncase(list->elems[i].data, elem))
return (int)(i + 1);
}
} }
return 0;
return false;
} }
/** /**
* string_list_find_elem_prefix: * string_list_find_elem_prefix:
* @list : pointer to string list *
* @prefix : prefix to append to @elem * @param list
* @elem : element to find inside the string list. * Pointer to string list
* @param prefix
* Prefix to append to @elem
* @param elem
* Element to find inside the string list.
* *
* Searches for an element (@elem) inside the string list. Will * Searches for an element (@elem) inside the string list. Will
* also search for the same element prefixed by @prefix. * also search for the same element prefixed by @prefix.
* *
* Returns: true (1) if element could be found, otherwise false (0). * @return true if element could be found, otherwise false.
*/ */
bool string_list_find_elem_prefix(const struct string_list *list, bool string_list_find_elem_prefix(const struct string_list *list,
const char *prefix, const char *elem) const char *prefix, const char *elem)
{ {
size_t i; if (list)
char prefixed[255];
if (!list)
return false;
prefixed[0] = '\0';
strlcpy(prefixed, prefix, sizeof(prefixed));
strlcat(prefixed, elem, sizeof(prefixed));
for (i = 0; i < list->size; i++)
{ {
if (string_is_equal_noncase(list->elems[i].data, elem) || size_t i;
string_is_equal_noncase(list->elems[i].data, prefixed)) char prefixed[255];
return true; size_t _len = strlcpy(prefixed, prefix, sizeof(prefixed));
strlcpy(prefixed + _len, elem, sizeof(prefixed) - _len);
for (i = 0; i < list->size; i++)
{
if ( string_is_equal_noncase(list->elems[i].data, elem)
|| string_is_equal_noncase(list->elems[i].data, prefixed))
return true;
}
} }
return false; return false;
} }
struct string_list *string_list_clone( struct string_list *string_list_clone(const struct string_list *src)
const struct string_list *src)
{ {
unsigned i; size_t i;
struct string_list_elem struct string_list_elem *elems = NULL;
*elems = NULL; struct string_list *dest = (struct string_list*)
struct string_list
*dest = (struct string_list*)
malloc(sizeof(struct string_list)); malloc(sizeof(struct string_list));
if (!dest) if (!dest)
return NULL; return NULL;
dest->elems = NULL; dest->elems = NULL;
dest->size = src->size; dest->size = src->size;
dest->cap = src->cap; if (src->cap < dest->size)
if (dest->cap < dest->size) dest->cap = dest->size;
dest->cap = dest->size; else
dest->cap = src->cap;
elems = (struct string_list_elem*) if (!(elems = (struct string_list_elem*)
calloc(dest->cap, sizeof(struct string_list_elem)); calloc(dest->cap, sizeof(struct string_list_elem))))
if (!elems)
{ {
free(dest); free(dest);
return NULL; return NULL;
} }
dest->elems = elems; dest->elems = elems;
for (i = 0; i < src->size; i++) for (i = 0; i < src->size; i++)
{ {
const char *_src = src->elems[i].data; const char *_src = src->elems[i].data;
size_t len = _src ? strlen(_src) : 0; size_t len = _src ? strlen(_src) : 0;
dest->elems[i].data = NULL; dest->elems[i].data = NULL;
dest->elems[i].attr = src->elems[i].attr; dest->elems[i].attr = src->elems[i].attr;
if (len != 0) if (len != 0)
{ {

View File

@ -25,7 +25,6 @@
#include <string.h> #include <string.h>
#include <stdarg.h> #include <stdarg.h>
#include <ctype.h> #include <ctype.h>
#include <errno.h>
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" #include "config.h"
@ -46,7 +45,6 @@ struct RFILE
{ {
struct retro_vfs_file_handle *hfile; struct retro_vfs_file_handle *hfile;
bool error_flag; bool error_flag;
bool eof_flag;
}; };
static retro_vfs_get_path_t filestream_get_path_cb = NULL; static retro_vfs_get_path_t filestream_get_path_cb = NULL;
@ -109,18 +107,14 @@ bool filestream_exists(const char *path)
if (!path || !*path) if (!path || !*path)
return false; return false;
if (!(dummy = filestream_open(
dummy = filestream_open(
path, path,
RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_READ,
RETRO_VFS_FILE_ACCESS_HINT_NONE); RETRO_VFS_FILE_ACCESS_HINT_NONE)))
if (!dummy)
return false; return false;
if (filestream_close(dummy) != 0) if (filestream_close(dummy) != 0)
if (dummy) free(dummy);
free(dummy);
dummy = NULL; dummy = NULL;
return true; return true;
@ -165,7 +159,7 @@ int64_t filestream_truncate(RFILE *stream, int64_t length)
* @hints : * @hints :
* *
* Opens a file for reading or writing, depending on the requested mode. * Opens a file for reading or writing, depending on the requested mode.
* Returns a pointer to an RFILE if opened successfully, otherwise NULL. * @return A pointer to an RFILE if opened successfully, otherwise NULL.
**/ **/
RFILE* filestream_open(const char *path, unsigned mode, unsigned hints) RFILE* filestream_open(const char *path, unsigned mode, unsigned hints)
{ {
@ -184,7 +178,6 @@ RFILE* filestream_open(const char *path, unsigned mode, unsigned hints)
output = (RFILE*)malloc(sizeof(RFILE)); output = (RFILE*)malloc(sizeof(RFILE));
output->error_flag = false; output->error_flag = false;
output->eof_flag = false;
output->hfile = fp; output->hfile = fp;
return output; return output;
} }
@ -221,12 +214,12 @@ int filestream_getc(RFILE *stream)
return EOF; return EOF;
} }
int filestream_scanf(RFILE *stream, const char* format, ...) int filestream_vscanf(RFILE *stream, const char* format, va_list *args)
{ {
char buf[4096]; char buf[4096];
char subfmt[64]; char subfmt[64];
va_list args; va_list args_copy;
const char * bufiter = buf; const char *bufiter = buf;
int ret = 0; int ret = 0;
int64_t startpos = filestream_tell(stream); int64_t startpos = filestream_tell(stream);
int64_t maxlen = filestream_read(stream, buf, sizeof(buf)-1); int64_t maxlen = filestream_read(stream, buf, sizeof(buf)-1);
@ -236,7 +229,16 @@ int filestream_scanf(RFILE *stream, const char* format, ...)
buf[maxlen] = '\0'; buf[maxlen] = '\0';
va_start(args, format); /* Have to copy the input va_list here
* > Calling va_arg() on 'args' directly would
* cause the va_list to have an indeterminate value
* in the function calling filestream_vscanf(),
* leading to unexpected behaviour */
#ifdef __va_copy
__va_copy(args_copy, *args);
#else
va_copy(args_copy, *args);
#endif
while (*format) while (*format)
{ {
@ -302,7 +304,7 @@ int filestream_scanf(RFILE *stream, const char* format, ...)
} }
else else
{ {
int v = sscanf(bufiter, subfmt, va_arg(args, void*), &sublen); int v = sscanf(bufiter, subfmt, va_arg(args_copy, void*), &sublen);
if (v == EOF) if (v == EOF)
return EOF; return EOF;
if (v != 1) if (v != 1)
@ -327,13 +329,23 @@ int filestream_scanf(RFILE *stream, const char* format, ...)
} }
} }
va_end(args); va_end(args_copy);
filestream_seek(stream, startpos+(bufiter-buf), filestream_seek(stream, startpos + (bufiter - buf),
RETRO_VFS_SEEK_POSITION_START); RETRO_VFS_SEEK_POSITION_START);
return ret; return ret;
} }
int filestream_scanf(RFILE *stream, const char* format, ...)
{
int result;
va_list vl;
va_start(vl, format);
result = filestream_vscanf(stream, format, &vl);
va_end(vl);
return result;
}
int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position) int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position)
{ {
int64_t output; int64_t output;
@ -348,14 +360,12 @@ int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position)
if (output == VFS_ERROR_RETURN_VALUE) if (output == VFS_ERROR_RETURN_VALUE)
stream->error_flag = true; stream->error_flag = true;
stream->eof_flag = false;
return output; return output;
} }
int filestream_eof(RFILE *stream) int filestream_eof(RFILE *stream)
{ {
return stream->eof_flag; return filestream_tell(stream) == filestream_get_size(stream) ? EOF : 0;
} }
int64_t filestream_tell(RFILE *stream) int64_t filestream_tell(RFILE *stream)
@ -380,7 +390,6 @@ void filestream_rewind(RFILE *stream)
return; return;
filestream_seek(stream, 0L, RETRO_VFS_SEEK_POSITION_START); filestream_seek(stream, 0L, RETRO_VFS_SEEK_POSITION_START);
stream->error_flag = false; stream->error_flag = false;
stream->eof_flag = false;
} }
int64_t filestream_read(RFILE *stream, void *s, int64_t len) int64_t filestream_read(RFILE *stream, void *s, int64_t len)
@ -395,8 +404,6 @@ int64_t filestream_read(RFILE *stream, void *s, int64_t len)
if (output == VFS_ERROR_RETURN_VALUE) if (output == VFS_ERROR_RETURN_VALUE)
stream->error_flag = true; stream->error_flag = true;
if (output < len)
stream->eof_flag = true;
return output; return output;
} }
@ -494,9 +501,7 @@ int filestream_printf(RFILE *stream, const char* format, ...)
int filestream_error(RFILE *stream) int filestream_error(RFILE *stream)
{ {
if (stream && stream->error_flag) return (stream && stream->error_flag);
return 1;
return 0;
} }
int filestream_close(RFILE *stream) int filestream_close(RFILE *stream)
@ -521,10 +526,11 @@ int filestream_close(RFILE *stream)
* @path : path to file. * @path : path to file.
* @buf : buffer to allocate and read the contents of the * @buf : buffer to allocate and read the contents of the
* file into. Needs to be freed manually. * file into. Needs to be freed manually.
* @len : optional output integer containing bytes read.
* *
* Read the contents of a file into @buf. * Read the contents of a file into @buf.
* *
* Returns: number of items read, -1 on error. * @return Non-zero on success.
*/ */
int64_t filestream_read_file(const char *path, void **buf, int64_t *len) int64_t filestream_read_file(const char *path, void **buf, int64_t *len)
{ {
@ -541,25 +547,20 @@ int64_t filestream_read_file(const char *path, void **buf, int64_t *len)
return 0; return 0;
} }
content_buf_size = filestream_get_size(file); if ((content_buf_size = filestream_get_size(file)) < 0)
if (content_buf_size < 0)
goto error; goto error;
content_buf = malloc((size_t)(content_buf_size + 1)); if (!(content_buf = malloc((size_t)(content_buf_size + 1))))
if (!content_buf)
goto error; goto error;
if ((int64_t)(uint64_t)(content_buf_size + 1) != (content_buf_size + 1)) if ((int64_t)(uint64_t)(content_buf_size + 1) != (content_buf_size + 1))
goto error; goto error;
ret = filestream_read(file, content_buf, (int64_t)content_buf_size); if ((ret = filestream_read(file, content_buf, (int64_t)content_buf_size)) <
if (ret < 0) 0)
goto error; goto error;
if (filestream_close(file) != 0) if (filestream_close(file) != 0)
if (file) free(file);
free(file);
*buf = content_buf; *buf = content_buf;
@ -573,9 +574,8 @@ int64_t filestream_read_file(const char *path, void **buf, int64_t *len)
return 1; return 1;
error: error:
if (file) if (filestream_close(file) != 0)
if (filestream_close(file) != 0) free(file);
free(file);
if (content_buf) if (content_buf)
free(content_buf); free(content_buf);
if (len) if (len)
@ -592,8 +592,8 @@ error:
* *
* Writes data to a file. * Writes data to a file.
* *
* Returns: true (1) on success, false (0) otherwise. * @return true on success, otherwise false.
*/ **/
bool filestream_write_file(const char *path, const void *data, int64_t size) bool filestream_write_file(const char *path, const void *data, int64_t size)
{ {
int64_t ret = 0; int64_t ret = 0;
@ -602,20 +602,18 @@ bool filestream_write_file(const char *path, const void *data, int64_t size)
RETRO_VFS_FILE_ACCESS_HINT_NONE); RETRO_VFS_FILE_ACCESS_HINT_NONE);
if (!file) if (!file)
return false; return false;
ret = filestream_write(file, data, size); ret = filestream_write(file, data, size);
if (filestream_close(file) != 0) if (filestream_close(file) != 0)
if (file) free(file);
free(file); return (ret == size);
if (ret != size)
return false;
return true;
} }
/* Returned pointer must be freed by the caller. */ /**
char* filestream_getline(RFILE *stream) * filestream_getline:
*
* Returned pointer must be freed by the caller.
**/
char *filestream_getline(RFILE *stream)
{ {
char *newline_tmp = NULL; char *newline_tmp = NULL;
size_t cur_size = 8; size_t cur_size = 8;
@ -630,16 +628,15 @@ char* filestream_getline(RFILE *stream)
return NULL; return NULL;
} }
in = filestream_getc(stream); in = filestream_getc(stream);
while (in != EOF && in != '\n') while (in != EOF && in != '\n')
{ {
if (idx == cur_size) if (idx == cur_size)
{ {
cur_size *= 2; cur_size *= 2;
newline_tmp = (char*)realloc(newline, cur_size + 1);
if (!newline_tmp) if (!(newline_tmp = (char*)realloc(newline, cur_size + 1)))
{ {
free(newline); free(newline);
return NULL; return NULL;

View File

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

View File

@ -22,7 +22,9 @@
#include <stdint.h> #include <stdint.h>
#include <ctype.h> #include <ctype.h>
#include <string.h>
#include <compat/strl.h>
#include <string/stdstring.h> #include <string/stdstring.h>
#include <encodings/utf.h> #include <encodings/utf.h>
@ -88,9 +90,11 @@ char *string_ucwords(char *s)
} }
char *string_replace_substring(const char *in, char *string_replace_substring(const char *in,
const char *pattern, const char *replacement) const char *pattern, size_t pattern_len,
const char *replacement, size_t replacement_len)
{ {
size_t numhits, pattern_len, replacement_len, outlen; size_t outlen;
size_t numhits = 0;
const char *inat = NULL; const char *inat = NULL;
const char *inprev = NULL; const char *inprev = NULL;
char *out = NULL; char *out = NULL;
@ -101,9 +105,6 @@ char *string_replace_substring(const char *in,
if (!pattern || !replacement) if (!pattern || !replacement)
return strdup(in); return strdup(in);
pattern_len = strlen(pattern);
replacement_len = strlen(replacement);
numhits = 0;
inat = in; inat = in;
while ((inat = strstr(inat, pattern))) while ((inat = strstr(inat, pattern)))
@ -113,9 +114,8 @@ char *string_replace_substring(const char *in,
} }
outlen = strlen(in) - pattern_len*numhits + replacement_len*numhits; outlen = strlen(in) - pattern_len*numhits + replacement_len*numhits;
out = (char *)malloc(outlen+1);
if (!out) if (!(out = (char *)malloc(outlen+1)))
return NULL; return NULL;
outat = out; outat = out;
@ -128,7 +128,7 @@ char *string_replace_substring(const char *in,
outat += inat-inprev; outat += inat-inprev;
memcpy(outat, replacement, replacement_len); memcpy(outat, replacement, replacement_len);
outat += replacement_len; outat += replacement_len;
inat += pattern_len; inat += pattern_len;
inprev = inat; inprev = inat;
} }
strcpy(outat, inprev); strcpy(outat, inprev);
@ -136,7 +136,11 @@ char *string_replace_substring(const char *in,
return out; return out;
} }
/* Remove leading whitespaces */ /**
* string_trim_whitespace_left:
*
* Remove leading whitespaces
**/
char *string_trim_whitespace_left(char *const s) char *string_trim_whitespace_left(char *const s)
{ {
if (s && *s) if (s && *s)
@ -157,7 +161,11 @@ char *string_trim_whitespace_left(char *const s)
return s; return s;
} }
/* Remove trailing whitespaces */ /**
* string_trim_whitespace_right:
*
* Remove trailing whitespaces
**/
char *string_trim_whitespace_right(char *const s) char *string_trim_whitespace_right(char *const s)
{ {
if (s && *s) if (s && *s)
@ -177,7 +185,11 @@ char *string_trim_whitespace_right(char *const s)
return s; return s;
} }
/* Remove leading and trailing whitespaces */ /**
* string_trim_whitespace:
*
* Remove leading and trailing whitespaces
**/
char *string_trim_whitespace(char *const s) char *string_trim_whitespace(char *const s)
{ {
string_trim_whitespace_right(s); /* order matters */ string_trim_whitespace_right(s); /* order matters */
@ -186,94 +198,249 @@ char *string_trim_whitespace(char *const s)
return s; return s;
} }
char *word_wrap(char* buffer, const char *string, int line_width, bool unicode, unsigned max_lines) /**
* word_wrap:
* @dst : pointer to destination buffer.
* @dst_size : size of destination buffer.
* @src : pointer to input string.
* @line_width : max number of characters per line.
* @wideglyph_width : not used, but is necessary to keep
* compatibility with word_wrap_wideglyph().
* @max_lines : max lines of destination string.
* 0 means no limit.
*
* Wraps string specified by 'src' to destination buffer
* specified by 'dst' and 'dst_size'.
* This function assumes that all glyphs in the string
* have an on-screen pixel width similar to that of
* regular Latin characters - i.e. it will not wrap
* correctly any text containing so-called 'wide' Unicode
* characters (e.g. CJK languages, emojis, etc.).
**/
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)
{ {
unsigned i = 0; char *last_space = NULL;
unsigned len = (unsigned)strlen(string); unsigned counter = 0;
unsigned lines = 1; unsigned lines = 1;
const char *src_end = src + src_len;
while (i < len) /* Prevent buffer overflow */
if (dst_size < src_len + 1)
return 0;
/* Early return if src string length is less
* than line width */
if (src_len < (size_t)line_width)
return strlcpy(dst, src, dst_size);
while (*src != '\0')
{ {
unsigned counter; unsigned char_len = (unsigned)(utf8skip(src, 1) - src);
int pos = (int)(&buffer[i] - buffer); counter++;
/* copy string until the end of the line is reached */ if (*src == ' ')
for (counter = 1; counter <= (unsigned)line_width; counter++) last_space = dst; /* Remember the location of the whitespace */
else if (*src == '\n')
{ {
const char *character; /* If newlines embedded in the input,
unsigned char_len; * reset the index */
unsigned j = i; lines++;
counter = 0;
/* check if end of string reached */ /* Early return if remaining src string
if (i == len) * length is less than line width */
{ if (src_end - src <= line_width)
buffer[i] = 0; return strlcpy(dst, src, dst_size);
return buffer; }
}
character = utf8skip(&string[i], 1); while (char_len--)
char_len = (unsigned)(character - &string[i]); *dst++ = *src++;
if (!unicode) if (counter >= (unsigned)line_width)
counter += char_len - 1;
do
{
buffer[i] = string[i];
char_len--;
i++;
} while (char_len);
/* check for newlines embedded in the original input
* and reset the index */
if (buffer[j] == '\n')
{
lines++;
counter = 1;
}
}
/* check for whitespace */
if (string[i] == ' ')
{ {
if ((max_lines == 0 || lines < max_lines)) counter = 0;
if (last_space && (max_lines == 0 || lines < max_lines))
{ {
buffer[i] = '\n'; /* Replace nearest (previous) whitespace
i++; * with newline character */
*last_space = '\n';
lines++; lines++;
src -= dst - last_space - 1;
dst = last_space + 1;
last_space = NULL;
/* Early return if remaining src string
* length is less than line width */
if (src_end - src < line_width)
return strlcpy(dst, src, dst_size);
} }
} }
else
{
int k;
/* check for nearest whitespace back in string */
for (k = i; k > 0; k--)
{
if (string[k] != ' ' || (max_lines != 0 && lines >= max_lines))
continue;
buffer[k] = '\n';
/* set string index back to character after this one */
i = k + 1;
lines++;
break;
}
if (&buffer[i] - buffer == pos)
return buffer;
}
} }
buffer[i] = 0; *dst = '\0';
return 0;
return buffer;
} }
/* Splits string into tokens seperated by 'delim' /**
* word_wrap_wideglyph:
* @dst : pointer to destination buffer.
* @dst_size : size of destination buffer.
* @src : pointer to input string.
* @line_width : max number of characters per line.
* @wideglyph_width : effective width of 'wide' Unicode glyphs.
* the value here is normalised relative to the
* typical on-screen pixel width of a regular
* Latin character:
* - a regular Latin character is defined to
* have an effective width of 100
* - wideglyph_width = 100 * (wide_character_pixel_width / latin_character_pixel_width)
* - e.g. if 'wide' Unicode characters in 'src'
* have an on-screen pixel width twice that of
* regular Latin characters, wideglyph_width
* would be 200
* @max_lines : max lines of destination string.
* 0 means no limit.
*
* Wraps string specified by @src to destination buffer
* specified by @dst and @dst_size.
* This function assumes that all glyphs in the string
* are:
* - EITHER 'non-wide' Unicode glyphs, with an on-screen
* pixel width similar to that of regular Latin characters
* - OR 'wide' Unicode glyphs (e.g. CJK languages, emojis, etc.)
* with an on-screen pixel width defined by @wideglyph_width
* Note that wrapping may occur in inappropriate locations
* if @src string contains 'wide' Unicode characters whose
* on-screen pixel width deviates greatly from the set
* @wideglyph_width value.
**/
size_t word_wrap_wideglyph(char *dst, size_t dst_size,
const char *src, size_t src_len, int line_width,
int wideglyph_width, unsigned max_lines)
{
char *lastspace = NULL;
char *lastwideglyph = NULL;
const char *src_end = src + src_len;
unsigned lines = 1;
/* 'line_width' means max numbers of characters per line,
* but this metric is only meaningful when dealing with
* 'regular' glyphs that have an on-screen pixel width
* similar to that of regular Latin characters.
* When handing so-called 'wide' Unicode glyphs, it is
* necessary to consider the actual on-screen pixel width
* of each character.
* In order to do this, we create a distinction between
* regular Latin 'non-wide' glyphs and 'wide' glyphs, and
* normalise all values relative to the on-screen pixel
* width of regular Latin characters:
* - Regular 'non-wide' glyphs have a normalised width of 100
* - 'line_width' is therefore normalised to 100 * (width_in_characters)
* - 'wide' glyphs have a normalised width of
* 100 * (wide_character_pixel_width / latin_character_pixel_width)
* - When a character is detected, the position in the current
* line is incremented by the regular normalised width of 100
* - If that character is then determined to be a 'wide'
* glyph, the position in the current line is further incremented
* by the difference between the normalised 'wide' and 'non-wide'
* width values */
unsigned counter_normalized = 0;
int line_width_normalized = line_width * 100;
int additional_counter_normalized = wideglyph_width - 100;
/* Early return if src string length is less
* than line width */
if (src_end - src < line_width)
return strlcpy(dst, src, dst_size);
while (*src != '\0')
{
unsigned char_len = (unsigned)(utf8skip(src, 1) - src);
counter_normalized += 100;
/* Prevent buffer overflow */
if (char_len >= dst_size)
break;
if (*src == ' ')
lastspace = dst; /* Remember the location of the whitespace */
else if (*src == '\n')
{
/* If newlines embedded in the input,
* reset the index */
lines++;
counter_normalized = 0;
/* Early return if remaining src string
* length is less than line width */
if (src_end - src <= line_width)
return strlcpy(dst, src, dst_size);
}
else if (char_len >= 3)
{
/* Remember the location of the first byte
* whose length as UTF-8 >= 3*/
lastwideglyph = dst;
counter_normalized += additional_counter_normalized;
}
dst_size -= char_len;
while (char_len--)
*dst++ = *src++;
if (counter_normalized >= (unsigned)line_width_normalized)
{
counter_normalized = 0;
if (max_lines != 0 && lines >= max_lines)
continue;
else if (lastwideglyph && (!lastspace || lastwideglyph > lastspace))
{
/* Insert newline character */
*lastwideglyph = '\n';
lines++;
src -= dst - lastwideglyph;
dst = lastwideglyph + 1;
lastwideglyph = NULL;
/* Early return if remaining src string
* length is less than line width */
if (src_end - src <= line_width)
return strlcpy(dst, src, dst_size);
}
else if (lastspace)
{
/* Replace nearest (previous) whitespace
* with newline character */
*lastspace = '\n';
lines++;
src -= dst - lastspace - 1;
dst = lastspace + 1;
lastspace = NULL;
/* Early return if remaining src string
* length is less than line width */
if (src_end - src < line_width)
return strlcpy(dst, src, dst_size);
}
}
}
*dst = '\0';
return 0;
}
/**
* string_tokenize:
*
* Splits string into tokens seperated by @delim
* > Returned token string must be free()'d * > Returned token string must be free()'d
* > Returns NULL if token is not found * > Returns NULL if token is not found
* > After each call, 'str' is set to the position after the * > After each call, @str is set to the position after the
* last found token * last found token
* > Tokens *include* empty strings * > Tokens *include* empty strings
* Usage example: * Usage example:
@ -286,7 +453,7 @@ char *word_wrap(char* buffer, const char *string, int line_width, bool unicode,
* free(token); * free(token);
* token = NULL; * token = NULL;
* } * }
*/ **/
char* string_tokenize(char **str, const char *delim) char* string_tokenize(char **str, const char *delim)
{ {
/* Taken from https://codereview.stackexchange.com/questions/216956/strtok-function-thread-safe-supports-empty-tokens-doesnt-change-string# */ /* Taken from https://codereview.stackexchange.com/questions/216956/strtok-function-thread-safe-supports-empty-tokens-doesnt-change-string# */
@ -299,25 +466,20 @@ char* string_tokenize(char **str, const char *delim)
if (!str || string_is_empty(delim)) if (!str || string_is_empty(delim))
return NULL; return NULL;
str_ptr = *str;
/* Note: we don't check string_is_empty() here, /* Note: we don't check string_is_empty() here,
* empty strings are valid */ * empty strings are valid */
if (!str_ptr) if (!(str_ptr = *str))
return NULL; return NULL;
/* Search for delimiter */ /* Search for delimiter */
delim_ptr = strstr(str_ptr, delim); if ((delim_ptr = strstr(str_ptr, delim)))
if (delim_ptr)
token_len = delim_ptr - str_ptr; token_len = delim_ptr - str_ptr;
else else
token_len = strlen(str_ptr); token_len = strlen(str_ptr);
/* Allocate token string */ /* Allocate token string */
token = (char *)malloc((token_len + 1) * sizeof(char)); if (!(token = (char *)malloc((token_len + 1) * sizeof(char))))
if (!token)
return NULL; return NULL;
/* Copy token */ /* Copy token */
@ -330,42 +492,53 @@ char* string_tokenize(char **str, const char *delim)
return token; return token;
} }
/* Removes every instance of character 'c' from 'str' */ /**
* string_remove_all_chars:
* @str : input string (must be non-NULL, otherwise UB)
*
* Leaf function.
*
* Removes every instance of character @c from @str
**/
void string_remove_all_chars(char *str, char c) void string_remove_all_chars(char *str, char c)
{ {
char *read_ptr = NULL; char *read_ptr = str;
char *write_ptr = NULL; char *write_ptr = str;
if (string_is_empty(str))
return;
read_ptr = str;
write_ptr = str;
while (*read_ptr != '\0') while (*read_ptr != '\0')
{ {
*write_ptr = *read_ptr++; *write_ptr = *read_ptr++;
write_ptr += (*write_ptr != c) ? 1 : 0; if (*write_ptr != c)
write_ptr++;
} }
*write_ptr = '\0'; *write_ptr = '\0';
} }
/* Replaces every instance of character 'find' in 'str' /**
* with character 'replace' */ * string_replace_all_chars:
* @str : input string (must be non-NULL, otherwise UB)
* @find : character to find
* @replace : character to replace @find with
*
* Replaces every instance of character @find in @str
* with character @replace
**/
void string_replace_all_chars(char *str, char find, char replace) void string_replace_all_chars(char *str, char find, char replace)
{ {
char *str_ptr = str; char *str_ptr = str;
if (string_is_empty(str))
return;
while ((str_ptr = strchr(str_ptr, find))) while ((str_ptr = strchr(str_ptr, find)))
*str_ptr++ = replace; *str_ptr++ = replace;
} }
/* Converts string to unsigned integer. /**
* 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) unsigned string_to_unsigned(const char *str)
{ {
const char *ptr = NULL; const char *ptr = NULL;
@ -382,27 +555,33 @@ unsigned string_to_unsigned(const char *str)
return (unsigned)strtoul(str, NULL, 10); return (unsigned)strtoul(str, NULL, 10);
} }
/* Converts hexadecimal string to unsigned integer. /**
* string_hex_to_unsigned:
* @str : input string (must be non-NULL, otherwise UB)
*
* Converts hexadecimal string to unsigned integer.
* Handles optional leading '0x'. * Handles optional leading '0x'.
* Returns 0 if string is invalid */ *
* @return 0 if string is invalid, otherwise > 0
**/
unsigned string_hex_to_unsigned(const char *str) unsigned string_hex_to_unsigned(const char *str)
{ {
const char *hex_str = str; const char *hex_str = str;
const char *ptr = NULL; const char *ptr = NULL;
size_t len;
if (string_is_empty(str))
return 0;
/* Remove leading '0x', if required */ /* Remove leading '0x', if required */
len = strlen(str); if (str[0] != '\0' && str[1] != '\0')
{
if (len >= 2) if ( (str[0] == '0') &&
if ((str[0] == '0') && ((str[1] == 'x') ||
((str[1] == 'x') || (str[1] == 'X'))) (str[1] == 'X')))
{
hex_str = str + 2; hex_str = str + 2;
if (string_is_empty(hex_str))
if (string_is_empty(hex_str)) return 0;
}
}
else
return 0; return 0;
/* Check for valid characters */ /* Check for valid characters */
@ -414,3 +593,117 @@ unsigned string_hex_to_unsigned(const char *str)
return (unsigned)strtoul(hex_str, NULL, 16); return (unsigned)strtoul(hex_str, NULL, 16);
} }
/**
* string_count_occurrences_single_character:
*
* Leaf function.
*
* Get the total number of occurrences of character @c in @str.
*
* @return Total number of occurrences of character @c
*/
int string_count_occurrences_single_character(const char *str, char c)
{
int count = 0;
for (; *str; str++)
if (*str == c)
count++;
return count;
}
/**
* string_replace_whitespace_with_single_character:
*
* Leaf function.
*
* Replaces all spaces with given character @c.
**/
void string_replace_whitespace_with_single_character(char *str, char c)
{
for (; *str; str++)
if (ISSPACE(*str))
*str = c;
}
/**
* string_replace_multi_space_with_single_space:
*
* Leaf function.
*
* Replaces multiple spaces with a single space in a string.
**/
void string_replace_multi_space_with_single_space(char *str)
{
char *str_trimmed = str;
bool prev_is_space = false;
bool curr_is_space = false;
for (; *str; str++)
{
curr_is_space = ISSPACE(*str);
if (prev_is_space && curr_is_space)
continue;
*str_trimmed++ = *str;
prev_is_space = curr_is_space;
}
*str_trimmed = '\0';
}
/**
* string_remove_all_whitespace:
*
* Leaf function.
*
* Remove all spaces from the given string.
**/
void string_remove_all_whitespace(char *str_trimmed, const char *str)
{
for (; *str; str++)
if (!ISSPACE(*str))
*str_trimmed++ = *str;
*str_trimmed = '\0';
}
/**
* Retrieve the last occurance of the given character in a string.
*/
int string_index_last_occurance(const char *str, char c)
{
const char *pos = strrchr(str, c);
if (pos)
return (int)(pos - str);
return -1;
}
/**
* string_find_index_substring_string:
* @str : input string (must be non-NULL, otherwise UB)
* @substr : substring to find in @str
*
* Find the position of substring @substr in string @str.
**/
int string_find_index_substring_string(const char *str, const char *substr)
{
const char *pos = strstr(str, substr);
if (pos)
return (int)(pos - str);
return -1;
}
/**
* string_copy_only_ascii:
*
* Leaf function.
*
* Strips non-ASCII characters from a string.
**/
void string_copy_only_ascii(char *str_stripped, const char *str)
{
for (; *str; str++)
if (*str > 0x1F && *str < 0x7F)
*str_stripped++ = *str;
*str_stripped = '\0';
}

View File

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

View File

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

File diff suppressed because it is too large Load Diff