LDFLAGS += `pkg-config --libs glib-2.0 libosso dbus-1 hildon-fm-2`
endif
ifeq "$(PLATFORM)" "libretro"
+ifeq "$(USE_LIBRETRO_VFS)" "1"
+OBJS += libretro-common/compat/compat_posix_string.o
OBJS += libretro-common/compat/fopen_utf8.o
OBJS += libretro-common/encodings/compat_strl.o
OBJS += libretro-common/encodings/encoding_utf.o
+OBJS += libretro-common/file/file_path.o
+OBJS += libretro-common/streams/file_stream.o
+OBJS += libretro-common/streams/file_stream_transforms.o
+OBJS += libretro-common/string/stdstring.o
+OBJS += libretro-common/time/rtime.o
+OBJS += libretro-common/vfs/vfs_implementation.o
+CFLAGS += -DUSE_LIBRETRO_VFS
+endif
OBJS += frontend/libretro.o
CFLAGS += -Ilibretro-common/include
CFLAGS += -DFRONTEND_SUPPORTS_RGB565
DEBUG ?= 0
WANT_ZLIB ?= 1
HAVE_CHD ?= 1
+USE_LIBRETRO_VFS ?= 0
# Dynarec options: lightrec, ari64
DYNAREC ?= lightrec
LIBPTHREAD :=
LIBDL :=
LIBM :=
+ USE_LIBRETRO_VFS = 1
endif
CFLAGS += $(fpic)
typedef int16_t INT16;
typedef int8_t INT8;
-#define core_file FILE
-#ifdef HAVE_LIBRETRO
-#include <compat/fopen_utf8.h>
-#define core_fopen(file) fopen_utf8(file, "rb")
+#ifdef USE_LIBRETRO_VFS
+#define core_file RFILE
+#define core_fopen(file) rfopen(file, "rb")
+#define core_fseek rfseek
+#define core_ftell rftell
+#define core_fread(fc, buff, len) rfread(buff, 1, len, fc)
+#define core_fclose rfclose
#else
+#define core_file FILE
#define core_fopen(file) fopen(file, "rb")
-#endif
#if defined(__WIN32__) || defined(_WIN32) || defined(WIN32) || defined(__WIN64__)
#define core_fseek _fseeki64
#define core_ftell _ftelli64
#endif
#define core_fread(fc, buff, len) fread(buff, 1, len, fc)
#define core_fclose fclose
+#endif
static UINT64 core_fsize(core_file *f)
{
#include "revision.h"
#include <libretro.h>
-#include <compat/fopen_utf8.h>
#include "libretro_core_options.h"
+#ifdef USE_LIBRETRO_VFS
+#include <streams/file_stream_transforms.h>
+#endif
+
#ifdef _3DS
#include "3ds/3ds_utils.h"
#endif
/* libretro */
void retro_set_environment(retro_environment_t cb)
{
+#ifdef USE_LIBRETRO_VFS
+ struct retro_vfs_interface_info vfs_iface_info;
+#endif
+
environ_cb = cb;
if (cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &logging))
environ_cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports);
libretro_set_core_options(environ_cb);
+
+#ifdef USE_LIBRETRO_VFS
+ vfs_iface_info.required_interface_version = 1;
+ vfs_iface_info.iface = NULL;
+ if (environ_cb(RETRO_ENVIRONMENT_GET_VFS_INTERFACE, &vfs_iface_info))
+ filestream_vfs_init(&vfs_iface_info);
+#endif
}
void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; }
{
char line[1024];
char name[PATH_MAX];
- FILE *f = fopen_utf8(file, "r");
- if (!f)
+ FILE *fp = fopen(file, "r");
+ if (!fp)
return false;
- while (fgets(line, sizeof(line), f) && disk_count < sizeof(disks) / sizeof(disks[0]))
+ while (fgets(fp, line, sizeof(line)) && disk_count < sizeof(disks) / sizeof(disks[0]))
{
if (line[0] == '#')
continue;
}
}
- fclose(f);
+ fclose(fp);
return (disk_count != 0);
}
static bool try_use_bios(const char *path)
{
- FILE *f;
long size;
const char *name;
-
- f = fopen_utf8(path, "rb");
- if (f == NULL)
+ FILE *fp = fopen(path, "rb");
+ if (fp == NULL)
return false;
- fseek(f, 0, SEEK_END);
- size = ftell(f);
- fclose(f);
+ fseek(fp, 0, SEEK_END);
+ size = ftell(fp);
+ fclose(fp);
if (size != 512 * 1024)
return false;
$(shell cd "$(LOCAL_PATH)" && (rm ../frontend/revision.h_))
HAVE_CHD ?= 1
+USE_LIBRETRO_VFS ?= 0
ROOT_DIR := $(LOCAL_PATH)/..
CORE_DIR := $(ROOT_DIR)/libpcsxcore
COREFLAGS := -ffast-math -funroll-loops -DHAVE_LIBRETRO -DNO_FRONTEND -DFRONTEND_SUPPORTS_RGB565 -DANDROID -DREARMED
COREFLAGS += -DHAVE_CHD -D_7ZIP_ST
+ifeq ($(USE_LIBRETRO_VFS),1)
+SOURCES_C += \
+ $(LIBRETRO_COMMON)/compat/compat_posix_string.c \
+ $(LIBRETRO_COMMON)/compat/fopen_utf8.c \
+ $(LIBRETRO_COMMON)/encodings/compat_strl.c \
+ $(LIBRETRO_COMMON)/encodings/encoding_utf.c \
+ $(LIBRETRO_COMMON)/file/file_path.c \
+ $(LIBRETRO_COMMON)/streams/file_stream.c \
+ $(LIBRETRO_COMMON)/streams/file_stream_transforms.c \
+ $(LIBRETRO_COMMON)/string/stdstring.c \
+ $(LIBRETRO_COMMON)/time/rtime.c \
+ $(LIBRETRO_COMMON)/vfs/vfs_implementation.c
+COREFLAGS += -DUSE_LIBRETRO_VFS
+endif
+
HAVE_ARI64=0
HAVE_LIGHTREC=0
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
***************************************************************************/
-#include <compat/fopen_utf8.h>
-
#include "psxcommon.h"
#include "plugins.h"
#include "cdrom.h"
#include <unistd.h>
#endif
+#ifdef USE_LIBRETRO_VFS
+#include <streams/file_stream_transforms.h>
+#undef fseeko
+#undef ftello
+#define ftello rftell
+#define fseeko rfseek
+#endif
#define OFF_T_MSB ((off_t)1 << (sizeof(off_t) * 8 - 1))
unsigned int cdrIsoMultidiskCount;
return -1;
}
- if ((fi = fopen_utf8(tocname, "r")) == NULL) {
+ if ((fi = fopen(tocname, "r")) == NULL) {
// try changing extension to .cue (to satisfy some stupid tutorials)
strcpy(tocname + strlen(tocname) - 4, ".cue");
- if ((fi = fopen_utf8(tocname, "r")) == NULL) {
+ if ((fi = fopen(tocname, "r")) == NULL) {
// if filename is image.toc.bin, try removing .bin (for Brasero)
strcpy(tocname, isofile);
t = strlen(tocname);
if (t >= 8 && strcmp(tocname + t - 8, ".toc.bin") == 0) {
tocname[t - 4] = '\0';
- if ((fi = fopen_utf8(tocname, "r")) == NULL) {
+ if ((fi = fopen(tocname, "r")) == NULL) {
return -1;
}
}
return -1;
}
- if ((fi = fopen_utf8(cuename, "r")) == NULL) {
+ if ((fi = fopen(cuename, "r")) == NULL) {
return -1;
}
else
tmp = tmpb;
strncpy(incue_fname, tmp, incue_max_len);
- ti[numtracks + 1].handle = fopen_utf8(filepath, "rb");
+ ti[numtracks + 1].handle = fopen(filepath, "rb");
// update global offset if this is not first file in this .cue
if (numtracks + 1 > 1) {
strncasecmp(isofile + strlen(isofile) - 4, ".cd", 3) == 0)) {
// user selected .cue/.cdX as image file, use it's data track instead
fclose(cdHandle);
- cdHandle = fopen_utf8(filepath, "rb");
+ cdHandle = fopen(filepath, "rb");
}
}
}
return -1;
}
- if ((fi = fopen_utf8(ccdname, "r")) == NULL) {
+ if ((fi = fopen(ccdname, "r")) == NULL) {
return -1;
}
return -1;
}
- if ((fi = fopen_utf8(mdsname, "rb")) == NULL) {
+ if ((fi = fopen(mdsname, "rb")) == NULL) {
return -1;
}
return -1;
}
- subHandle = fopen_utf8(subname, "rb");
+ subHandle = fopen(subname, "rb");
if (subHandle == NULL) {
return -1;
}
return 0; // it's already open
}
- cdHandle = fopen_utf8(GetIsoFile(), "rb");
+ cdHandle = fopen(GetIsoFile(), "rb");
if (cdHandle == NULL) {
SysPrintf(_("Could't open '%s' for reading: %s\n"),
GetIsoFile(), strerror(errno));
p = alt_bin_filename + strlen(alt_bin_filename) - 4;
for (i = 0; i < sizeof(exts) / sizeof(exts[0]); i++) {
strcpy(p, exts[i]);
- tmpf = fopen_utf8(alt_bin_filename, "rb");
+ tmpf = fopen(alt_bin_filename, "rb");
if (tmpf != NULL)
break;
}
// make sure we have another handle open for cdda
if (numtracks > 1 && ti[1].handle == NULL) {
- ti[1].handle = fopen_utf8(bin_filename, "rb");
+ ti[1].handle = fopen(bin_filename, "rb");
}
cdda_cur_sector = 0;
cdda_file_offset = 0;
// TODO: Implement caches & cycle penalty.
-#include <compat/fopen_utf8.h>
-
#include "psxmem.h"
#include "psxmem_map.h"
#include "r3000a.h"
#include "memmap.h"
+#ifdef USE_LIBRETRO_VFS
+#include <streams/file_stream_transforms.h>
+#endif
+
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif
if (strcmp(Config.Bios, "HLE") != 0) {
sprintf(bios, "%s/%s", Config.BiosDir, Config.Bios);
- f = fopen_utf8(bios, "rb");
+ f = fopen(bios, "rb");
if (f == NULL) {
SysMessage(_("Could not open BIOS:\"%s\". Enabling HLE Bios!\n"), bios);
* 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307 USA. *
***************************************************************************/
-#include <compat/fopen_utf8.h>
-
/*
* SIO functions.
*/
#include "sio.h"
#include <sys/stat.h>
+#ifdef USE_LIBRETRO_VFS
+#include <streams/file_stream_transforms.h>
+#endif
+
// Status Flags
#define TX_RDY 0x0001
#define RX_RDY 0x0002
if (*str == 0)
return;
- f = fopen_utf8(str, "rb");
+ f = fopen(str, "rb");
if (f == NULL) {
SysPrintf(_("The memory card %s doesn't exist - creating it\n"), str);
CreateMcd(str);
- f = fopen_utf8(str, "rb");
+ f = fopen(str, "rb");
if (f != NULL) {
struct stat buf;
if (mcd == NULL || *mcd == 0 || strcmp(mcd, "none") == 0)
return;
- f = fopen_utf8(mcd, "r+b");
+ f = fopen(mcd, "r+b");
if (f != NULL) {
struct stat buf;
int s = MCD_SIZE;
int i = 0, j;
- f = fopen_utf8(mcd, "wb");
+ f = fopen(mcd, "wb");
if (f == NULL)
return;
--- /dev/null
+/* Copyright (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (compat_posix_string.c).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <ctype.h>
+
+#include <compat/posix_string.h>
+
+#ifdef _WIN32
+
+#undef strcasecmp
+#undef strdup
+#undef isblank
+#undef strtok_r
+#include <ctype.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <compat/strl.h>
+
+#include <string.h>
+
+int retro_strcasecmp__(const char *a, const char *b)
+{
+ while (*a && *b)
+ {
+ int a_ = tolower(*a);
+ int b_ = tolower(*b);
+
+ if (a_ != b_)
+ return a_ - b_;
+
+ a++;
+ b++;
+ }
+
+ return tolower(*a) - tolower(*b);
+}
+
+char *retro_strdup__(const char *orig)
+{
+ size_t len = strlen(orig) + 1;
+ char *ret = (char*)malloc(len);
+ if (!ret)
+ return NULL;
+
+ strlcpy(ret, orig, len);
+ return ret;
+}
+
+int retro_isblank__(int c)
+{
+ return (c == ' ') || (c == '\t');
+}
+
+char *retro_strtok_r__(char *str, const char *delim, char **saveptr)
+{
+ char *first = NULL;
+ if (!saveptr || !delim)
+ return NULL;
+
+ if (str)
+ *saveptr = str;
+
+ do
+ {
+ char *ptr = NULL;
+ first = *saveptr;
+ while (*first && strchr(delim, *first))
+ *first++ = '\0';
+
+ if (*first == '\0')
+ return NULL;
+
+ ptr = first + 1;
+
+ while (*ptr && !strchr(delim, *ptr))
+ ptr++;
+
+ *saveptr = ptr + (*ptr ? 1 : 0);
+ *ptr = '\0';
+ } while (strlen(first) == 0);
+
+ return first;
+}
+
+#endif
--- /dev/null
+/* Copyright (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (compat_strcasestr.c).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <ctype.h>
+
+#include <compat/strcasestr.h>
+
+/* Pretty much strncasecmp. */
+static int casencmp(const char *a, const char *b, size_t n)
+{
+ size_t i;
+
+ for (i = 0; i < n; i++)
+ {
+ int a_lower = tolower(a[i]);
+ int b_lower = tolower(b[i]);
+ if (a_lower != b_lower)
+ return a_lower - b_lower;
+ }
+
+ return 0;
+}
+
+char *strcasestr_retro__(const char *haystack, const char *needle)
+{
+ size_t i, search_off;
+ size_t hay_len = strlen(haystack);
+ size_t needle_len = strlen(needle);
+
+ if (needle_len > hay_len)
+ return NULL;
+
+ search_off = hay_len - needle_len;
+ for (i = 0; i <= search_off; i++)
+ if (!casencmp(haystack + i, needle, needle_len))
+ return (char*)haystack + i;
+
+ return NULL;
+}
--- /dev/null
+/* Copyright (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (file_path.c).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#include <sys/stat.h>
+
+#include <boolean.h>
+#include <file/file_path.h>
+#include <retro_assert.h>
+#include <string/stdstring.h>
+#include <time/rtime.h>
+
+/* TODO: There are probably some unnecessary things on this huge include list now but I'm too afraid to touch it */
+#ifdef __APPLE__
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+#ifdef __HAIKU__
+#include <kernel/image.h>
+#endif
+#ifndef __MACH__
+#include <compat/strl.h>
+#include <compat/posix_string.h>
+#endif
+#include <retro_miscellaneous.h>
+#include <encodings/utf.h>
+
+#if defined(_WIN32)
+#ifdef _MSC_VER
+#define setmode _setmode
+#endif
+#include <sys/stat.h>
+#ifdef _XBOX
+#include <xtl.h>
+#define INVALID_FILE_ATTRIBUTES -1
+#else
+#include <io.h>
+#include <fcntl.h>
+#include <direct.h>
+#include <windows.h>
+#if defined(_MSC_VER) && _MSC_VER <= 1200
+#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
+#endif
+#endif
+#elif defined(VITA)
+#define SCE_ERROR_ERRNO_EEXIST 0x80010011
+#include <psp2/io/fcntl.h>
+#include <psp2/io/dirent.h>
+#include <psp2/io/stat.h>
+#else
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#endif
+
+#if defined(PSP)
+#include <pspkernel.h>
+#endif
+
+#if defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
+#include <cell/cell_fs.h>
+#endif
+
+#if defined(VITA)
+#define FIO_S_ISDIR SCE_S_ISDIR
+#endif
+
+#if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(__QNX__) || defined(PSP)
+#include <unistd.h> /* stat() is defined here */
+#endif
+
+#if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
+#ifdef __WINRT__
+#include <uwp/uwp_func.h>
+#endif
+#endif
+
+/* Assume W-functions do not work below Win2K and Xbox platforms */
+#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
+
+#ifndef LEGACY_WIN32
+#define LEGACY_WIN32
+#endif
+
+#endif
+
+/**
+ * path_get_archive_delim:
+ * @path : path
+ *
+ * Find delimiter of an archive file. Only the first '#'
+ * after a compression extension is considered.
+ *
+ * Returns: pointer to the delimiter in the path if it contains
+ * a path inside a compressed file, otherwise NULL.
+ */
+const char *path_get_archive_delim(const char *path)
+{
+ const char *last_slash = find_last_slash(path);
+ const char *delim = NULL;
+ char buf[5];
+
+ buf[0] = '\0';
+
+ if (!last_slash)
+ return NULL;
+
+ /* Find delimiter position */
+ delim = strrchr(last_slash, '#');
+
+ if (!delim)
+ return NULL;
+
+ /* Check whether this is a known archive type
+ * > Note: The code duplication here is
+ * deliberate, to maximise performance */
+ if (delim - last_slash > 4)
+ {
+ strlcpy(buf, delim - 4, sizeof(buf));
+ buf[4] = '\0';
+
+ string_to_lower(buf);
+
+ /* Check if this is a '.zip', '.apk' or '.7z' file */
+ if (string_is_equal(buf, ".zip") ||
+ string_is_equal(buf, ".apk") ||
+ string_is_equal(buf + 1, ".7z"))
+ return delim;
+ }
+ else if (delim - last_slash > 3)
+ {
+ strlcpy(buf, delim - 3, sizeof(buf));
+ buf[3] = '\0';
+
+ string_to_lower(buf);
+
+ /* Check if this is a '.7z' file */
+ if (string_is_equal(buf, ".7z"))
+ return delim;
+ }
+
+ return NULL;
+}
+
+/**
+ * path_get_extension:
+ * @path : path
+ *
+ * Gets extension of file. Only '.'s
+ * after the last slash are considered.
+ *
+ * Returns: extension part from the path.
+ */
+const char *path_get_extension(const char *path)
+{
+ const char *ext;
+ if (!string_is_empty(path) && ((ext = strrchr(path_basename(path), '.'))))
+ return ext + 1;
+ return "";
+}
+
+/**
+ * path_remove_extension:
+ * @path : path
+ *
+ * Mutates path by removing its extension. Removes all
+ * text after and including the last '.'.
+ * Only '.'s after the last slash are considered.
+ *
+ * Returns:
+ * 1) If path has an extension, returns path with the
+ * extension removed.
+ * 2) If there is no extension, returns NULL.
+ * 3) If path is empty or NULL, returns NULL
+ */
+char *path_remove_extension(char *path)
+{
+ char *last = !string_is_empty(path)
+ ? (char*)strrchr(path_basename(path), '.') : NULL;
+ if (!last)
+ return NULL;
+ if (*last)
+ *last = '\0';
+ return path;
+}
+
+/**
+ * path_is_compressed_file:
+ * @path : path
+ *
+ * Checks if path is a compressed file.
+ *
+ * Returns: true (1) if path is a compressed file, otherwise false (0).
+ **/
+bool path_is_compressed_file(const char* path)
+{
+ const char *ext = path_get_extension(path);
+
+ if (string_is_empty(ext))
+ return false;
+
+ if (string_is_equal_noncase(ext, "zip") ||
+ string_is_equal_noncase(ext, "apk") ||
+ string_is_equal_noncase(ext, "7z"))
+ return true;
+
+ return false;
+}
+
+/**
+ * fill_pathname:
+ * @out_path : output path
+ * @in_path : input path
+ * @replace : what to replace
+ * @size : buffer size of output path
+ *
+ * FIXME: Verify
+ *
+ * Replaces filename extension with 'replace' and outputs result to out_path.
+ * The extension here is considered to be the string from the last '.'
+ * to the end.
+ *
+ * Only '.'s after the last slash are considered as extensions.
+ * If no '.' is present, in_path and replace will simply be concatenated.
+ * 'size' is buffer size of 'out_path'.
+ * E.g.: in_path = "/foo/bar/baz/boo.c", replace = ".asm" =>
+ * out_path = "/foo/bar/baz/boo.asm"
+ * E.g.: in_path = "/foo/bar/baz/boo.c", replace = "" =>
+ * out_path = "/foo/bar/baz/boo"
+ */
+void fill_pathname(char *out_path, const char *in_path,
+ const char *replace, size_t size)
+{
+ char tmp_path[PATH_MAX_LENGTH];
+ char *tok = NULL;
+
+ tmp_path[0] = '\0';
+
+ strlcpy(tmp_path, in_path, sizeof(tmp_path));
+ if ((tok = (char*)strrchr(path_basename(tmp_path), '.')))
+ *tok = '\0';
+
+ fill_pathname_noext(out_path, tmp_path, replace, 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)
+{
+ strlcpy(out_path, in_path, size);
+ return strlcat(out_path, replace, size);
+}
+
+char *find_last_slash(const char *str)
+{
+ const char *slash = strrchr(str, '/');
+#ifdef _WIN32
+ const char *backslash = strrchr(str, '\\');
+
+ if (!slash || (backslash > slash))
+ return (char*)backslash;
+#endif
+ return (char*)slash;
+}
+
+/**
+ * fill_pathname_slash:
+ * @path : path
+ * @size : size of path
+ *
+ * Assumes path is a directory. Appends a slash
+ * if not already there.
+ **/
+void fill_pathname_slash(char *path, size_t size)
+{
+ size_t path_len;
+ const char *last_slash = find_last_slash(path);
+
+ if (!last_slash)
+ {
+ strlcat(path, PATH_DEFAULT_SLASH(), size);
+ return;
+ }
+
+ path_len = strlen(path);
+ /* Try to preserve slash type. */
+ if (last_slash != (path + path_len - 1))
+ {
+ path[path_len] = last_slash[0];
+ path[path_len+1] = '\0';
+ }
+}
+
+/**
+ * fill_pathname_dir:
+ * @in_dir : input directory path
+ * @in_basename : input basename to be appended to @in_dir
+ * @replace : replacement to be appended to @in_basename
+ * @size : size of buffer
+ *
+ * Appends basename of 'in_basename', to 'in_dir', along with 'replace'.
+ * Basename of in_basename is the string after the last '/' or '\\',
+ * i.e the filename without directories.
+ *
+ * If in_basename has no '/' or '\\', the whole 'in_basename' will be used.
+ * 'size' is buffer size of 'in_dir'.
+ *
+ * E.g..: in_dir = "/tmp/some_dir", in_basename = "/some_content/foo.c",
+ * replace = ".asm" => in_dir = "/tmp/some_dir/foo.c.asm"
+ **/
+size_t fill_pathname_dir(char *in_dir, const char *in_basename,
+ const char *replace, size_t size)
+{
+ const char *base = NULL;
+
+ fill_pathname_slash(in_dir, size);
+ base = path_basename(in_basename);
+ strlcat(in_dir, base, size);
+ return strlcat(in_dir, replace, size);
+}
+
+/**
+ * fill_pathname_base:
+ * @out : output path
+ * @in_path : input path
+ * @size : size of output path
+ *
+ * Copies basename of @in_path into @out_path.
+ **/
+size_t fill_pathname_base(char *out, const char *in_path, size_t size)
+{
+ const char *ptr = path_basename(in_path);
+
+ if (!ptr)
+ ptr = in_path;
+
+ return strlcpy(out, ptr, size);
+}
+
+void fill_pathname_base_noext(char *out,
+ const char *in_path, size_t size)
+{
+ fill_pathname_base(out, in_path, size);
+ path_remove_extension(out);
+}
+
+size_t fill_pathname_base_ext(char *out,
+ const char *in_path, const char *ext,
+ size_t size)
+{
+ fill_pathname_base_noext(out, in_path, size);
+ return strlcat(out, ext, size);
+}
+
+/**
+ * fill_pathname_basedir:
+ * @out_dir : output directory
+ * @in_path : input path
+ * @size : size of output directory
+ *
+ * Copies base directory of @in_path into @out_path.
+ * If in_path is a path without any slashes (relative current directory),
+ * @out_path will get path "./".
+ **/
+void fill_pathname_basedir(char *out_dir,
+ const char *in_path, size_t size)
+{
+ if (out_dir != in_path)
+ strlcpy(out_dir, in_path, size);
+ path_basedir(out_dir);
+}
+
+void fill_pathname_basedir_noext(char *out_dir,
+ const char *in_path, size_t size)
+{
+ fill_pathname_basedir(out_dir, in_path, size);
+ path_remove_extension(out_dir);
+}
+
+/**
+ * fill_pathname_parent_dir_name:
+ * @out_dir : output directory
+ * @in_dir : input directory
+ * @size : size of output directory
+ *
+ * Copies only the parent directory name of @in_dir into @out_dir.
+ * The two buffers must not overlap. Removes trailing '/'.
+ * Returns true on success, false if a slash was not found in the path.
+ **/
+bool fill_pathname_parent_dir_name(char *out_dir,
+ const char *in_dir, size_t size)
+{
+ bool success = false;
+ char *temp = strdup(in_dir);
+ char *last = find_last_slash(temp);
+
+ if (last && last[1] == 0)
+ {
+ *last = '\0';
+ last = find_last_slash(temp);
+ }
+
+ if (last)
+ *last = '\0';
+
+ in_dir = find_last_slash(temp);
+
+ success = in_dir && in_dir[1];
+
+ if (success)
+ strlcpy(out_dir, in_dir + 1, size);
+
+ free(temp);
+ return success;
+}
+
+/**
+ * fill_pathname_parent_dir:
+ * @out_dir : output directory
+ * @in_dir : input directory
+ * @size : size of output directory
+ *
+ * Copies parent directory of @in_dir into @out_dir.
+ * Assumes @in_dir is a directory. Keeps trailing '/'.
+ * If the path was already at the root directory, @out_dir will be an empty string.
+ **/
+void fill_pathname_parent_dir(char *out_dir,
+ const char *in_dir, size_t size)
+{
+ if (out_dir != in_dir)
+ strlcpy(out_dir, in_dir, size);
+ path_parent_dir(out_dir);
+}
+
+/**
+ * fill_dated_filename:
+ * @out_filename : output filename
+ * @ext : extension of output filename
+ * @size : buffer size of output filename
+ *
+ * Creates a 'dated' filename prefixed by 'RetroArch', and
+ * concatenates extension (@ext) to it.
+ *
+ * E.g.:
+ * out_filename = "RetroArch-{month}{day}-{Hours}{Minutes}.{@ext}"
+ **/
+size_t fill_dated_filename(char *out_filename,
+ const char *ext, size_t size)
+{
+ time_t cur_time = time(NULL);
+ struct tm tm_;
+
+ rtime_localtime(&cur_time, &tm_);
+
+ strftime(out_filename, size,
+ "RetroArch-%m%d-%H%M%S", &tm_);
+ return strlcat(out_filename, ext, size);
+}
+
+/**
+ * fill_str_dated_filename:
+ * @out_filename : output filename
+ * @in_str : input string
+ * @ext : extension of output filename
+ * @size : buffer size of output filename
+ *
+ * Creates a 'dated' filename prefixed by the string @in_str, and
+ * concatenates extension (@ext) to it.
+ *
+ * E.g.:
+ * out_filename = "RetroArch-{year}{month}{day}-{Hour}{Minute}{Second}.{@ext}"
+ **/
+void fill_str_dated_filename(char *out_filename,
+ const char *in_str, const char *ext, size_t size)
+{
+ char format[256];
+ struct tm tm_;
+ time_t cur_time = time(NULL);
+
+ format[0] = '\0';
+
+ rtime_localtime(&cur_time, &tm_);
+
+ if (string_is_empty(ext))
+ {
+ strftime(format, sizeof(format), "-%y%m%d-%H%M%S", &tm_);
+ fill_pathname_noext(out_filename, in_str, format, size);
+ }
+ else
+ {
+ strftime(format, sizeof(format), "-%y%m%d-%H%M%S.", &tm_);
+
+ fill_pathname_join_concat_noext(out_filename,
+ in_str, format, ext,
+ size);
+ }
+}
+
+/**
+ * path_basedir:
+ * @path : path
+ *
+ * Extracts base directory by mutating path.
+ * Keeps trailing '/'.
+ **/
+void path_basedir(char *path)
+{
+ char *last = NULL;
+
+ if (strlen(path) < 2)
+ return;
+
+ last = find_last_slash(path);
+
+ if (last)
+ last[1] = '\0';
+ else
+ snprintf(path, 3, "." PATH_DEFAULT_SLASH());
+}
+
+/**
+ * path_parent_dir:
+ * @path : path
+ *
+ * Extracts parent directory by mutating path.
+ * Assumes that path is a directory. Keeps trailing '/'.
+ * If the path was already at the root directory, returns empty string
+ **/
+void path_parent_dir(char *path)
+{
+ size_t len = 0;
+
+ if (!path)
+ return;
+
+ len = strlen(path);
+
+ if (len && PATH_CHAR_IS_SLASH(path[len - 1]))
+ {
+ bool path_was_absolute = path_is_absolute(path);
+
+ path[len - 1] = '\0';
+
+ if (path_was_absolute && !find_last_slash(path))
+ {
+ /* We removed the only slash from what used to be an absolute path.
+ * On Linux, this goes from "/" to an empty string and everything works fine,
+ * but on Windows, we went from C:\ to C:, which is not a valid path and that later
+ * gets errornously treated as a relative one by path_basedir and returns "./".
+ * What we really wanted is an empty string. */
+ path[0] = '\0';
+ return;
+ }
+ }
+ path_basedir(path);
+}
+
+/**
+ * path_basename:
+ * @path : path
+ *
+ * Get basename from @path.
+ *
+ * Returns: basename from path.
+ **/
+const char *path_basename(const char *path)
+{
+ /* We cut at the first compression-related hash */
+ const char *delim = path_get_archive_delim(path);
+ if (delim)
+ return delim + 1;
+
+ {
+ /* We cut at the last slash */
+ const char *last = find_last_slash(path);
+ if (last)
+ return last + 1;
+ }
+
+ return path;
+}
+
+/**
+ * path_is_absolute:
+ * @path : path
+ *
+ * Checks if @path is an absolute path or a relative path.
+ *
+ * Returns: true if path is absolute, false if path is relative.
+ **/
+bool path_is_absolute(const char *path)
+{
+ if (string_is_empty(path))
+ return false;
+
+ if (path[0] == '/')
+ return true;
+
+#if defined(_WIN32)
+ /* Many roads lead to Rome...
+ * Note: Drive letter can only be 1 character long */
+ if (string_starts_with_size(path, "\\\\", STRLEN_CONST("\\\\")) ||
+ string_starts_with_size(path + 1, ":/", STRLEN_CONST(":/")) ||
+ string_starts_with_size(path + 1, ":\\", STRLEN_CONST(":\\")))
+ return true;
+#elif defined(__wiiu__) || defined(VITA)
+ {
+ const char *seperator = strchr(path, ':');
+ if (seperator && (seperator[1] == '/'))
+ return true;
+ }
+#endif
+
+ return false;
+}
+
+/**
+ * path_resolve_realpath:
+ * @buf : input and output buffer for path
+ * @size : size of buffer
+ * @resolve_symlinks : whether to resolve symlinks or not
+ *
+ * Resolves use of ".", "..", multiple slashes etc in absolute paths.
+ *
+ * Relative paths are rebased on the current working dir.
+ *
+ * Returns: @buf if successful, NULL otherwise.
+ * Note: Not implemented on consoles
+ * Note: Symlinks are only resolved on Unix-likes
+ * Note: The current working dir might not be what you expect,
+ * e.g. on Android it is "/"
+ * Use of fill_pathname_resolve_relative() should be prefered
+ **/
+char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks)
+{
+#if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
+ char tmp[PATH_MAX_LENGTH];
+#ifdef _WIN32
+ strlcpy(tmp, buf, sizeof(tmp));
+ if (!_fullpath(buf, tmp, size))
+ {
+ strlcpy(buf, tmp, size);
+ return NULL;
+ }
+ return buf;
+#else
+ size_t t;
+ char *p;
+ const char *next;
+ const char *buf_end;
+
+ if (resolve_symlinks)
+ {
+ strlcpy(tmp, buf, sizeof(tmp));
+
+ /* NOTE: realpath() expects at least PATH_MAX_LENGTH bytes in buf.
+ * Technically, PATH_MAX_LENGTH needn't be defined, but we rely on it anyways.
+ * POSIX 2008 can automatically allocate for you,
+ * but don't rely on that. */
+ if (!realpath(tmp, buf))
+ {
+ strlcpy(buf, tmp, size);
+ return NULL;
+ }
+
+ return buf;
+ }
+
+ t = 0; /* length of output */
+ buf_end = buf + strlen(buf);
+
+ if (!path_is_absolute(buf))
+ {
+ size_t len;
+ /* rebase on working directory */
+ if (!getcwd(tmp, PATH_MAX_LENGTH-1))
+ return NULL;
+
+ len = strlen(tmp);
+ t += len;
+
+ if (tmp[len-1] != '/')
+ tmp[t++] = '/';
+
+ if (string_is_empty(buf))
+ goto end;
+
+ p = buf;
+ }
+ else
+ {
+ /* UNIX paths can start with multiple '/', copy those */
+ for (p = buf; *p == '/'; p++)
+ tmp[t++] = '/';
+ }
+
+ /* p points to just after a slash while 'next' points to the next slash
+ * if there are no slashes, they point relative to where one would be */
+ do
+ {
+ next = strchr(p, '/');
+ if (!next)
+ next = buf_end;
+
+ if ((next - p == 2 && p[0] == '.' && p[1] == '.'))
+ {
+ p += 3;
+
+ /* fail for illegal /.., //.. etc */
+ if (t == 1 || tmp[t-2] == '/')
+ return NULL;
+
+ /* delete previous segment in tmp by adjusting size t
+ * tmp[t-1] == '/', find '/' before that */
+ t = t-2;
+ while (tmp[t] != '/')
+ t--;
+ t++;
+ }
+ else if (next - p == 1 && p[0] == '.')
+ p += 2;
+ else if (next - p == 0)
+ p += 1;
+ else
+ {
+ /* fail when truncating */
+ if (t + next-p+1 > PATH_MAX_LENGTH-1)
+ return NULL;
+
+ while (p <= next)
+ tmp[t++] = *p++;
+ }
+
+ }
+ while (next < buf_end);
+
+end:
+ tmp[t] = '\0';
+ strlcpy(buf, tmp, size);
+ return buf;
+#endif
+#endif
+ return NULL;
+}
+
+/**
+ * path_relative_to:
+ * @out : buffer to write the relative path to
+ * @path : path to be expressed relatively
+ * @base : base directory to start out on
+ * @size : size of output buffer
+ *
+ * Turns @path into a path relative to @base and writes it to @out.
+ *
+ * @base is assumed to be a base directory, i.e. a path ending with '/' or '\'.
+ * Both @path and @base are assumed to be absolute paths without "." or "..".
+ *
+ * E.g. path /a/b/e/f.cg with base /a/b/c/d/ turns into ../../e/f.cg
+ **/
+size_t path_relative_to(char *out,
+ const char *path, const char *base, size_t size)
+{
+ size_t i, j;
+ const char *trimmed_path, *trimmed_base;
+
+#ifdef _WIN32
+ /* For different drives, return absolute path */
+ if (strlen(path) >= 2 && strlen(base) >= 2
+ && path[1] == ':' && base[1] == ':'
+ && path[0] != base[0])
+ return strlcpy(out, path, size);
+#endif
+
+ /* Trim common beginning */
+ for (i = 0, j = 0; path[i] && base[i] && path[i] == base[i]; i++)
+ if (path[i] == PATH_DEFAULT_SLASH_C())
+ j = i + 1;
+
+ trimmed_path = path+j;
+ trimmed_base = base+i;
+
+ /* Each segment of base turns into ".." */
+ out[0] = '\0';
+ for (i = 0; trimmed_base[i]; i++)
+ if (trimmed_base[i] == PATH_DEFAULT_SLASH_C())
+ strlcat(out, ".." PATH_DEFAULT_SLASH(), size);
+
+ return strlcat(out, trimmed_path, size);
+}
+
+/**
+ * fill_pathname_resolve_relative:
+ * @out_path : output path
+ * @in_refpath : input reference path
+ * @in_path : input path
+ * @size : size of @out_path
+ *
+ * Joins basedir of @in_refpath together with @in_path.
+ * If @in_path is an absolute path, out_path = in_path.
+ * E.g.: in_refpath = "/foo/bar/baz.a", in_path = "foobar.cg",
+ * out_path = "/foo/bar/foobar.cg".
+ **/
+void fill_pathname_resolve_relative(char *out_path,
+ const char *in_refpath, const char *in_path, size_t size)
+{
+ if (path_is_absolute(in_path))
+ {
+ strlcpy(out_path, in_path, size);
+ return;
+ }
+
+ fill_pathname_basedir(out_path, in_refpath, size);
+ strlcat(out_path, in_path, size);
+ path_resolve_realpath(out_path, size, false);
+}
+
+/**
+ * fill_pathname_join:
+ * @out_path : output path
+ * @dir : directory
+ * @path : path
+ * @size : size of output path
+ *
+ * Joins a directory (@dir) and path (@path) together.
+ * Makes sure not to get two consecutive slashes
+ * between directory and path.
+ **/
+size_t fill_pathname_join(char *out_path,
+ const char *dir, const char *path, size_t size)
+{
+ if (out_path != dir)
+ strlcpy(out_path, dir, size);
+
+ if (*out_path)
+ fill_pathname_slash(out_path, size);
+
+ return strlcat(out_path, path, size);
+}
+
+size_t fill_pathname_join_special_ext(char *out_path,
+ const char *dir, const char *path,
+ const char *last, const char *ext,
+ size_t size)
+{
+ fill_pathname_join(out_path, dir, path, size);
+ if (*out_path)
+ fill_pathname_slash(out_path, size);
+
+ strlcat(out_path, last, size);
+ return strlcat(out_path, ext, size);
+}
+
+size_t fill_pathname_join_concat_noext(char *out_path,
+ const char *dir, const char *path,
+ const char *concat,
+ size_t size)
+{
+ fill_pathname_noext(out_path, dir, path, size);
+ return strlcat(out_path, concat, size);
+}
+
+size_t fill_pathname_join_concat(char *out_path,
+ const char *dir, const char *path,
+ const char *concat,
+ size_t size)
+{
+ fill_pathname_join(out_path, dir, path, size);
+ return strlcat(out_path, concat, size);
+}
+
+void fill_pathname_join_noext(char *out_path,
+ const char *dir, const char *path, size_t size)
+{
+ fill_pathname_join(out_path, dir, path, size);
+ path_remove_extension(out_path);
+}
+
+/**
+ * fill_pathname_join_delim:
+ * @out_path : output path
+ * @dir : directory
+ * @path : path
+ * @delim : delimiter
+ * @size : size of output path
+ *
+ * Joins a directory (@dir) and path (@path) together
+ * using the given delimiter (@delim).
+ **/
+size_t fill_pathname_join_delim(char *out_path, const char *dir,
+ const char *path, const char delim, size_t size)
+{
+ size_t copied;
+ /* behavior of strlcpy is undefined if dst and src overlap */
+ if (out_path == dir)
+ copied = strlen(dir);
+ else
+ copied = strlcpy(out_path, dir, size);
+
+ out_path[copied] = delim;
+ out_path[copied+1] = '\0';
+
+ if (path)
+ copied = strlcat(out_path, path, size);
+ return copied;
+}
+
+size_t fill_pathname_join_delim_concat(char *out_path, const char *dir,
+ const char *path, const char delim, const char *concat,
+ size_t size)
+{
+ fill_pathname_join_delim(out_path, dir, path, delim, size);
+ return strlcat(out_path, concat, size);
+}
+
+/**
+ * fill_short_pathname_representation:
+ * @out_rep : output representation
+ * @in_path : input path
+ * @size : size of output representation
+ *
+ * Generates a short representation of path. It should only
+ * be used for displaying the result; the output representation is not
+ * binding in any meaningful way (for a normal path, this is the same as basename)
+ * In case of more complex URLs, this should cut everything except for
+ * the main image file.
+ *
+ * E.g.: "/path/to/game.img" -> game.img
+ * "/path/to/myarchive.7z#folder/to/game.img" -> game.img
+ */
+size_t fill_short_pathname_representation(char* out_rep,
+ const char *in_path, size_t size)
+{
+ char path_short[PATH_MAX_LENGTH];
+
+ path_short[0] = '\0';
+
+ fill_pathname(path_short, path_basename(in_path), "",
+ sizeof(path_short));
+
+ return strlcpy(out_rep, path_short, size);
+}
+
+void fill_short_pathname_representation_noext(char* out_rep,
+ const char *in_path, size_t size)
+{
+ fill_short_pathname_representation(out_rep, in_path, size);
+ path_remove_extension(out_rep);
+}
+
+void fill_pathname_expand_special(char *out_path,
+ const char *in_path, size_t size)
+{
+#if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
+ if (in_path[0] == '~')
+ {
+ char *home_dir = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
+
+ home_dir[0] = '\0';
+
+ fill_pathname_home_dir(home_dir,
+ PATH_MAX_LENGTH * sizeof(char));
+
+ if (*home_dir)
+ {
+ size_t src_size = strlcpy(out_path, home_dir, size);
+ retro_assert(src_size < size);
+
+ out_path += src_size;
+ size -= src_size;
+
+ if (!PATH_CHAR_IS_SLASH(out_path[-1]))
+ {
+ src_size = strlcpy(out_path, PATH_DEFAULT_SLASH(), size);
+ retro_assert(src_size < size);
+
+ out_path += src_size;
+ size -= src_size;
+ }
+
+ in_path += 2;
+ }
+
+ free(home_dir);
+ }
+ else if (in_path[0] == ':')
+ {
+ char *application_dir = (char*)malloc(PATH_MAX_LENGTH * sizeof(char));
+
+ application_dir[0] = '\0';
+
+ fill_pathname_application_dir(application_dir,
+ PATH_MAX_LENGTH * sizeof(char));
+
+ if (*application_dir)
+ {
+ size_t src_size = strlcpy(out_path, application_dir, size);
+ retro_assert(src_size < size);
+
+ out_path += src_size;
+ size -= src_size;
+
+ if (!PATH_CHAR_IS_SLASH(out_path[-1]))
+ {
+ src_size = strlcpy(out_path, PATH_DEFAULT_SLASH(), size);
+ retro_assert(src_size < size);
+
+ out_path += src_size;
+ size -= src_size;
+ }
+
+ in_path += 2;
+ }
+
+ free(application_dir);
+ }
+#endif
+
+ retro_assert(strlcpy(out_path, in_path, size) < size);
+}
+
+void fill_pathname_abbreviate_special(char *out_path,
+ const char *in_path, size_t size)
+{
+#if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
+ unsigned i;
+ const char *candidates[3];
+ const char *notations[3];
+ char application_dir[PATH_MAX_LENGTH];
+ char home_dir[PATH_MAX_LENGTH];
+
+ application_dir[0] = '\0';
+ home_dir[0] = '\0';
+
+ /* application_dir could be zero-string. Safeguard against this.
+ *
+ * Keep application dir in front of home, moving app dir to a
+ * new location inside home would break otherwise. */
+
+ /* ugly hack - use application_dir pointer
+ * before filling it in. C89 reasons */
+ candidates[0] = application_dir;
+ candidates[1] = home_dir;
+ candidates[2] = NULL;
+
+ notations [0] = ":";
+ notations [1] = "~";
+ notations [2] = NULL;
+
+ fill_pathname_application_dir(application_dir, sizeof(application_dir));
+ fill_pathname_home_dir(home_dir, sizeof(home_dir));
+
+ for (i = 0; candidates[i]; i++)
+ {
+ if (!string_is_empty(candidates[i]) &&
+ string_starts_with(in_path, candidates[i]))
+ {
+ size_t src_size = strlcpy(out_path, notations[i], size);
+
+ retro_assert(src_size < size);
+
+ out_path += src_size;
+ size -= src_size;
+ in_path += strlen(candidates[i]);
+
+ if (!PATH_CHAR_IS_SLASH(*in_path))
+ {
+ strcpy_literal(out_path, PATH_DEFAULT_SLASH());
+ out_path++;
+ size--;
+ }
+
+ break; /* Don't allow more abbrevs to take place. */
+ }
+ }
+
+#endif
+
+ retro_assert(strlcpy(out_path, in_path, size) < size);
+}
+
+/**
+ * path_basedir:
+ * @path : path
+ *
+ * Extracts base directory by mutating path.
+ * Keeps trailing '/'.
+ **/
+void path_basedir_wrapper(char *path)
+{
+ char *last = NULL;
+ if (strlen(path) < 2)
+ return;
+
+#ifdef HAVE_COMPRESSION
+ /* We want to find the directory with the archive in basedir. */
+ last = (char*)path_get_archive_delim(path);
+ if (last)
+ *last = '\0';
+#endif
+
+ last = find_last_slash(path);
+
+ if (last)
+ last[1] = '\0';
+ else
+ snprintf(path, 3, "." PATH_DEFAULT_SLASH());
+}
+
+#if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
+void fill_pathname_application_path(char *s, size_t len)
+{
+ size_t i;
+#ifdef __APPLE__
+ CFBundleRef bundle = CFBundleGetMainBundle();
+#endif
+#ifdef _WIN32
+ DWORD ret = 0;
+ wchar_t wstr[PATH_MAX_LENGTH] = {0};
+#endif
+#ifdef __HAIKU__
+ image_info info;
+ int32_t cookie = 0;
+#endif
+ (void)i;
+
+ if (!len)
+ return;
+
+#if defined(_WIN32)
+#ifdef LEGACY_WIN32
+ ret = GetModuleFileNameA(NULL, s, len);
+#else
+ ret = GetModuleFileNameW(NULL, wstr, ARRAY_SIZE(wstr));
+
+ if (*wstr)
+ {
+ char *str = utf16_to_utf8_string_alloc(wstr);
+
+ if (str)
+ {
+ strlcpy(s, str, len);
+ free(str);
+ }
+ }
+#endif
+ s[ret] = '\0';
+#elif defined(__APPLE__)
+ if (bundle)
+ {
+ CFURLRef bundle_url = CFBundleCopyBundleURL(bundle);
+ CFStringRef bundle_path = CFURLCopyPath(bundle_url);
+ CFStringGetCString(bundle_path, s, len, kCFStringEncodingUTF8);
+#ifdef HAVE_COCOATOUCH
+ {
+ /* This needs to be done so that the path becomes
+ * /private/var/... and this
+ * is used consistently throughout for the iOS bundle path */
+ char resolved_bundle_dir_buf[PATH_MAX_LENGTH] = {0};
+ if (realpath(s, resolved_bundle_dir_buf))
+ {
+ strlcpy(s, resolved_bundle_dir_buf, len - 1);
+ strlcat(s, "/", len);
+ }
+ }
+#endif
+
+ CFRelease(bundle_path);
+ CFRelease(bundle_url);
+#ifndef HAVE_COCOATOUCH
+ /* Not sure what this does but it breaks
+ * stuff for iOS, so skipping */
+ retro_assert(strlcat(s, "nobin", len) < len);
+#endif
+ return;
+ }
+#elif defined(__HAIKU__)
+ while (get_next_image_info(0, &cookie, &info) == B_OK)
+ {
+ if (info.type == B_APP_IMAGE)
+ {
+ strlcpy(s, info.name, len);
+ return;
+ }
+ }
+#elif defined(__QNX__)
+ char *buff = malloc(len);
+
+ if (_cmdname(buff))
+ strlcpy(s, buff, len);
+
+ free(buff);
+#else
+ {
+ pid_t pid;
+ static const char *exts[] = { "exe", "file", "path/a.out" };
+ char link_path[255];
+
+ link_path[0] = *s = '\0';
+ pid = getpid();
+
+ /* Linux, BSD and Solaris paths. Not standardized. */
+ for (i = 0; i < ARRAY_SIZE(exts); i++)
+ {
+ ssize_t ret;
+
+ snprintf(link_path, sizeof(link_path), "/proc/%u/%s",
+ (unsigned)pid, exts[i]);
+ ret = readlink(link_path, s, len - 1);
+
+ if (ret >= 0)
+ {
+ s[ret] = '\0';
+ return;
+ }
+ }
+ }
+#endif
+}
+
+void fill_pathname_application_dir(char *s, size_t len)
+{
+#ifdef __WINRT__
+ strlcpy(s, uwp_dir_install, len);
+#else
+ fill_pathname_application_path(s, len);
+ path_basedir_wrapper(s);
+#endif
+}
+
+void fill_pathname_home_dir(char *s, size_t len)
+{
+#ifdef __WINRT__
+ const char *home = uwp_dir_data;
+#else
+ const char *home = getenv("HOME");
+#endif
+ if (home)
+ strlcpy(s, home, len);
+ else
+ *s = 0;
+}
+#endif
+
+bool is_path_accessible_using_standard_io(const char *path)
+{
+#ifdef __WINRT__
+ char relative_path_abbrev[PATH_MAX_LENGTH];
+ fill_pathname_abbreviate_special(relative_path_abbrev,
+ path, sizeof(relative_path_abbrev));
+ return (strlen(relative_path_abbrev) >= 2 )
+ && ( relative_path_abbrev[0] == ':'
+ || relative_path_abbrev[0] == '~')
+ && PATH_CHAR_IS_SLASH(relative_path_abbrev[1]);
+#else
+ return true;
+#endif
+}
--- /dev/null
+/* Copyright (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (posix_string.h).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __LIBRETRO_SDK_COMPAT_POSIX_STRING_H
+#define __LIBRETRO_SDK_COMPAT_POSIX_STRING_H
+
+#include <retro_common_api.h>
+
+#ifdef _MSC_VER
+#include <compat/msvc.h>
+#endif
+
+RETRO_BEGIN_DECLS
+
+#ifdef _WIN32
+#undef strtok_r
+#define strtok_r(str, delim, saveptr) retro_strtok_r__(str, delim, saveptr)
+
+char *strtok_r(char *str, const char *delim, char **saveptr);
+#endif
+
+#ifdef _MSC_VER
+#undef strcasecmp
+#undef strdup
+#define strcasecmp(a, b) retro_strcasecmp__(a, b)
+#define strdup(orig) retro_strdup__(orig)
+int strcasecmp(const char *a, const char *b);
+char *strdup(const char *orig);
+
+/* isblank is available since MSVC 2013 */
+#if _MSC_VER < 1800
+#undef isblank
+#define isblank(c) retro_isblank__(c)
+int isblank(int c);
+#endif
+
+#endif
+
+RETRO_END_DECLS
+
+#endif
--- /dev/null
+/* Copyright (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (strcasestr.h).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __LIBRETRO_SDK_COMPAT_STRCASESTR_H
+#define __LIBRETRO_SDK_COMPAT_STRCASESTR_H
+
+#include <string.h>
+
+#if defined(RARCH_INTERNAL) && defined(HAVE_CONFIG_H)
+#include "../../../config.h"
+#endif
+
+#ifndef HAVE_STRCASESTR
+
+#include <retro_common_api.h>
+
+RETRO_BEGIN_DECLS
+
+/* Avoid possible naming collisions during link
+ * since we prefer to use the actual name. */
+#define strcasestr(haystack, needle) strcasestr_retro__(haystack, needle)
+
+char *strcasestr(const char *haystack, const char *needle);
+
+RETRO_END_DECLS
+
+#endif
+
+#endif
--- /dev/null
+/* Copyright (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (file_path.h).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __LIBRETRO_SDK_FILE_PATH_H
+#define __LIBRETRO_SDK_FILE_PATH_H
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+#include <libretro.h>
+#include <retro_common_api.h>
+
+#include <boolean.h>
+
+RETRO_BEGIN_DECLS
+
+#define PATH_REQUIRED_VFS_VERSION 3
+
+void path_vfs_init(const struct retro_vfs_interface_info* vfs_info);
+
+/* Order in this enum is equivalent to negative sort order in filelist
+ * (i.e. DIRECTORY is on top of PLAIN_FILE) */
+enum
+{
+ RARCH_FILETYPE_UNSET,
+ RARCH_PLAIN_FILE,
+ RARCH_COMPRESSED_FILE_IN_ARCHIVE,
+ RARCH_COMPRESSED_ARCHIVE,
+ RARCH_DIRECTORY,
+ RARCH_FILE_UNSUPPORTED
+};
+
+/**
+ * path_is_compressed_file:
+ * @path : path
+ *
+ * Checks if path is a compressed file.
+ *
+ * Returns: true (1) if path is a compressed file, otherwise false (0).
+ **/
+bool path_is_compressed_file(const char *path);
+
+/**
+ * path_contains_compressed_file:
+ * @path : path
+ *
+ * Checks if path contains a compressed file.
+ *
+ * Currently we only check for hash symbol (#) inside the pathname.
+ * If path is ever expanded to a general URI, we should check for that here.
+ *
+ * Example: Somewhere in the path there might be a compressed file
+ * E.g.: /path/to/file.7z#mygame.img
+ *
+ * Returns: true (1) if path contains compressed file, otherwise false (0).
+ **/
+#define path_contains_compressed_file(path) (path_get_archive_delim((path)) != NULL)
+
+/**
+ * path_get_archive_delim:
+ * @path : path
+ *
+ * Gets delimiter of an archive file. Only the first '#'
+ * after a compression extension is considered.
+ *
+ * Returns: pointer to the delimiter in the path if it contains
+ * a compressed file, otherwise NULL.
+ */
+const char *path_get_archive_delim(const char *path);
+
+/**
+ * path_get_extension:
+ * @path : path
+ *
+ * Gets extension of file. Only '.'s
+ * after the last slash are considered.
+ *
+ * Returns: extension part from the path.
+ */
+const char *path_get_extension(const char *path);
+
+/**
+ * path_remove_extension:
+ * @path : path
+ *
+ * Mutates path by removing its extension. Removes all
+ * text after and including the last '.'.
+ * Only '.'s after the last slash are considered.
+ *
+ * Returns:
+ * 1) If path has an extension, returns path with the
+ * extension removed.
+ * 2) If there is no extension, returns NULL.
+ * 3) If path is empty or NULL, returns NULL
+ */
+char *path_remove_extension(char *path);
+
+/**
+ * path_basename:
+ * @path : path
+ *
+ * Get basename from @path.
+ *
+ * Returns: basename from path.
+ **/
+const char *path_basename(const char *path);
+
+/**
+ * path_basedir:
+ * @path : path
+ *
+ * Extracts base directory by mutating path.
+ * Keeps trailing '/'.
+ **/
+void path_basedir(char *path);
+
+/**
+ * path_parent_dir:
+ * @path : path
+ *
+ * Extracts parent directory by mutating path.
+ * Assumes that path is a directory. Keeps trailing '/'.
+ * If the path was already at the root directory, returns empty string
+ **/
+void path_parent_dir(char *path);
+
+/**
+ * path_resolve_realpath:
+ * @buf : input and output buffer for path
+ * @size : size of buffer
+ * @resolve_symlinks : whether to resolve symlinks or not
+ *
+ * Resolves use of ".", "..", multiple slashes etc in absolute paths.
+ *
+ * Relative paths are rebased on the current working dir.
+ *
+ * Returns: @buf if successful, NULL otherwise.
+ * Note: Not implemented on consoles
+ * Note: Symlinks are only resolved on Unix-likes
+ * Note: The current working dir might not be what you expect,
+ * e.g. on Android it is "/"
+ * Use of fill_pathname_resolve_relative() should be prefered
+ **/
+char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks);
+
+/**
+ * path_relative_to:
+ * @out : buffer to write the relative path to
+ * @path : path to be expressed relatively
+ * @base : relative to this
+ * @size : size of output buffer
+ *
+ * Turns @path into a path relative to @base and writes it to @out.
+ *
+ * @base is assumed to be a base directory, i.e. a path ending with '/' or '\'.
+ * Both @path and @base are assumed to be absolute paths without "." or "..".
+ *
+ * E.g. path /a/b/e/f.cgp with base /a/b/c/d/ turns into ../../e/f.cgp
+ **/
+size_t path_relative_to(char *out, const char *path, const char *base, size_t size);
+
+/**
+ * path_is_absolute:
+ * @path : path
+ *
+ * Checks if @path is an absolute path or a relative path.
+ *
+ * Returns: true if path is absolute, false if path is relative.
+ **/
+bool path_is_absolute(const char *path);
+
+/**
+ * fill_pathname:
+ * @out_path : output path
+ * @in_path : input path
+ * @replace : what to replace
+ * @size : buffer size of output path
+ *
+ * FIXME: Verify
+ *
+ * Replaces filename extension with 'replace' and outputs result to out_path.
+ * The extension here is considered to be the string from the last '.'
+ * to the end.
+ *
+ * Only '.'s after the last slash are considered as extensions.
+ * If no '.' is present, in_path and replace will simply be concatenated.
+ * 'size' is buffer size of 'out_path'.
+ * E.g.: in_path = "/foo/bar/baz/boo.c", replace = ".asm" =>
+ * out_path = "/foo/bar/baz/boo.asm"
+ * E.g.: in_path = "/foo/bar/baz/boo.c", replace = "" =>
+ * out_path = "/foo/bar/baz/boo"
+ */
+void fill_pathname(char *out_path, const char *in_path,
+ const char *replace, size_t size);
+
+/**
+ * fill_dated_filename:
+ * @out_filename : output filename
+ * @ext : extension of output filename
+ * @size : buffer size of output filename
+ *
+ * Creates a 'dated' filename prefixed by 'RetroArch', and
+ * concatenates extension (@ext) to it.
+ *
+ * E.g.:
+ * out_filename = "RetroArch-{month}{day}-{Hours}{Minutes}.{@ext}"
+ **/
+size_t fill_dated_filename(char *out_filename,
+ const char *ext, size_t size);
+
+/**
+ * fill_str_dated_filename:
+ * @out_filename : output filename
+ * @in_str : input string
+ * @ext : extension of output filename
+ * @size : buffer size of output filename
+ *
+ * Creates a 'dated' filename prefixed by the string @in_str, and
+ * concatenates extension (@ext) to it.
+ *
+ * E.g.:
+ * out_filename = "RetroArch-{year}{month}{day}-{Hour}{Minute}{Second}.{@ext}"
+ **/
+void fill_str_dated_filename(char *out_filename,
+ const char *in_str, const char *ext, size_t size);
+
+/**
+ * fill_pathname_noext:
+ * @out_path : output path
+ * @in_path : input path
+ * @replace : what to replace
+ * @size : buffer size of output path
+ *
+ * Appends a filename extension 'replace' to 'in_path', and outputs
+ * result in 'out_path'.
+ *
+ * Assumes in_path has no extension. If an extension is still
+ * present in 'in_path', it will be ignored.
+ *
+ */
+size_t fill_pathname_noext(char *out_path, const char *in_path,
+ const char *replace, size_t size);
+
+/**
+ * find_last_slash:
+ * @str : input path
+ *
+ * Gets a pointer to the last slash in the input path.
+ *
+ * Returns: a pointer to the last slash in the input path.
+ **/
+char *find_last_slash(const char *str);
+
+/**
+ * fill_pathname_dir:
+ * @in_dir : input directory path
+ * @in_basename : input basename to be appended to @in_dir
+ * @replace : replacement to be appended to @in_basename
+ * @size : size of buffer
+ *
+ * Appends basename of 'in_basename', to 'in_dir', along with 'replace'.
+ * Basename of in_basename is the string after the last '/' or '\\',
+ * i.e the filename without directories.
+ *
+ * If in_basename has no '/' or '\\', the whole 'in_basename' will be used.
+ * 'size' is buffer size of 'in_dir'.
+ *
+ * E.g..: in_dir = "/tmp/some_dir", in_basename = "/some_content/foo.c",
+ * replace = ".asm" => in_dir = "/tmp/some_dir/foo.c.asm"
+ **/
+size_t fill_pathname_dir(char *in_dir, const char *in_basename,
+ const char *replace, size_t size);
+
+/**
+ * fill_pathname_base:
+ * @out : output path
+ * @in_path : input path
+ * @size : size of output path
+ *
+ * Copies basename of @in_path into @out_path.
+ **/
+size_t fill_pathname_base(char *out_path, const char *in_path, size_t size);
+
+void fill_pathname_base_noext(char *out_dir,
+ const char *in_path, size_t size);
+
+size_t fill_pathname_base_ext(char *out,
+ const char *in_path, const char *ext,
+ size_t size);
+
+/**
+ * fill_pathname_basedir:
+ * @out_dir : output directory
+ * @in_path : input path
+ * @size : size of output directory
+ *
+ * Copies base directory of @in_path into @out_path.
+ * If in_path is a path without any slashes (relative current directory),
+ * @out_path will get path "./".
+ **/
+void fill_pathname_basedir(char *out_path, const char *in_path, size_t size);
+
+void fill_pathname_basedir_noext(char *out_dir,
+ const char *in_path, size_t size);
+
+/**
+ * fill_pathname_parent_dir_name:
+ * @out_dir : output directory
+ * @in_dir : input directory
+ * @size : size of output directory
+ *
+ * Copies only the parent directory name of @in_dir into @out_dir.
+ * The two buffers must not overlap. Removes trailing '/'.
+ * Returns true on success, false if a slash was not found in the path.
+ **/
+bool fill_pathname_parent_dir_name(char *out_dir,
+ const char *in_dir, size_t size);
+
+/**
+ * fill_pathname_parent_dir:
+ * @out_dir : output directory
+ * @in_dir : input directory
+ * @size : size of output directory
+ *
+ * Copies parent directory of @in_dir into @out_dir.
+ * Assumes @in_dir is a directory. Keeps trailing '/'.
+ * If the path was already at the root directory, @out_dir will be an empty string.
+ **/
+void fill_pathname_parent_dir(char *out_dir,
+ const char *in_dir, size_t size);
+
+/**
+ * fill_pathname_resolve_relative:
+ * @out_path : output path
+ * @in_refpath : input reference path
+ * @in_path : input path
+ * @size : size of @out_path
+ *
+ * Joins basedir of @in_refpath together with @in_path.
+ * If @in_path is an absolute path, out_path = in_path.
+ * E.g.: in_refpath = "/foo/bar/baz.a", in_path = "foobar.cg",
+ * out_path = "/foo/bar/foobar.cg".
+ **/
+void fill_pathname_resolve_relative(char *out_path, const char *in_refpath,
+ const char *in_path, size_t size);
+
+/**
+ * fill_pathname_join:
+ * @out_path : output path
+ * @dir : directory
+ * @path : path
+ * @size : size of output path
+ *
+ * Joins a directory (@dir) and path (@path) together.
+ * Makes sure not to get two consecutive slashes
+ * between directory and path.
+ **/
+size_t fill_pathname_join(char *out_path, const char *dir,
+ const char *path, size_t size);
+
+size_t fill_pathname_join_special_ext(char *out_path,
+ const char *dir, const char *path,
+ const char *last, const char *ext,
+ size_t size);
+
+size_t fill_pathname_join_concat_noext(char *out_path,
+ const char *dir, const char *path,
+ const char *concat,
+ size_t size);
+
+size_t fill_pathname_join_concat(char *out_path,
+ const char *dir, const char *path,
+ const char *concat,
+ size_t size);
+
+void fill_pathname_join_noext(char *out_path,
+ const char *dir, const char *path, size_t size);
+
+/**
+ * fill_pathname_join_delim:
+ * @out_path : output path
+ * @dir : directory
+ * @path : path
+ * @delim : delimiter
+ * @size : size of output path
+ *
+ * Joins a directory (@dir) and path (@path) together
+ * using the given delimiter (@delim).
+ **/
+size_t fill_pathname_join_delim(char *out_path, const char *dir,
+ const char *path, const char delim, size_t size);
+
+size_t fill_pathname_join_delim_concat(char *out_path, const char *dir,
+ const char *path, const char delim, const char *concat,
+ size_t size);
+
+/**
+ * fill_short_pathname_representation:
+ * @out_rep : output representation
+ * @in_path : input path
+ * @size : size of output representation
+ *
+ * Generates a short representation of path. It should only
+ * be used for displaying the result; the output representation is not
+ * binding in any meaningful way (for a normal path, this is the same as basename)
+ * In case of more complex URLs, this should cut everything except for
+ * the main image file.
+ *
+ * E.g.: "/path/to/game.img" -> game.img
+ * "/path/to/myarchive.7z#folder/to/game.img" -> game.img
+ */
+size_t fill_short_pathname_representation(char* out_rep,
+ const char *in_path, size_t size);
+
+void fill_short_pathname_representation_noext(char* out_rep,
+ const char *in_path, size_t size);
+
+void fill_pathname_expand_special(char *out_path,
+ const char *in_path, size_t size);
+
+void fill_pathname_abbreviate_special(char *out_path,
+ const char *in_path, size_t size);
+
+/**
+ * path_basedir:
+ * @path : path
+ *
+ * Extracts base directory by mutating path.
+ * Keeps trailing '/'.
+ **/
+void path_basedir_wrapper(char *path);
+
+/**
+ * path_char_is_slash:
+ * @c : character
+ *
+ * Checks if character (@c) is a slash.
+ *
+ * Returns: true (1) if character is a slash, otherwise false (0).
+ */
+#ifdef _WIN32
+#define PATH_CHAR_IS_SLASH(c) (((c) == '/') || ((c) == '\\'))
+#else
+#define PATH_CHAR_IS_SLASH(c) ((c) == '/')
+#endif
+
+/**
+ * path_default_slash and path_default_slash_c:
+ *
+ * Gets the default slash separator.
+ *
+ * Returns: default slash separator.
+ */
+#ifdef _WIN32
+#define PATH_DEFAULT_SLASH() "\\"
+#define PATH_DEFAULT_SLASH_C() '\\'
+#else
+#define PATH_DEFAULT_SLASH() "/"
+#define PATH_DEFAULT_SLASH_C() '/'
+#endif
+
+/**
+ * fill_pathname_slash:
+ * @path : path
+ * @size : size of path
+ *
+ * Assumes path is a directory. Appends a slash
+ * if not already there.
+ **/
+void fill_pathname_slash(char *path, size_t size);
+
+#if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
+void fill_pathname_application_path(char *buf, size_t size);
+void fill_pathname_application_dir(char *buf, size_t size);
+void fill_pathname_home_dir(char *buf, size_t size);
+#endif
+
+/**
+ * path_mkdir:
+ * @dir : directory
+ *
+ * Create directory on filesystem.
+ *
+ * Returns: true (1) if directory could be created, otherwise false (0).
+ **/
+bool path_mkdir(const char *dir);
+
+/**
+ * path_is_directory:
+ * @path : path
+ *
+ * Checks if path is a directory.
+ *
+ * Returns: true (1) if path is a directory, otherwise false (0).
+ */
+bool path_is_directory(const char *path);
+
+bool path_is_character_special(const char *path);
+
+int path_stat(const char *path);
+
+bool path_is_valid(const char *path);
+
+int32_t path_get_size(const char *path);
+
+bool is_path_accessible_using_standard_io(const char *path);
+
+RETRO_END_DECLS
+
+#endif
--- /dev/null
+/* Copyright (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (memmap.h).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _LIBRETRO_MEMMAP_H
+#define _LIBRETRO_MEMMAP_H
+
+#include <stdio.h>
+#include <stdint.h>
+
+#if defined(__CELLOS_LV2__) || defined(PSP) || defined(PS2) || defined(GEKKO) || defined(VITA) || defined(_XBOX) || defined(_3DS) || defined(WIIU) || defined(SWITCH) || defined(HAVE_LIBNX)
+/* No mman available */
+#elif defined(_WIN32) && !defined(_XBOX)
+#include <windows.h>
+#include <errno.h>
+#include <io.h>
+#else
+#define HAVE_MMAN
+#include <sys/mman.h>
+#endif
+
+#if !defined(HAVE_MMAN) || defined(_WIN32)
+void* mmap(void *addr, size_t len, int mmap_prot, int mmap_flags, int fildes, size_t off);
+
+int munmap(void *addr, size_t len);
+
+int mprotect(void *addr, size_t len, int prot);
+#endif
+
+int memsync(void *start, void *end);
+
+int memprotect(void *addr, size_t len);
+
+#endif
--- /dev/null
+/* Copyright (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (retro_assert.h).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __RETRO_ASSERT_H
+#define __RETRO_ASSERT_H
+
+#include <assert.h>
+
+#ifdef RARCH_INTERNAL
+#include <stdio.h>
+#define retro_assert(cond) do { \
+ if (!(cond)) { printf("Assertion failed at %s:%d.\n", __FILE__, __LINE__); abort(); } \
+} while(0)
+#else
+#define retro_assert(cond) assert(cond)
+#endif
+
+#endif
--- /dev/null
+/* Copyright (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (retro_environment.h).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __LIBRETRO_SDK_ENVIRONMENT_H
+#define __LIBRETRO_SDK_ENVIRONMENT_H
+
+/*
+This file is designed to create a normalized environment for compiling
+libretro-common's private implementations, or any other sources which might
+enjoy use of it's environment (RetroArch for instance).
+This should be an elaborately crafted environment so that sources don't
+need to be full of platform-specific workarounds.
+*/
+
+#if defined (__cplusplus)
+#if 0
+printf("This is C++, version %d.\n", __cplusplus);
+#endif
+/* The expected values would be
+ * 199711L, for ISO/IEC 14882:1998 or 14882:2003
+ */
+
+#elif defined(__STDC__)
+/* This is standard C. */
+
+#if (__STDC__ == 1)
+/* The implementation is ISO-conforming. */
+#define __STDC_ISO__
+#else
+/* The implementation is not ISO-conforming. */
+#endif
+
+#if defined(__STDC_VERSION__)
+#if (__STDC_VERSION__ >= 201112L)
+/* This is C11. */
+#define __STDC_C11__
+#elif (__STDC_VERSION__ >= 199901L)
+/* This is C99. */
+#define __STDC_C99__
+#elif (__STDC_VERSION__ >= 199409L)
+/* This is C89 with amendment 1. */
+#define __STDC_C89__
+#define __STDC_C89_AMENDMENT_1__
+#else
+/* This is C89 without amendment 1. */
+#define __STDC_C89__
+#endif
+#else /* !defined(__STDC_VERSION__) */
+/* This is C89. __STDC_VERSION__ is not defined. */
+#define __STDC_C89__
+#endif
+
+#else /* !defined(__STDC__) */
+/* This is not standard C. __STDC__ is not defined. */
+#endif
+
+#if defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
+/* Try to find out if we're compiling for WinRT or non-WinRT */
+#if defined(_MSC_VER) && defined(__has_include)
+#if __has_include(<winapifamily.h>)
+#define HAVE_WINAPIFAMILY_H 1
+#else
+#define HAVE_WINAPIFAMILY_H 0
+#endif
+
+/* If _USING_V110_SDK71_ is defined it means we are using the Windows XP toolset. */
+#elif defined(_MSC_VER) && (_MSC_VER >= 1700 && !_USING_V110_SDK71_) /* _MSC_VER == 1700 for Visual Studio 2012 */
+#define HAVE_WINAPIFAMILY_H 1
+#else
+#define HAVE_WINAPIFAMILY_H 0
+#endif
+
+#if HAVE_WINAPIFAMILY_H
+#include <winapifamily.h>
+#define WINAPI_FAMILY_WINRT (!WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP))
+#else
+#define WINAPI_FAMILY_WINRT 0
+#endif /* HAVE_WINAPIFAMILY_H */
+
+#if WINAPI_FAMILY_WINRT
+#undef __WINRT__
+#define __WINRT__ 1
+#endif
+
+/* MSVC obviously has to have some non-standard constants... */
+#if _M_IX86_FP == 1
+#define __SSE__ 1
+#elif _M_IX86_FP == 2 || (defined(_M_AMD64) || defined(_M_X64))
+#define __SSE__ 1
+#define __SSE2__ 1
+#endif
+
+#endif
+
+#endif
--- /dev/null
+/* Copyright (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (retro_miscellaneous.h).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __RARCH_MISCELLANEOUS_H
+#define __RARCH_MISCELLANEOUS_H
+
+#define RARCH_MAX_SUBSYSTEMS 10
+#define RARCH_MAX_SUBSYSTEM_ROMS 10
+
+#include <stdint.h>
+#include <boolean.h>
+#include <retro_inline.h>
+
+#if defined(_WIN32)
+
+#if defined(_XBOX)
+#include <Xtl.h>
+#else
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#endif
+
+#endif
+
+#if defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
+#include <sys/fs_external.h>
+#endif
+
+#include <limits.h>
+
+#ifdef _MSC_VER
+#include <compat/msvc.h>
+#endif
+
+static INLINE void bits_or_bits(uint32_t *a, uint32_t *b, uint32_t count)
+{
+ uint32_t i;
+ for (i = 0; i < count;i++)
+ a[i] |= b[i];
+}
+
+static INLINE void bits_clear_bits(uint32_t *a, uint32_t *b, uint32_t count)
+{
+ uint32_t i;
+ for (i = 0; i < count;i++)
+ a[i] &= ~b[i];
+}
+
+static INLINE bool bits_any_set(uint32_t* ptr, uint32_t count)
+{
+ uint32_t i;
+ for (i = 0; i < count; i++)
+ {
+ if (ptr[i] != 0)
+ return true;
+ }
+ return false;
+}
+
+#ifndef PATH_MAX_LENGTH
+#if defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
+#define PATH_MAX_LENGTH CELL_FS_MAX_FS_PATH_LENGTH
+#elif defined(_XBOX1) || defined(_3DS) || defined(PSP) || defined(PS2) || defined(GEKKO)|| defined(WIIU) || defined(ORBIS)
+#define PATH_MAX_LENGTH 512
+#else
+#define PATH_MAX_LENGTH 4096
+#endif
+#endif
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+#define BITS_GET_ELEM(a, i) ((a).data[i])
+#define BITS_GET_ELEM_PTR(a, i) ((a)->data[i])
+
+#define BIT_SET(a, bit) ((a)[(bit) >> 3] |= (1 << ((bit) & 7)))
+#define BIT_CLEAR(a, bit) ((a)[(bit) >> 3] &= ~(1 << ((bit) & 7)))
+#define BIT_GET(a, bit) (((a)[(bit) >> 3] >> ((bit) & 7)) & 1)
+
+#define BIT16_SET(a, bit) ((a) |= (1 << ((bit) & 15)))
+#define BIT16_CLEAR(a, bit) ((a) &= ~(1 << ((bit) & 15)))
+#define BIT16_GET(a, bit) (((a) >> ((bit) & 15)) & 1)
+#define BIT16_CLEAR_ALL(a) ((a) = 0)
+
+#define BIT32_SET(a, bit) ((a) |= (UINT32_C(1) << ((bit) & 31)))
+#define BIT32_CLEAR(a, bit) ((a) &= ~(UINT32_C(1) << ((bit) & 31)))
+#define BIT32_GET(a, bit) (((a) >> ((bit) & 31)) & 1)
+#define BIT32_CLEAR_ALL(a) ((a) = 0)
+
+#define BIT64_SET(a, bit) ((a) |= (UINT64_C(1) << ((bit) & 63)))
+#define BIT64_CLEAR(a, bit) ((a) &= ~(UINT64_C(1) << ((bit) & 63)))
+#define BIT64_GET(a, bit) (((a) >> ((bit) & 63)) & 1)
+#define BIT64_CLEAR_ALL(a) ((a) = 0)
+
+#define BIT128_SET(a, bit) ((a).data[(bit) >> 5] |= (UINT32_C(1) << ((bit) & 31)))
+#define BIT128_CLEAR(a, bit) ((a).data[(bit) >> 5] &= ~(UINT32_C(1) << ((bit) & 31)))
+#define BIT128_GET(a, bit) (((a).data[(bit) >> 5] >> ((bit) & 31)) & 1)
+#define BIT128_CLEAR_ALL(a) memset(&(a), 0, sizeof(a))
+
+#define BIT128_SET_PTR(a, bit) BIT128_SET(*a, bit)
+#define BIT128_CLEAR_PTR(a, bit) BIT128_CLEAR(*a, bit)
+#define BIT128_GET_PTR(a, bit) BIT128_GET(*a, bit)
+#define BIT128_CLEAR_ALL_PTR(a) BIT128_CLEAR_ALL(*a)
+
+#define BIT256_SET(a, bit) BIT128_SET(a, bit)
+#define BIT256_CLEAR(a, bit) BIT128_CLEAR(a, bit)
+#define BIT256_GET(a, bit) BIT128_GET(a, bit)
+#define BIT256_CLEAR_ALL(a) BIT128_CLEAR_ALL(a)
+
+#define BIT256_SET_PTR(a, bit) BIT256_SET(*a, bit)
+#define BIT256_CLEAR_PTR(a, bit) BIT256_CLEAR(*a, bit)
+#define BIT256_GET_PTR(a, bit) BIT256_GET(*a, bit)
+#define BIT256_CLEAR_ALL_PTR(a) BIT256_CLEAR_ALL(*a)
+
+#define BITS_COPY16_PTR(a,bits) \
+{ \
+ BIT128_CLEAR_ALL_PTR(a); \
+ BITS_GET_ELEM_PTR(a, 0) = (bits) & 0xffff; \
+}
+
+#define BITS_COPY32_PTR(a,bits) \
+{ \
+ BIT128_CLEAR_ALL_PTR(a); \
+ BITS_GET_ELEM_PTR(a, 0) = (bits); \
+}
+
+/* Helper macros and struct to keep track of many booleans. */
+/* This struct has 256 bits. */
+typedef struct
+{
+ uint32_t data[8];
+} retro_bits_t;
+
+#ifdef _WIN32
+# ifdef _WIN64
+# define PRI_SIZET PRIu64
+# else
+# if _MSC_VER == 1800
+# define PRI_SIZET PRIu32
+# else
+# define PRI_SIZET "u"
+# endif
+# endif
+#elif defined(PS2)
+# define PRI_SIZET "u"
+#else
+# if (SIZE_MAX == 0xFFFF)
+# define PRI_SIZET "hu"
+# elif (SIZE_MAX == 0xFFFFFFFF)
+# define PRI_SIZET "u"
+# elif (SIZE_MAX == 0xFFFFFFFFFFFFFFFF)
+# define PRI_SIZET "lu"
+# else
+# error PRI_SIZET: unknown SIZE_MAX
+# endif
+#endif
+
+#endif
--- /dev/null
+/* Copyright (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (file_stream.h).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __LIBRETRO_SDK_FILE_STREAM_H
+#define __LIBRETRO_SDK_FILE_STREAM_H
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stddef.h>
+
+#include <sys/types.h>
+
+#include <libretro.h>
+#include <retro_common_api.h>
+#include <retro_inline.h>
+#include <boolean.h>
+
+#include <stdarg.h>
+#include <vfs/vfs_implementation.h>
+
+#define FILESTREAM_REQUIRED_VFS_VERSION 2
+
+RETRO_BEGIN_DECLS
+
+typedef struct RFILE RFILE;
+
+#define FILESTREAM_REQUIRED_VFS_VERSION 2
+
+void filestream_vfs_init(const struct retro_vfs_interface_info* vfs_info);
+
+int64_t filestream_get_size(RFILE *stream);
+
+int64_t filestream_truncate(RFILE *stream, int64_t length);
+
+/**
+ * filestream_open:
+ * @path : path to file
+ * @mode : file mode to use when opening (read/write)
+ * @bufsize : optional buffer size (-1 or 0 to use default)
+ *
+ * Opens a file for reading or writing, depending on the requested mode.
+ * Returns a pointer to an RFILE if opened successfully, otherwise NULL.
+ **/
+RFILE* filestream_open(const char *path, unsigned mode, unsigned hints);
+
+int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position);
+
+int64_t filestream_read(RFILE *stream, void *data, int64_t len);
+
+int64_t filestream_write(RFILE *stream, const void *data, int64_t len);
+
+int64_t filestream_tell(RFILE *stream);
+
+void filestream_rewind(RFILE *stream);
+
+int filestream_close(RFILE *stream);
+
+int64_t filestream_read_file(const char *path, void **buf, int64_t *len);
+
+char* filestream_gets(RFILE *stream, char *s, size_t len);
+
+int filestream_getc(RFILE *stream);
+
+int filestream_scanf(RFILE *stream, const char* format, ...);
+
+int filestream_eof(RFILE *stream);
+
+bool filestream_write_file(const char *path, const void *data, int64_t size);
+
+int filestream_putc(RFILE *stream, int c);
+
+int filestream_vprintf(RFILE *stream, const char* format, va_list args);
+
+int filestream_printf(RFILE *stream, const char* format, ...);
+
+int filestream_error(RFILE *stream);
+
+int filestream_flush(RFILE *stream);
+
+int filestream_delete(const char *path);
+
+int filestream_rename(const char *old_path, const char *new_path);
+
+const char* filestream_get_path(RFILE *stream);
+
+bool filestream_exists(const char *path);
+
+/* Returned pointer must be freed by the caller. */
+char* filestream_getline(RFILE *stream);
+
+libretro_vfs_implementation_file* filestream_get_vfs_handle(RFILE *stream);
+
+RETRO_END_DECLS
+
+#endif
--- /dev/null
+/* Copyright (C) 2010-2020 The RetroArch team
+*
+* ---------------------------------------------------------------------------------------
+* The following license statement only applies to this file (file_stream_transforms.h).
+* ---------------------------------------------------------------------------------------
+*
+* Permission is hereby granted, free of charge,
+* to any person obtaining a copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation the rights to
+* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __LIBRETRO_SDK_FILE_STREAM_TRANSFORMS_H
+#define __LIBRETRO_SDK_FILE_STREAM_TRANSFORMS_H
+
+#include <stdint.h>
+#include <string.h>
+#include <retro_common_api.h>
+#include <streams/file_stream.h>
+
+RETRO_BEGIN_DECLS
+
+#ifndef SKIP_STDIO_REDEFINES
+
+#define FILE RFILE
+
+#undef fopen
+#undef fclose
+#undef ftell
+#undef fseek
+#undef fread
+#undef fgets
+#undef fgetc
+#undef fwrite
+#undef fputc
+#undef fflush
+#undef fprintf
+#undef ferror
+#undef feof
+#undef fscanf
+
+#define fopen rfopen
+#define fclose rfclose
+#define ftell rftell
+#define fseek rfseek
+#define fread rfread
+#define fgets rfgets
+#define fgetc rfgetc
+#define fwrite rfwrite
+#define fputc rfputc
+#define fflush rfflush
+#define fprintf rfprintf
+#define ferror rferror
+#define feof rfeof
+#define fscanf rfscanf
+
+#endif
+
+RFILE* rfopen(const char *path, const char *mode);
+
+int rfclose(RFILE* stream);
+
+int64_t rftell(RFILE* stream);
+
+int64_t rfseek(RFILE* stream, int64_t offset, int origin);
+
+int64_t rfread(void* buffer,
+ size_t elem_size, size_t elem_count, RFILE* stream);
+
+char *rfgets(char *buffer, int maxCount, RFILE* stream);
+
+int rfgetc(RFILE* stream);
+
+int64_t rfwrite(void const* buffer,
+ size_t elem_size, size_t elem_count, RFILE* stream);
+
+int rfputc(int character, RFILE * stream);
+
+int64_t rfflush(RFILE * stream);
+
+int rfprintf(RFILE * stream, const char * format, ...);
+
+int rferror(RFILE* stream);
+
+int rfeof(RFILE* stream);
+
+int rfscanf(RFILE * stream, const char * format, ...);
+
+RETRO_END_DECLS
+
+#endif
--- /dev/null
+/* Copyright (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (stdstring.h).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __LIBRETRO_SDK_STDSTRING_H
+#define __LIBRETRO_SDK_STDSTRING_H
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <string.h>
+#include <boolean.h>
+
+#include <retro_common_api.h>
+#include <retro_inline.h>
+#include <compat/strl.h>
+
+RETRO_BEGIN_DECLS
+
+#define STRLEN_CONST(x) ((sizeof((x))-1))
+
+#define strcpy_literal(a, b) strcpy(a, b)
+
+#define string_is_not_equal(a, b) !string_is_equal((a), (b))
+
+#define string_is_not_equal_fast(a, b, size) (memcmp(a, b, size) != 0)
+#define string_is_equal_fast(a, b, size) (memcmp(a, b, size) == 0)
+
+#define TOLOWER(c) (c | (lr_char_props[c] & 0x20))
+#define TOUPPER(c) (c & ~(lr_char_props[c] & 0x20))
+
+/* C standard says \f \v are space, but this one disagrees */
+#define ISSPACE(c) (lr_char_props[c] & 0x80)
+
+#define ISDIGIT(c) (lr_char_props[c] & 0x40)
+#define ISALPHA(c) (lr_char_props[c] & 0x20)
+#define ISLOWER(c) (lr_char_props[c] & 0x04)
+#define ISUPPER(c) (lr_char_props[c] & 0x02)
+#define ISALNUM(c) (lr_char_props[c] & 0x60)
+#define ISUALPHA(c) (lr_char_props[c] & 0x28)
+#define ISUALNUM(c) (lr_char_props[c] & 0x68)
+#define IS_XDIGIT(c) (lr_char_props[c] & 0x01)
+
+/* Deprecated alias, all callers should use string_is_equal_case_insensitive instead */
+#define string_is_equal_noncase string_is_equal_case_insensitive
+
+static INLINE bool string_is_empty(const char *data)
+{
+ return !data || (*data == '\0');
+}
+
+static INLINE bool string_is_equal(const char *a, const char *b)
+{
+ return (a && b) ? !strcmp(a, b) : false;
+}
+
+static INLINE bool string_starts_with_size(const char *str, const char *prefix,
+ size_t size)
+{
+ return (str && prefix) ? !strncmp(prefix, str, size) : false;
+}
+
+static INLINE bool string_starts_with(const char *str, const char *prefix)
+{
+ return (str && prefix) ? !strncmp(prefix, str, strlen(prefix)) : false;
+}
+
+static INLINE bool string_ends_with_size(const char *str, const char *suffix,
+ size_t str_len, size_t suffix_len)
+{
+ return (str_len < suffix_len) ? false :
+ !memcmp(suffix, str + (str_len - suffix_len), suffix_len);
+}
+
+static INLINE bool string_ends_with(const char *str, const char *suffix)
+{
+ if (!str || !suffix)
+ return false;
+ return string_ends_with_size(str, suffix, strlen(str), strlen(suffix));
+}
+
+/* Returns the length of 'str' (c.f. strlen()), but only
+ * checks the first 'size' characters
+ * - If 'str' is NULL, returns 0
+ * - If 'str' is not NULL and no '\0' character is found
+ * in the first 'size' characters, returns 'size' */
+static INLINE size_t strlen_size(const char *str, size_t size)
+{
+ size_t i = 0;
+ if (str)
+ while (i < size && str[i]) i++;
+ return i;
+}
+
+
+static INLINE bool string_is_equal_case_insensitive(const char *a,
+ const char *b)
+{
+ int result = 0;
+ const unsigned char *p1 = (const unsigned char*)a;
+ const unsigned char *p2 = (const unsigned char*)b;
+
+ if (!a || !b)
+ return false;
+ if (p1 == p2)
+ return true;
+
+ while ((result = tolower (*p1) - tolower (*p2++)) == 0)
+ if (*p1++ == '\0')
+ break;
+
+ return (result == 0);
+}
+
+char *string_to_upper(char *s);
+
+char *string_to_lower(char *s);
+
+char *string_ucwords(char *s);
+
+char *string_replace_substring(const char *in, const char *pattern,
+ const char *by);
+
+/* Remove leading whitespaces */
+char *string_trim_whitespace_left(char *const s);
+
+/* Remove trailing whitespaces */
+char *string_trim_whitespace_right(char *const s);
+
+/* Remove leading and trailing whitespaces */
+char *string_trim_whitespace(char *const s);
+
+/* max_lines == 0 means no limit */
+char *word_wrap(char *buffer, const char *string,
+ int line_width, bool unicode, unsigned max_lines);
+
+/* Splits string into tokens seperated by 'delim'
+ * > Returned token string must be free()'d
+ * > Returns NULL if token is not found
+ * > After each call, 'str' is set to the position after the
+ * last found token
+ * > Tokens *include* empty strings
+ * Usage example:
+ * char *str = "1,2,3,4,5,6,7,,,10,";
+ * char **str_ptr = &str;
+ * char *token = NULL;
+ * while ((token = string_tokenize(str_ptr, ",")))
+ * {
+ * printf("%s\n", token);
+ * free(token);
+ * token = NULL;
+ * }
+ */
+char* string_tokenize(char **str, const char *delim);
+
+/* Removes every instance of character 'c' from 'str' */
+void string_remove_all_chars(char *str, char c);
+
+/* Replaces every instance of character 'find' in 'str'
+ * with character 'replace' */
+void string_replace_all_chars(char *str, char find, char replace);
+
+/* Converts string to unsigned integer.
+ * Returns 0 if string is invalid */
+unsigned string_to_unsigned(const char *str);
+
+/* Converts hexadecimal string to unsigned integer.
+ * Handles optional leading '0x'.
+ * Returns 0 if string is invalid */
+unsigned string_hex_to_unsigned(const char *str);
+
+char *string_init(const char *src);
+
+void string_set(char **string, const char *src);
+
+extern const unsigned char lr_char_props[256];
+
+RETRO_END_DECLS
+
+#endif
--- /dev/null
+/* Copyright (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (rtime.h).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __LIBRETRO_SDK_RTIME_H__
+#define __LIBRETRO_SDK_RTIME_H__
+
+#include <retro_common_api.h>
+
+#include <stdint.h>
+#include <stddef.h>
+#include <time.h>
+
+RETRO_BEGIN_DECLS
+
+/* TODO/FIXME: Move all generic time handling functions
+ * to this file */
+
+/* Must be called before using rtime_localtime() */
+void rtime_init(void);
+
+/* Must be called upon program termination */
+void rtime_deinit(void);
+
+/* Thread-safe wrapper for localtime() */
+struct tm *rtime_localtime(const time_t *timep, struct tm *result);
+
+RETRO_END_DECLS
+
+#endif
--- /dev/null
+/* Copyright (C) 2010-2020 The RetroArch team
+*
+* ---------------------------------------------------------------------------------------
+* The following license statement only applies to this file (vfs_implementation.h).
+* ---------------------------------------------------------------------------------------
+*
+* Permission is hereby granted, free of charge,
+* to any person obtaining a copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation the rights to
+* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __LIBRETRO_SDK_VFS_H
+#define __LIBRETRO_SDK_VFS_H
+
+#include <retro_common_api.h>
+#include <boolean.h>
+
+#ifdef RARCH_INTERNAL
+#ifndef VFS_FRONTEND
+#define VFS_FRONTEND
+#endif
+#endif
+
+RETRO_BEGIN_DECLS
+
+#ifdef _WIN32
+typedef void* HANDLE;
+#endif
+
+#ifdef HAVE_CDROM
+typedef struct
+{
+ int64_t byte_pos;
+ char *cue_buf;
+ size_t cue_len;
+ unsigned cur_lba;
+ unsigned last_frame_lba;
+ unsigned char cur_min;
+ unsigned char cur_sec;
+ unsigned char cur_frame;
+ unsigned char cur_track;
+ unsigned char last_frame[2352];
+ char drive;
+ bool last_frame_valid;
+} vfs_cdrom_t;
+#endif
+
+enum vfs_scheme
+{
+ VFS_SCHEME_NONE = 0,
+ VFS_SCHEME_CDROM
+};
+
+#ifndef __WINRT__
+#ifdef VFS_FRONTEND
+struct retro_vfs_file_handle
+#else
+struct libretro_vfs_implementation_file
+#endif
+{
+#ifdef HAVE_CDROM
+ vfs_cdrom_t cdrom; /* int64_t alignment */
+#endif
+ int64_t size;
+ uint64_t mappos;
+ uint64_t mapsize;
+ FILE *fp;
+#ifdef _WIN32
+ HANDLE fh;
+#endif
+ char *buf;
+ char* orig_path;
+ uint8_t *mapped;
+ int fd;
+ unsigned hints;
+ enum vfs_scheme scheme;
+};
+#endif
+
+/* Replace the following symbol with something appropriate
+ * to signify the file is being compiled for a front end instead of a core.
+ * This allows the same code to act as reference implementation
+ * for VFS and as fallbacks for when the front end does not provide VFS functionality.
+ */
+
+#ifdef VFS_FRONTEND
+typedef struct retro_vfs_file_handle libretro_vfs_implementation_file;
+#else
+typedef struct libretro_vfs_implementation_file libretro_vfs_implementation_file;
+#endif
+
+#ifdef VFS_FRONTEND
+typedef struct retro_vfs_dir_handle libretro_vfs_implementation_dir;
+#else
+typedef struct libretro_vfs_implementation_dir libretro_vfs_implementation_dir;
+#endif
+
+RETRO_END_DECLS
+
+#endif
--- /dev/null
+/* Copyright (C) 2010-2020 The RetroArch team
+*
+* ---------------------------------------------------------------------------------------
+* The following license statement only applies to this file (vfs_implementation.h).
+* ---------------------------------------------------------------------------------------
+*
+* Permission is hereby granted, free of charge,
+* to any person obtaining a copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation the rights to
+* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __LIBRETRO_SDK_VFS_IMPLEMENTATION_H
+#define __LIBRETRO_SDK_VFS_IMPLEMENTATION_H
+
+#include <stdio.h>
+#include <stdint.h>
+#include <libretro.h>
+#include <retro_environment.h>
+#include <vfs/vfs.h>
+
+RETRO_BEGIN_DECLS
+
+libretro_vfs_implementation_file *retro_vfs_file_open_impl(const char *path, unsigned mode, unsigned hints);
+
+int retro_vfs_file_close_impl(libretro_vfs_implementation_file *stream);
+
+int retro_vfs_file_error_impl(libretro_vfs_implementation_file *stream);
+
+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_tell_impl(libretro_vfs_implementation_file *stream);
+
+int64_t retro_vfs_file_seek_impl(libretro_vfs_implementation_file *stream, int64_t offset, int seek_position);
+
+int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream, void *s, uint64_t len);
+
+int64_t retro_vfs_file_write_impl(libretro_vfs_implementation_file *stream, const void *s, uint64_t len);
+
+int retro_vfs_file_flush_impl(libretro_vfs_implementation_file *stream);
+
+int retro_vfs_file_remove_impl(const char *path);
+
+int retro_vfs_file_rename_impl(const char *old_path, const char *new_path);
+
+const char *retro_vfs_file_get_path_impl(libretro_vfs_implementation_file *stream);
+
+int retro_vfs_stat_impl(const char *path, int32_t *size);
+
+int retro_vfs_mkdir_impl(const char *dir);
+
+libretro_vfs_implementation_dir *retro_vfs_opendir_impl(const char *dir, bool include_hidden);
+
+bool retro_vfs_readdir_impl(libretro_vfs_implementation_dir *dirstream);
+
+const char *retro_vfs_dirent_get_name_impl(libretro_vfs_implementation_dir *dirstream);
+
+bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *dirstream);
+
+int retro_vfs_closedir_impl(libretro_vfs_implementation_dir *dirstream);
+
+RETRO_END_DECLS
+
+#endif
--- /dev/null
+/* Copyright (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (file_stream.c).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <errno.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef _MSC_VER
+#include <compat/msvc.h>
+#endif
+
+#include <string/stdstring.h>
+#include <streams/file_stream.h>
+#define VFS_FRONTEND
+#include <vfs/vfs_implementation.h>
+
+#define VFS_ERROR_RETURN_VALUE -1
+
+struct RFILE
+{
+ struct retro_vfs_file_handle *hfile;
+ bool error_flag;
+ bool eof_flag;
+};
+
+static retro_vfs_get_path_t filestream_get_path_cb = NULL;
+static retro_vfs_open_t filestream_open_cb = NULL;
+static retro_vfs_close_t filestream_close_cb = NULL;
+static retro_vfs_size_t filestream_size_cb = NULL;
+static retro_vfs_truncate_t filestream_truncate_cb = NULL;
+static retro_vfs_tell_t filestream_tell_cb = NULL;
+static retro_vfs_seek_t filestream_seek_cb = NULL;
+static retro_vfs_read_t filestream_read_cb = NULL;
+static retro_vfs_write_t filestream_write_cb = NULL;
+static retro_vfs_flush_t filestream_flush_cb = NULL;
+static retro_vfs_remove_t filestream_remove_cb = NULL;
+static retro_vfs_rename_t filestream_rename_cb = NULL;
+
+/* VFS Initialization */
+
+void filestream_vfs_init(const struct retro_vfs_interface_info* vfs_info)
+{
+ const struct retro_vfs_interface *
+ vfs_iface = vfs_info->iface;
+
+ filestream_get_path_cb = NULL;
+ filestream_open_cb = NULL;
+ filestream_close_cb = NULL;
+ filestream_tell_cb = NULL;
+ filestream_size_cb = NULL;
+ filestream_truncate_cb = NULL;
+ filestream_seek_cb = NULL;
+ filestream_read_cb = NULL;
+ filestream_write_cb = NULL;
+ filestream_flush_cb = NULL;
+ filestream_remove_cb = NULL;
+ filestream_rename_cb = NULL;
+
+ if (
+ (vfs_info->required_interface_version <
+ FILESTREAM_REQUIRED_VFS_VERSION)
+ || !vfs_iface)
+ return;
+
+ filestream_get_path_cb = vfs_iface->get_path;
+ filestream_open_cb = vfs_iface->open;
+ filestream_close_cb = vfs_iface->close;
+ filestream_size_cb = vfs_iface->size;
+ filestream_truncate_cb = vfs_iface->truncate;
+ filestream_tell_cb = vfs_iface->tell;
+ filestream_seek_cb = vfs_iface->seek;
+ filestream_read_cb = vfs_iface->read;
+ filestream_write_cb = vfs_iface->write;
+ filestream_flush_cb = vfs_iface->flush;
+ filestream_remove_cb = vfs_iface->remove;
+ filestream_rename_cb = vfs_iface->rename;
+}
+
+/* Callback wrappers */
+bool filestream_exists(const char *path)
+{
+ RFILE *dummy = NULL;
+
+ if (!path || !*path)
+ return false;
+
+ dummy = filestream_open(
+ path,
+ RETRO_VFS_FILE_ACCESS_READ,
+ RETRO_VFS_FILE_ACCESS_HINT_NONE);
+
+ if (!dummy)
+ return false;
+
+ if (filestream_close(dummy) != 0)
+ if (dummy)
+ free(dummy);
+
+ dummy = NULL;
+ return true;
+}
+
+int64_t filestream_get_size(RFILE *stream)
+{
+ int64_t output;
+
+ if (filestream_size_cb)
+ output = filestream_size_cb(stream->hfile);
+ else
+ output = retro_vfs_file_size_impl(
+ (libretro_vfs_implementation_file*)stream->hfile);
+
+ if (output == VFS_ERROR_RETURN_VALUE)
+ stream->error_flag = true;
+
+ return output;
+}
+
+int64_t filestream_truncate(RFILE *stream, int64_t length)
+{
+ int64_t output;
+
+ if (filestream_truncate_cb)
+ output = filestream_truncate_cb(stream->hfile, length);
+ else
+ output = retro_vfs_file_truncate_impl(
+ (libretro_vfs_implementation_file*)stream->hfile, length);
+
+ if (output == VFS_ERROR_RETURN_VALUE)
+ stream->error_flag = true;
+
+ return output;
+}
+
+/**
+ * filestream_open:
+ * @path : path to file
+ * @mode : file mode to use when opening (read/write)
+ * @hints :
+ *
+ * Opens a file for reading or writing, depending on the requested mode.
+ * Returns a pointer to an RFILE if opened successfully, otherwise NULL.
+ **/
+RFILE* filestream_open(const char *path, unsigned mode, unsigned hints)
+{
+ struct retro_vfs_file_handle *fp = NULL;
+ RFILE* output = NULL;
+
+ if (filestream_open_cb)
+ fp = (struct retro_vfs_file_handle*)
+ filestream_open_cb(path, mode, hints);
+ else
+ fp = (struct retro_vfs_file_handle*)
+ retro_vfs_file_open_impl(path, mode, hints);
+
+ if (!fp)
+ return NULL;
+
+ output = (RFILE*)malloc(sizeof(RFILE));
+ output->error_flag = false;
+ output->eof_flag = false;
+ output->hfile = fp;
+ return output;
+}
+
+char* filestream_gets(RFILE *stream, char *s, size_t len)
+{
+ int c = 0;
+ char *p = s;
+ if (!stream)
+ return NULL;
+
+ /* get max bytes or up to a newline */
+
+ for (len--; len > 0; len--)
+ {
+ if ((c = filestream_getc(stream)) == EOF)
+ break;
+ *p++ = c;
+ if (c == '\n')
+ break;
+ }
+ *p = 0;
+
+ if (p == s && c == EOF)
+ return NULL;
+ return (s);
+}
+
+int filestream_getc(RFILE *stream)
+{
+ char c = 0;
+ if (stream && filestream_read(stream, &c, 1) == 1)
+ return (int)(unsigned char)c;
+ return EOF;
+}
+
+int filestream_scanf(RFILE *stream, const char* format, ...)
+{
+ char buf[4096];
+ char subfmt[64];
+ va_list args;
+ const char * bufiter = buf;
+ int ret = 0;
+ int64_t startpos = filestream_tell(stream);
+ int64_t maxlen = filestream_read(stream, buf, sizeof(buf)-1);
+
+ if (maxlen <= 0)
+ return EOF;
+
+ buf[maxlen] = '\0';
+
+ va_start(args, format);
+
+ while (*format)
+ {
+ if (*format == '%')
+ {
+ int sublen;
+ char* subfmtiter = subfmt;
+ bool asterisk = false;
+
+ *subfmtiter++ = *format++; /* '%' */
+
+ /* %[*][width][length]specifier */
+
+ if (*format == '*')
+ {
+ asterisk = true;
+ *subfmtiter++ = *format++;
+ }
+
+ while (ISDIGIT((unsigned char)*format))
+ *subfmtiter++ = *format++; /* width */
+
+ /* length */
+ if (*format == 'h' || *format == 'l')
+ {
+ if (format[1] == format[0])
+ *subfmtiter++ = *format++;
+ *subfmtiter++ = *format++;
+ }
+ else if (
+ *format == 'j' ||
+ *format == 'z' ||
+ *format == 't' ||
+ *format == 'L')
+ {
+ *subfmtiter++ = *format++;
+ }
+
+ /* specifier - always a single character (except ]) */
+ if (*format == '[')
+ {
+ while (*format != ']')
+ *subfmtiter++ = *format++;
+ *subfmtiter++ = *format++;
+ }
+ else
+ *subfmtiter++ = *format++;
+
+ *subfmtiter++ = '%';
+ *subfmtiter++ = 'n';
+ *subfmtiter++ = '\0';
+
+ if (sizeof(void*) != sizeof(long*))
+ abort(); /* all pointers must have the same size */
+
+ if (asterisk)
+ {
+ int v = sscanf(bufiter, subfmt, &sublen);
+ if (v == EOF)
+ return EOF;
+ if (v != 0)
+ break;
+ }
+ else
+ {
+ int v = sscanf(bufiter, subfmt, va_arg(args, void*), &sublen);
+ if (v == EOF)
+ return EOF;
+ if (v != 1)
+ break;
+ }
+
+ ret++;
+ bufiter += sublen;
+ }
+ else if (isspace((unsigned char)*format))
+ {
+ while (isspace((unsigned char)*bufiter))
+ bufiter++;
+ format++;
+ }
+ else
+ {
+ if (*bufiter != *format)
+ break;
+ bufiter++;
+ format++;
+ }
+ }
+
+ va_end(args);
+ filestream_seek(stream, startpos+(bufiter-buf),
+ RETRO_VFS_SEEK_POSITION_START);
+
+ return ret;
+}
+
+int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position)
+{
+ int64_t output;
+
+ if (filestream_seek_cb)
+ output = filestream_seek_cb(stream->hfile, offset, seek_position);
+ else
+ output = retro_vfs_file_seek_impl(
+ (libretro_vfs_implementation_file*)stream->hfile,
+ offset, seek_position);
+
+ if (output == VFS_ERROR_RETURN_VALUE)
+ stream->error_flag = true;
+
+ stream->eof_flag = false;
+
+ return output;
+}
+
+int filestream_eof(RFILE *stream)
+{
+ return stream->eof_flag;
+}
+
+int64_t filestream_tell(RFILE *stream)
+{
+ int64_t output;
+
+ if (filestream_size_cb)
+ output = filestream_tell_cb(stream->hfile);
+ else
+ output = retro_vfs_file_tell_impl(
+ (libretro_vfs_implementation_file*)stream->hfile);
+
+ if (output == VFS_ERROR_RETURN_VALUE)
+ stream->error_flag = true;
+
+ return output;
+}
+
+void filestream_rewind(RFILE *stream)
+{
+ if (!stream)
+ return;
+ filestream_seek(stream, 0L, RETRO_VFS_SEEK_POSITION_START);
+ stream->error_flag = false;
+ stream->eof_flag = false;
+}
+
+int64_t filestream_read(RFILE *stream, void *s, int64_t len)
+{
+ int64_t output;
+
+ if (filestream_read_cb)
+ output = filestream_read_cb(stream->hfile, s, len);
+ else
+ output = retro_vfs_file_read_impl(
+ (libretro_vfs_implementation_file*)stream->hfile, s, len);
+
+ if (output == VFS_ERROR_RETURN_VALUE)
+ stream->error_flag = true;
+ if (output < len)
+ stream->eof_flag = true;
+
+ return output;
+}
+
+int filestream_flush(RFILE *stream)
+{
+ int output;
+
+ if (filestream_flush_cb)
+ output = filestream_flush_cb(stream->hfile);
+ else
+ output = retro_vfs_file_flush_impl(
+ (libretro_vfs_implementation_file*)stream->hfile);
+
+ if (output == VFS_ERROR_RETURN_VALUE)
+ stream->error_flag = true;
+
+ return output;
+}
+
+int filestream_delete(const char *path)
+{
+ if (filestream_remove_cb)
+ return filestream_remove_cb(path);
+
+ return retro_vfs_file_remove_impl(path);
+}
+
+int filestream_rename(const char *old_path, const char *new_path)
+{
+ if (filestream_rename_cb)
+ return filestream_rename_cb(old_path, new_path);
+
+ return retro_vfs_file_rename_impl(old_path, new_path);
+}
+
+const char* filestream_get_path(RFILE *stream)
+{
+ if (filestream_get_path_cb)
+ return filestream_get_path_cb(stream->hfile);
+
+ return retro_vfs_file_get_path_impl(
+ (libretro_vfs_implementation_file*)stream->hfile);
+}
+
+int64_t filestream_write(RFILE *stream, const void *s, int64_t len)
+{
+ int64_t output;
+
+ if (filestream_write_cb)
+ output = filestream_write_cb(stream->hfile, s, len);
+ else
+ output = retro_vfs_file_write_impl(
+ (libretro_vfs_implementation_file*)stream->hfile, s, len);
+
+ if (output == VFS_ERROR_RETURN_VALUE)
+ stream->error_flag = true;
+
+ return output;
+}
+
+int filestream_putc(RFILE *stream, int c)
+{
+ char c_char = (char)c;
+ if (!stream)
+ return EOF;
+ return filestream_write(stream, &c_char, 1) == 1
+ ? (int)(unsigned char)c
+ : EOF;
+}
+
+int filestream_vprintf(RFILE *stream, const char* format, va_list args)
+{
+ static char buffer[8 * 1024];
+ int64_t num_chars = vsnprintf(buffer, sizeof(buffer),
+ format, args);
+
+ if (num_chars < 0)
+ return -1;
+ else if (num_chars == 0)
+ return 0;
+
+ return (int)filestream_write(stream, buffer, num_chars);
+}
+
+int filestream_printf(RFILE *stream, const char* format, ...)
+{
+ va_list vl;
+ int result;
+ va_start(vl, format);
+ result = filestream_vprintf(stream, format, vl);
+ va_end(vl);
+ return result;
+}
+
+int filestream_error(RFILE *stream)
+{
+ if (stream && stream->error_flag)
+ return 1;
+ return 0;
+}
+
+int filestream_close(RFILE *stream)
+{
+ int output;
+ struct retro_vfs_file_handle* fp = stream->hfile;
+
+ if (filestream_close_cb)
+ output = filestream_close_cb(fp);
+ else
+ output = retro_vfs_file_close_impl(
+ (libretro_vfs_implementation_file*)fp);
+
+ if (output == 0)
+ free(stream);
+
+ return output;
+}
+
+/**
+ * filestream_read_file:
+ * @path : path to file.
+ * @buf : buffer to allocate and read the contents of the
+ * file into. Needs to be freed manually.
+ *
+ * Read the contents of a file into @buf.
+ *
+ * Returns: number of items read, -1 on error.
+ */
+int64_t filestream_read_file(const char *path, void **buf, int64_t *len)
+{
+ int64_t ret = 0;
+ int64_t content_buf_size = 0;
+ void *content_buf = NULL;
+ RFILE *file = filestream_open(path,
+ RETRO_VFS_FILE_ACCESS_READ,
+ RETRO_VFS_FILE_ACCESS_HINT_NONE);
+
+ if (!file)
+ {
+ *buf = NULL;
+ return 0;
+ }
+
+ content_buf_size = filestream_get_size(file);
+
+ if (content_buf_size < 0)
+ goto error;
+
+ content_buf = malloc((size_t)(content_buf_size + 1));
+
+ if (!content_buf)
+ goto error;
+ if ((int64_t)(uint64_t)(content_buf_size + 1) != (content_buf_size + 1))
+ goto error;
+
+ ret = filestream_read(file, content_buf, (int64_t)content_buf_size);
+ if (ret < 0)
+ goto error;
+
+ if (filestream_close(file) != 0)
+ if (file)
+ free(file);
+
+ *buf = content_buf;
+
+ /* Allow for easy reading of strings to be safe.
+ * Will only work with sane character formatting (Unix). */
+ ((char*)content_buf)[ret] = '\0';
+
+ if (len)
+ *len = ret;
+
+ return 1;
+
+error:
+ if (file)
+ if (filestream_close(file) != 0)
+ free(file);
+ if (content_buf)
+ free(content_buf);
+ if (len)
+ *len = -1;
+ *buf = NULL;
+ return 0;
+}
+
+/**
+ * filestream_write_file:
+ * @path : path to file.
+ * @data : contents to write to the file.
+ * @size : size of the contents.
+ *
+ * Writes data to a file.
+ *
+ * Returns: true (1) on success, false (0) otherwise.
+ */
+bool filestream_write_file(const char *path, const void *data, int64_t size)
+{
+ int64_t ret = 0;
+ RFILE *file = filestream_open(path,
+ RETRO_VFS_FILE_ACCESS_WRITE,
+ RETRO_VFS_FILE_ACCESS_HINT_NONE);
+ if (!file)
+ return false;
+
+ ret = filestream_write(file, data, size);
+ if (filestream_close(file) != 0)
+ if (file)
+ free(file);
+
+ if (ret != size)
+ return false;
+
+ return true;
+}
+
+/* Returned pointer must be freed by the caller. */
+char* filestream_getline(RFILE *stream)
+{
+ char *newline_tmp = NULL;
+ size_t cur_size = 8;
+ size_t idx = 0;
+ int in = 0;
+ char *newline = (char*)malloc(9);
+
+ if (!stream || !newline)
+ {
+ if (newline)
+ free(newline);
+ return NULL;
+ }
+
+ in = filestream_getc(stream);
+
+ while (in != EOF && in != '\n')
+ {
+ if (idx == cur_size)
+ {
+ cur_size *= 2;
+ newline_tmp = (char*)realloc(newline, cur_size + 1);
+
+ if (!newline_tmp)
+ {
+ free(newline);
+ return NULL;
+ }
+
+ newline = newline_tmp;
+ }
+
+ newline[idx++] = in;
+ in = filestream_getc(stream);
+ }
+
+ newline[idx] = '\0';
+ return newline;
+}
+
+libretro_vfs_implementation_file* filestream_get_vfs_handle(RFILE *stream)
+{
+ return (libretro_vfs_implementation_file*)stream->hfile;
+}
--- /dev/null
+/* Copyright (C) 2010-2020 The RetroArch team
+*
+* ---------------------------------------------------------------------------------------
+* The following license statement only applies to this file (file_stream_transforms.c).
+* ---------------------------------------------------------------------------------------
+*
+* Permission is hereby granted, free of charge,
+* to any person obtaining a copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation the rights to
+* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <string.h>
+#include <stdarg.h>
+
+#include <libretro.h>
+#include <streams/file_stream.h>
+
+RFILE* rfopen(const char *path, const char *mode)
+{
+ RFILE *output = NULL;
+ unsigned int retro_mode = RETRO_VFS_FILE_ACCESS_READ;
+ bool position_to_end = false;
+
+ if (strstr(mode, "r"))
+ {
+ retro_mode = RETRO_VFS_FILE_ACCESS_READ;
+ if (strstr(mode, "+"))
+ {
+ retro_mode = RETRO_VFS_FILE_ACCESS_READ_WRITE |
+ RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING;
+ }
+ }
+ else if (strstr(mode, "w"))
+ {
+ retro_mode = RETRO_VFS_FILE_ACCESS_WRITE;
+ if (strstr(mode, "+"))
+ retro_mode = RETRO_VFS_FILE_ACCESS_READ_WRITE;
+ }
+ else if (strstr(mode, "a"))
+ {
+ retro_mode = RETRO_VFS_FILE_ACCESS_WRITE |
+ RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING;
+ position_to_end = true;
+ if (strstr(mode, "+"))
+ {
+ retro_mode = RETRO_VFS_FILE_ACCESS_READ_WRITE |
+ RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING;
+ }
+ }
+
+ output = filestream_open(path, retro_mode,
+ RETRO_VFS_FILE_ACCESS_HINT_NONE);
+ if (output && position_to_end)
+ filestream_seek(output, 0, RETRO_VFS_SEEK_POSITION_END);
+
+ return output;
+}
+
+int rfclose(RFILE* stream)
+{
+ return filestream_close(stream);
+}
+
+int64_t rftell(RFILE* stream)
+{
+ return filestream_tell(stream);
+}
+
+int64_t rfseek(RFILE* stream, int64_t offset, int origin)
+{
+ int seek_position = -1;
+ switch (origin)
+ {
+ case SEEK_SET:
+ seek_position = RETRO_VFS_SEEK_POSITION_START;
+ break;
+ case SEEK_CUR:
+ seek_position = RETRO_VFS_SEEK_POSITION_CURRENT;
+ break;
+ case SEEK_END:
+ seek_position = RETRO_VFS_SEEK_POSITION_END;
+ break;
+ }
+
+ return filestream_seek(stream, offset, seek_position);
+}
+
+int64_t rfread(void* buffer,
+ size_t elem_size, size_t elem_count, RFILE* stream)
+{
+ return (filestream_read(stream, buffer, elem_size * elem_count) / elem_size);
+}
+
+char *rfgets(char *buffer, int maxCount, RFILE* stream)
+{
+ return filestream_gets(stream, buffer, maxCount);
+}
+
+int rfgetc(RFILE* stream)
+{
+ return filestream_getc(stream);
+}
+
+int64_t rfwrite(void const* buffer,
+ size_t elem_size, size_t elem_count, RFILE* stream)
+{
+ return filestream_write(stream, buffer, elem_size * elem_count);
+}
+
+int rfputc(int character, RFILE * stream)
+{
+ return filestream_putc(stream, character);
+}
+
+int64_t rfflush(RFILE * stream)
+{
+ return filestream_flush(stream);
+}
+
+int rfprintf(RFILE * stream, const char * format, ...)
+{
+ int result;
+ va_list vl;
+ va_start(vl, format);
+ result = filestream_vprintf(stream, format, vl);
+ va_end(vl);
+ return result;
+}
+
+int rferror(RFILE* stream)
+{
+ return filestream_error(stream);
+}
+
+int rfeof(RFILE* stream)
+{
+ return filestream_eof(stream);
+}
+
+int rfscanf(RFILE * stream, const char * format, ...)
+{
+ int result;
+ va_list vl;
+ va_start(vl, format);
+ result = filestream_scanf(stream, format, vl);
+ va_end(vl);
+ return result;
+}
--- /dev/null
+/* Copyright (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (stdstring.c).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <ctype.h>
+
+#include <string/stdstring.h>
+#include <encodings/utf.h>
+
+const uint8_t lr_char_props[256] = {
+ /*x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x80,0x00,0x00, /* 0x */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 1x */
+ 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 2x !"#$%&'()*+,-./ */
+ 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x00,0x00,0x00,0x00,0x00,0x00, /* 3x 0123456789:;<=>? */
+ 0x00,0x23,0x23,0x23,0x23,0x23,0x23,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, /* 4x @ABCDEFGHIJKLMNO */
+ 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x00,0x00,0x00,0x00,0x08, /* 5x PQRSTUVWXYZ[\]^_ */
+ 0x00,0x25,0x25,0x25,0x25,0x25,0x25,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, /* 6x `abcdefghijklmno */
+ 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x00,0x00,0x00,0x00,0x00, /* 7x pqrstuvwxyz{|}~ */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 8x */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 9x */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* Ax */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* Bx */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* Cx */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* Dx */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* Ex */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* Fx */
+};
+
+char *string_init(const char *src)
+{
+ return src ? strdup(src) : NULL;
+}
+
+void string_set(char **string, const char *src)
+{
+ free(*string);
+ *string = string_init(src);
+}
+
+
+char *string_to_upper(char *s)
+{
+ char *cs = (char *)s;
+ for ( ; *cs != '\0'; cs++)
+ *cs = toupper((unsigned char)*cs);
+ return s;
+}
+
+char *string_to_lower(char *s)
+{
+ char *cs = (char *)s;
+ for ( ; *cs != '\0'; cs++)
+ *cs = tolower((unsigned char)*cs);
+ return s;
+}
+
+char *string_ucwords(char *s)
+{
+ char *cs = (char *)s;
+ for ( ; *cs != '\0'; cs++)
+ {
+ if (*cs == ' ')
+ *(cs+1) = toupper((unsigned char)*(cs+1));
+ }
+
+ s[0] = toupper((unsigned char)s[0]);
+ return s;
+}
+
+char *string_replace_substring(const char *in,
+ const char *pattern, const char *replacement)
+{
+ size_t numhits, pattern_len, replacement_len, outlen;
+ const char *inat = NULL;
+ const char *inprev = NULL;
+ char *out = NULL;
+ char *outat = NULL;
+
+ /* if either pattern or replacement is NULL,
+ * duplicate in and let caller handle it. */
+ if (!pattern || !replacement)
+ return strdup(in);
+
+ pattern_len = strlen(pattern);
+ replacement_len = strlen(replacement);
+ numhits = 0;
+ inat = in;
+
+ while ((inat = strstr(inat, pattern)))
+ {
+ inat += pattern_len;
+ numhits++;
+ }
+
+ outlen = strlen(in) - pattern_len*numhits + replacement_len*numhits;
+ out = (char *)malloc(outlen+1);
+
+ if (!out)
+ return NULL;
+
+ outat = out;
+ inat = in;
+ inprev = in;
+
+ while ((inat = strstr(inat, pattern)))
+ {
+ memcpy(outat, inprev, inat-inprev);
+ outat += inat-inprev;
+ memcpy(outat, replacement, replacement_len);
+ outat += replacement_len;
+ inat += pattern_len;
+ inprev = inat;
+ }
+ strcpy(outat, inprev);
+
+ return out;
+}
+
+/* Remove leading whitespaces */
+char *string_trim_whitespace_left(char *const s)
+{
+ if (s && *s)
+ {
+ size_t len = strlen(s);
+ char *current = s;
+
+ while (*current && ISSPACE((unsigned char)*current))
+ {
+ ++current;
+ --len;
+ }
+
+ if (s != current)
+ memmove(s, current, len + 1);
+ }
+
+ return s;
+}
+
+/* Remove trailing whitespaces */
+char *string_trim_whitespace_right(char *const s)
+{
+ if (s && *s)
+ {
+ size_t len = strlen(s);
+ char *current = s + len - 1;
+
+ while (current != s && ISSPACE((unsigned char)*current))
+ {
+ --current;
+ --len;
+ }
+
+ current[ISSPACE((unsigned char)*current) ? 0 : 1] = '\0';
+ }
+
+ return s;
+}
+
+/* Remove leading and trailing whitespaces */
+char *string_trim_whitespace(char *const s)
+{
+ string_trim_whitespace_right(s); /* order matters */
+ string_trim_whitespace_left(s);
+
+ return s;
+}
+
+char *word_wrap(char* buffer, const char *string, int line_width, bool unicode, unsigned max_lines)
+{
+ unsigned i = 0;
+ unsigned len = (unsigned)strlen(string);
+ unsigned lines = 1;
+
+ while (i < len)
+ {
+ unsigned counter;
+ int pos = (int)(&buffer[i] - buffer);
+
+ /* copy string until the end of the line is reached */
+ for (counter = 1; counter <= (unsigned)line_width; counter++)
+ {
+ const char *character;
+ unsigned char_len;
+ unsigned j = i;
+
+ /* check if end of string reached */
+ if (i == len)
+ {
+ buffer[i] = 0;
+ return buffer;
+ }
+
+ character = utf8skip(&string[i], 1);
+ char_len = (unsigned)(character - &string[i]);
+
+ if (!unicode)
+ 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))
+ {
+ buffer[i] = '\n';
+ i++;
+ lines++;
+ }
+ }
+ 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;
+
+ return buffer;
+}
+
+/* Splits string into tokens seperated by 'delim'
+ * > Returned token string must be free()'d
+ * > Returns NULL if token is not found
+ * > After each call, 'str' is set to the position after the
+ * last found token
+ * > Tokens *include* empty strings
+ * Usage example:
+ * char *str = "1,2,3,4,5,6,7,,,10,";
+ * char **str_ptr = &str;
+ * char *token = NULL;
+ * while ((token = string_tokenize(str_ptr, ",")))
+ * {
+ * printf("%s\n", token);
+ * free(token);
+ * token = NULL;
+ * }
+ */
+char* string_tokenize(char **str, const char *delim)
+{
+ /* Taken from https://codereview.stackexchange.com/questions/216956/strtok-function-thread-safe-supports-empty-tokens-doesnt-change-string# */
+ char *str_ptr = NULL;
+ char *delim_ptr = NULL;
+ char *token = NULL;
+ size_t token_len = 0;
+
+ /* Sanity checks */
+ if (!str || string_is_empty(delim))
+ return NULL;
+
+ str_ptr = *str;
+
+ /* Note: we don't check string_is_empty() here,
+ * empty strings are valid */
+ if (!str_ptr)
+ return NULL;
+
+ /* Search for delimiter */
+ delim_ptr = strstr(str_ptr, delim);
+
+ if (delim_ptr)
+ token_len = delim_ptr - str_ptr;
+ else
+ token_len = strlen(str_ptr);
+
+ /* Allocate token string */
+ token = (char *)malloc((token_len + 1) * sizeof(char));
+
+ if (!token)
+ return NULL;
+
+ /* Copy token */
+ strlcpy(token, str_ptr, (token_len + 1) * sizeof(char));
+ token[token_len] = '\0';
+
+ /* Update input string pointer */
+ *str = delim_ptr ? delim_ptr + strlen(delim) : NULL;
+
+ return token;
+}
+
+/* Removes every instance of character 'c' from 'str' */
+void string_remove_all_chars(char *str, char c)
+{
+ char *read_ptr = NULL;
+ char *write_ptr = NULL;
+
+ if (string_is_empty(str))
+ return;
+
+ read_ptr = str;
+ write_ptr = str;
+
+ while (*read_ptr != '\0')
+ {
+ *write_ptr = *read_ptr++;
+ write_ptr += (*write_ptr != c) ? 1 : 0;
+ }
+
+ *write_ptr = '\0';
+}
+
+/* Replaces every instance of character 'find' in 'str'
+ * with character 'replace' */
+void string_replace_all_chars(char *str, char find, char replace)
+{
+ char *str_ptr = str;
+
+ if (string_is_empty(str))
+ return;
+
+ while ((str_ptr = strchr(str_ptr, find)))
+ *str_ptr++ = replace;
+}
+
+/* Converts string to unsigned integer.
+ * Returns 0 if string is invalid */
+unsigned string_to_unsigned(const char *str)
+{
+ const char *ptr = NULL;
+
+ if (string_is_empty(str))
+ return 0;
+
+ for (ptr = str; *ptr != '\0'; ptr++)
+ {
+ if (!ISDIGIT((unsigned char)*ptr))
+ return 0;
+ }
+
+ return (unsigned)strtoul(str, NULL, 10);
+}
+
+/* Converts hexadecimal string to unsigned integer.
+ * Handles optional leading '0x'.
+ * Returns 0 if string is invalid */
+unsigned string_hex_to_unsigned(const char *str)
+{
+ const char *hex_str = str;
+ const char *ptr = NULL;
+ size_t len;
+
+ if (string_is_empty(str))
+ return 0;
+
+ /* Remove leading '0x', if required */
+ len = strlen(str);
+
+ if (len >= 2)
+ if ((str[0] == '0') &&
+ ((str[1] == 'x') || (str[1] == 'X')))
+ hex_str = str + 2;
+
+ if (string_is_empty(hex_str))
+ return 0;
+
+ /* Check for valid characters */
+ for (ptr = hex_str; *ptr != '\0'; ptr++)
+ {
+ if (!isxdigit((unsigned char)*ptr))
+ return 0;
+ }
+
+ return (unsigned)strtoul(hex_str, NULL, 16);
+}
--- /dev/null
+/* Copyright (C) 2010-2020 The RetroArch team
+ *
+ * ---------------------------------------------------------------------------------------
+ * The following license statement only applies to this file (rtime.c).
+ * ---------------------------------------------------------------------------------------
+ *
+ * Permission is hereby granted, free of charge,
+ * to any person obtaining a copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifdef HAVE_THREADS
+#include <rthreads/rthreads.h>
+#include <retro_assert.h>
+#include <stdlib.h>
+#endif
+
+#include <string.h>
+#include <time/rtime.h>
+
+#ifdef HAVE_THREADS
+/* TODO/FIXME - global */
+slock_t *rtime_localtime_lock = NULL;
+#endif
+
+/* Must be called before using rtime_localtime() */
+void rtime_init(void)
+{
+ rtime_deinit();
+#ifdef HAVE_THREADS
+ if (!rtime_localtime_lock)
+ rtime_localtime_lock = slock_new();
+
+ retro_assert(rtime_localtime_lock);
+#endif
+}
+
+/* Must be called upon program termination */
+void rtime_deinit(void)
+{
+#ifdef HAVE_THREADS
+ if (rtime_localtime_lock)
+ {
+ slock_free(rtime_localtime_lock);
+ rtime_localtime_lock = NULL;
+ }
+#endif
+}
+
+/* Thread-safe wrapper for localtime() */
+struct tm *rtime_localtime(const time_t *timep, struct tm *result)
+{
+ struct tm *time_info = NULL;
+
+ /* Lock mutex */
+#ifdef HAVE_THREADS
+ slock_lock(rtime_localtime_lock);
+#endif
+
+ time_info = localtime(timep);
+ if (time_info)
+ memcpy(result, time_info, sizeof(struct tm));
+
+ /* Unlock mutex */
+#ifdef HAVE_THREADS
+ slock_unlock(rtime_localtime_lock);
+#endif
+
+ return result;
+}
--- /dev/null
+/* Copyright (C) 2010-2020 The RetroArch team
+*
+* ---------------------------------------------------------------------------------------
+* The following license statement only applies to this file (vfs_implementation.c).
+* ---------------------------------------------------------------------------------------
+*
+* Permission is hereby granted, free of charge,
+* to any person obtaining a copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation the rights to
+* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include <string/stdstring.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if defined(_WIN32)
+# ifdef _MSC_VER
+# define setmode _setmode
+# endif
+#include <sys/stat.h>
+# ifdef _XBOX
+# include <xtl.h>
+# define INVALID_FILE_ATTRIBUTES -1
+# else
+
+# include <fcntl.h>
+# include <direct.h>
+# include <windows.h>
+# endif
+# include <io.h>
+#else
+# if defined(PSP)
+# include <pspiofilemgr.h>
+# endif
+# include <sys/types.h>
+# include <sys/stat.h>
+# if !defined(VITA)
+# include <dirent.h>
+# endif
+# include <unistd.h>
+# if defined(ORBIS)
+# include <sys/fcntl.h>
+# include <sys/dirent.h>
+# include <orbisFile.h>
+# endif
+#endif
+
+#if defined (__CELLOS_LV2__) && !defined(__PSL1GHT__)
+#include <cell/cell_fs.h>
+#define O_RDONLY CELL_FS_O_RDONLY
+#define O_WRONLY CELL_FS_O_WRONLY
+#define O_CREAT CELL_FS_O_CREAT
+#define O_TRUNC CELL_FS_O_TRUNC
+#define O_RDWR CELL_FS_O_RDWR
+#else
+#include <fcntl.h>
+#endif
+
+/* TODO: Some things are duplicated but I'm really afraid of breaking other platforms by touching this */
+#if defined(VITA)
+# include <psp2/io/fcntl.h>
+# include <psp2/io/dirent.h>
+# include <psp2/io/stat.h>
+#elif defined(ORBIS)
+# include <orbisFile.h>
+# include <ps4link.h>
+# include <sys/dirent.h>
+# include <sys/fcntl.h>
+#elif !defined(_WIN32)
+# if defined(PSP)
+# include <pspiofilemgr.h>
+# endif
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <dirent.h>
+# include <unistd.h>
+#endif
+
+#if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(__QNX__) || defined(PSP)
+#include <unistd.h> /* stat() is defined here */
+#endif
+
+#ifdef __APPLE__
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+#ifdef __HAIKU__
+#include <kernel/image.h>
+#endif
+#ifndef __MACH__
+#include <compat/strl.h>
+#include <compat/posix_string.h>
+#endif
+#include <compat/strcasestr.h>
+#include <retro_miscellaneous.h>
+#include <encodings/utf.h>
+
+#if defined(_WIN32)
+#ifndef _XBOX
+#if defined(_MSC_VER) && _MSC_VER <= 1200
+#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
+#endif
+#endif
+#elif defined(VITA)
+#define SCE_ERROR_ERRNO_EEXIST 0x80010011
+#include <psp2/io/fcntl.h>
+#include <psp2/io/dirent.h>
+#include <psp2/io/stat.h>
+#else
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#endif
+
+#if defined(ORBIS)
+#include <orbisFile.h>
+#include <sys/fcntl.h>
+#include <sys/dirent.h>
+#endif
+#if defined(PSP)
+#include <pspkernel.h>
+#endif
+
+#if defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
+#include <cell/cell_fs.h>
+#endif
+
+#if defined(VITA)
+#define FIO_S_ISDIR SCE_S_ISDIR
+#endif
+
+#if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(__QNX__) || defined(PSP)
+#include <unistd.h> /* stat() is defined here */
+#endif
+
+/* Assume W-functions do not work below Win2K and Xbox platforms */
+#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
+
+#ifndef LEGACY_WIN32
+#define LEGACY_WIN32
+#endif
+
+#endif
+
+#if defined(_WIN32)
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+#define ATLEAST_VC2005
+#endif
+#endif
+
+#include <vfs/vfs_implementation.h>
+#include <libretro.h>
+#include <memmap.h>
+#include <encodings/utf.h>
+#include <compat/fopen_utf8.h>
+#include <file/file_path.h>
+
+#ifdef HAVE_CDROM
+#include <vfs/vfs_implementation_cdrom.h>
+#endif
+
+#if (defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE - 0) >= 200112) || (defined(__POSIX_VISIBLE) && __POSIX_VISIBLE >= 200112) || (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112) || __USE_LARGEFILE || (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64)
+#ifndef HAVE_64BIT_OFFSETS
+#define HAVE_64BIT_OFFSETS
+#endif
+#endif
+
+#define RFILE_HINT_UNBUFFERED (1 << 8)
+
+int64_t retro_vfs_file_seek_internal(
+ libretro_vfs_implementation_file *stream,
+ int64_t offset, int whence)
+{
+ if (!stream)
+ return -1;
+
+ if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
+ {
+#ifdef HAVE_CDROM
+ if (stream->scheme == VFS_SCHEME_CDROM)
+ return retro_vfs_file_seek_cdrom(stream, offset, whence);
+#endif
+#ifdef ATLEAST_VC2005
+ /* VC2005 and up have a special 64-bit fseek */
+ return _fseeki64(stream->fp, offset, whence);
+#elif defined(ORBIS)
+ {
+ int ret = orbisLseek(stream->fd, offset, whence);
+ if (ret < 0)
+ return -1;
+ return 0;
+ }
+#elif defined(HAVE_64BIT_OFFSETS)
+ return fseeko(stream->fp, (off_t)offset, whence);
+#else
+ return fseek(stream->fp, (long)offset, whence);
+#endif
+ }
+#ifdef HAVE_MMAP
+ /* Need to check stream->mapped because this function is
+ * called in filestream_open() */
+ if (stream->mapped && stream->hints &
+ RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
+ {
+ /* fseek() returns error on under/overflow but
+ * allows cursor > EOF for
+ read-only file descriptors. */
+ switch (whence)
+ {
+ case SEEK_SET:
+ if (offset < 0)
+ return -1;
+
+ stream->mappos = offset;
+ break;
+
+ case SEEK_CUR:
+ if ( (offset < 0 && stream->mappos + offset > stream->mappos) ||
+ (offset > 0 && stream->mappos + offset < stream->mappos))
+ return -1;
+
+ stream->mappos += offset;
+ break;
+
+ case SEEK_END:
+ if (stream->mapsize + offset < stream->mapsize)
+ return -1;
+
+ stream->mappos = stream->mapsize + offset;
+ break;
+ }
+ return stream->mappos;
+ }
+#endif
+
+ if (lseek(stream->fd, (off_t)offset, whence) < 0)
+ return -1;
+
+ return 0;
+}
+
+/**
+ * retro_vfs_file_open_impl:
+ * @path : path to file
+ * @mode : file mode to use when opening (read/write)
+ * @hints :
+ *
+ * Opens a file for reading or writing, depending on the requested mode.
+ * Returns a pointer to an RFILE if opened successfully, otherwise NULL.
+ **/
+
+libretro_vfs_implementation_file *retro_vfs_file_open_impl(
+ const char *path, unsigned mode, unsigned hints)
+{
+#if defined(VFS_FRONTEND) || defined(HAVE_CDROM)
+ int path_len = (int)strlen(path);
+#endif
+#ifdef VFS_FRONTEND
+ const char *dumb_prefix = "vfsonly://";
+ size_t dumb_prefix_siz = STRLEN_CONST("vfsonly://");
+ int dumb_prefix_len = (int)dumb_prefix_siz;
+#endif
+#ifdef HAVE_CDROM
+ const char *cdrom_prefix = "cdrom://";
+ size_t cdrom_prefix_siz = STRLEN_CONST("cdrom://");
+ int cdrom_prefix_len = (int)cdrom_prefix_siz;
+#endif
+ int flags = 0;
+ const char *mode_str = NULL;
+ libretro_vfs_implementation_file *stream =
+ (libretro_vfs_implementation_file*)
+ malloc(sizeof(*stream));
+
+ if (!stream)
+ return NULL;
+
+ stream->fd = 0;
+ stream->hints = hints;
+ stream->size = 0;
+ stream->buf = NULL;
+ stream->fp = NULL;
+#ifdef _WIN32
+ stream->fh = 0;
+#endif
+ stream->orig_path = NULL;
+ stream->mappos = 0;
+ stream->mapsize = 0;
+ stream->mapped = NULL;
+ stream->scheme = VFS_SCHEME_NONE;
+
+#ifdef VFS_FRONTEND
+ if (path_len >= dumb_prefix_len)
+ if (!memcmp(path, dumb_prefix, dumb_prefix_len))
+ path += dumb_prefix_siz;
+#endif
+
+#ifdef HAVE_CDROM
+ stream->cdrom.cue_buf = NULL;
+ stream->cdrom.cue_len = 0;
+ stream->cdrom.byte_pos = 0;
+ stream->cdrom.drive = 0;
+ stream->cdrom.cur_min = 0;
+ stream->cdrom.cur_sec = 0;
+ stream->cdrom.cur_frame = 0;
+ stream->cdrom.cur_track = 0;
+ stream->cdrom.cur_lba = 0;
+ stream->cdrom.last_frame_lba = 0;
+ stream->cdrom.last_frame[0] = '\0';
+ stream->cdrom.last_frame_valid = false;
+
+ if (path_len > cdrom_prefix_len)
+ {
+ if (!memcmp(path, cdrom_prefix, cdrom_prefix_len))
+ {
+ path += cdrom_prefix_siz;
+ stream->scheme = VFS_SCHEME_CDROM;
+ }
+ }
+#endif
+
+ stream->orig_path = strdup(path);
+
+#ifdef HAVE_MMAP
+ if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS && mode == RETRO_VFS_FILE_ACCESS_READ)
+ stream->hints |= RFILE_HINT_UNBUFFERED;
+ else
+#endif
+ stream->hints &= ~RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS;
+
+ switch (mode)
+ {
+ case RETRO_VFS_FILE_ACCESS_READ:
+ mode_str = "rb";
+
+ flags = O_RDONLY;
+#ifdef _WIN32
+ flags |= O_BINARY;
+#endif
+ break;
+
+ case RETRO_VFS_FILE_ACCESS_WRITE:
+ mode_str = "wb";
+
+ flags = O_WRONLY | O_CREAT | O_TRUNC;
+#if !defined(ORBIS)
+#if !defined(_WIN32)
+ flags |= S_IRUSR | S_IWUSR;
+#else
+ flags |= O_BINARY;
+#endif
+#endif
+ break;
+
+ case RETRO_VFS_FILE_ACCESS_READ_WRITE:
+ mode_str = "w+b";
+ flags = O_RDWR | O_CREAT | O_TRUNC;
+#if !defined(ORBIS)
+#if !defined(_WIN32)
+ flags |= S_IRUSR | S_IWUSR;
+#else
+ flags |= O_BINARY;
+#endif
+#endif
+ break;
+
+ case RETRO_VFS_FILE_ACCESS_WRITE | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING:
+ case RETRO_VFS_FILE_ACCESS_READ_WRITE | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING:
+ mode_str = "r+b";
+
+ flags = O_RDWR;
+#if !defined(ORBIS)
+#if !defined(_WIN32)
+ flags |= S_IRUSR | S_IWUSR;
+#else
+ flags |= O_BINARY;
+#endif
+#endif
+ break;
+
+ default:
+ goto error;
+ }
+
+ if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
+ {
+#ifdef ORBIS
+ int fd = orbisOpen(path, flags, 0644);
+ if (fd < 0)
+ {
+ stream->fd = -1;
+ goto error;
+ }
+ stream->fd = fd;
+#else
+ FILE *fp;
+#ifdef HAVE_CDROM
+ if (stream->scheme == VFS_SCHEME_CDROM)
+ {
+ retro_vfs_file_open_cdrom(stream, path, mode, hints);
+#if defined(_WIN32) && !defined(_XBOX)
+ if (!stream->fh)
+ goto error;
+#else
+ if (!stream->fp)
+ goto error;
+#endif
+ }
+ else
+#endif
+ {
+ fp = (FILE*)fopen_utf8(path, mode_str);
+
+ if (!fp)
+ goto error;
+
+ stream->fp = fp;
+ }
+ /* Regarding setvbuf:
+ *
+ * https://www.freebsd.org/cgi/man.cgi?query=setvbuf&apropos=0&sektion=0&manpath=FreeBSD+11.1-RELEASE&arch=default&format=html
+ *
+ * If the size argument is not zero but buf is NULL,
+ * a buffer of the given size will be allocated immediately, and
+ * released on close. This is an extension to ANSI C.
+ *
+ * Since C89 does not support specifying a NULL buffer
+ * with a non-zero size, we create and track our own buffer for it.
+ */
+ /* TODO: this is only useful for a few platforms,
+ * find which and add ifdef */
+#if defined(_3DS)
+ if (stream->scheme != VFS_SCHEME_CDROM)
+ {
+ stream->buf = (char*)calloc(1, 0x10000);
+ if (stream->fp)
+ setvbuf(stream->fp, stream->buf, _IOFBF, 0x10000);
+ }
+#elif !defined(PSP)
+ if (stream->scheme != VFS_SCHEME_CDROM)
+ {
+ stream->buf = (char*)calloc(1, 0x4000);
+ if (stream->fp)
+ setvbuf(stream->fp, stream->buf, _IOFBF, 0x4000);
+ }
+#endif
+#endif
+ }
+ else
+ {
+#if defined(_WIN32) && !defined(_XBOX)
+#if defined(LEGACY_WIN32)
+ char *path_local = utf8_to_local_string_alloc(path);
+ stream->fd = open(path_local, flags, 0);
+ if (path_local)
+ free(path_local);
+#else
+ wchar_t * path_wide = utf8_to_utf16_string_alloc(path);
+ stream->fd = _wopen(path_wide, flags, 0);
+ if (path_wide)
+ free(path_wide);
+#endif
+#else
+ stream->fd = open(path, flags, 0);
+#endif
+
+ if (stream->fd == -1)
+ goto error;
+
+#ifdef HAVE_MMAP
+ if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
+ {
+ stream->mappos = 0;
+ stream->mapped = NULL;
+ stream->mapsize = retro_vfs_file_seek_internal(stream, 0, SEEK_END);
+
+ if (stream->mapsize == (uint64_t)-1)
+ goto error;
+
+ retro_vfs_file_seek_internal(stream, 0, SEEK_SET);
+
+ stream->mapped = (uint8_t*)mmap((void*)0,
+ stream->mapsize, PROT_READ, MAP_SHARED, stream->fd, 0);
+
+ if (stream->mapped == MAP_FAILED)
+ stream->hints &= ~RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS;
+ }
+#endif
+ }
+#ifdef ORBIS
+ stream->size = orbisLseek(stream->fd, 0, SEEK_END);
+ orbisLseek(stream->fd, 0, SEEK_SET);
+#else
+#ifdef HAVE_CDROM
+ if (stream->scheme == VFS_SCHEME_CDROM)
+ {
+ retro_vfs_file_seek_cdrom(stream, 0, SEEK_SET);
+ retro_vfs_file_seek_cdrom(stream, 0, SEEK_END);
+
+ stream->size = retro_vfs_file_tell_impl(stream);
+
+ retro_vfs_file_seek_cdrom(stream, 0, SEEK_SET);
+ }
+ else
+#endif
+ {
+ retro_vfs_file_seek_internal(stream, 0, SEEK_SET);
+ retro_vfs_file_seek_internal(stream, 0, SEEK_END);
+
+ stream->size = retro_vfs_file_tell_impl(stream);
+
+ retro_vfs_file_seek_internal(stream, 0, SEEK_SET);
+ }
+#endif
+ return stream;
+
+error:
+ retro_vfs_file_close_impl(stream);
+ return NULL;
+}
+
+int retro_vfs_file_close_impl(libretro_vfs_implementation_file *stream)
+{
+ if (!stream)
+ return -1;
+
+#ifdef HAVE_CDROM
+ if (stream->scheme == VFS_SCHEME_CDROM)
+ {
+ retro_vfs_file_close_cdrom(stream);
+ goto end;
+ }
+#endif
+
+ if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
+ {
+ if (stream->fp)
+ fclose(stream->fp);
+ }
+ else
+ {
+#ifdef HAVE_MMAP
+ if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
+ munmap(stream->mapped, stream->mapsize);
+#endif
+ }
+
+ if (stream->fd > 0)
+ {
+#ifdef ORBIS
+ orbisClose(stream->fd);
+ stream->fd = -1;
+#else
+ close(stream->fd);
+#endif
+ }
+#ifdef HAVE_CDROM
+end:
+ if (stream->cdrom.cue_buf)
+ free(stream->cdrom.cue_buf);
+#endif
+ if (stream->buf)
+ free(stream->buf);
+
+ if (stream->orig_path)
+ free(stream->orig_path);
+
+ free(stream);
+
+ return 0;
+}
+
+int retro_vfs_file_error_impl(libretro_vfs_implementation_file *stream)
+{
+#ifdef HAVE_CDROM
+ if (stream->scheme == VFS_SCHEME_CDROM)
+ return retro_vfs_file_error_cdrom(stream);
+#endif
+#ifdef ORBIS
+ /* TODO/FIXME - implement this? */
+ return 0;
+#else
+ return ferror(stream->fp);
+#endif
+}
+
+int64_t retro_vfs_file_size_impl(libretro_vfs_implementation_file *stream)
+{
+ if (stream)
+ return stream->size;
+ return 0;
+}
+
+int64_t retro_vfs_file_truncate_impl(libretro_vfs_implementation_file *stream, int64_t length)
+{
+ if (!stream)
+ return -1;
+
+#ifdef _WIN32
+ if (_chsize(_fileno(stream->fp), length) != 0)
+ return -1;
+#elif !defined(VITA) && !defined(PSP) && !defined(PS2) && !defined(ORBIS) && (!defined(SWITCH) || defined(HAVE_LIBNX))
+ if (ftruncate(fileno(stream->fp), (off_t)length) != 0)
+ return -1;
+#endif
+
+ return 0;
+}
+
+int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream)
+{
+ if (!stream)
+ return -1;
+
+ if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
+ {
+#ifdef HAVE_CDROM
+ if (stream->scheme == VFS_SCHEME_CDROM)
+ return retro_vfs_file_tell_cdrom(stream);
+#endif
+#ifdef ORBIS
+ {
+ int64_t ret = orbisLseek(stream->fd, 0, SEEK_CUR);
+ if (ret < 0)
+ return -1;
+ return ret;
+ }
+#else
+#ifdef ATLEAST_VC2005
+ /* VC2005 and up have a special 64-bit ftell */
+ return _ftelli64(stream->fp);
+#elif defined(HAVE_64BIT_OFFSETS)
+ return ftello(stream->fp);
+#else
+ return ftell(stream->fp);
+#endif
+#endif
+ }
+#ifdef HAVE_MMAP
+ /* Need to check stream->mapped because this function
+ * is called in filestream_open() */
+ if (stream->mapped && stream->hints &
+ RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
+ return stream->mappos;
+#endif
+ if (lseek(stream->fd, 0, SEEK_CUR) < 0)
+ return -1;
+
+ return 0;
+}
+
+int64_t retro_vfs_file_seek_impl(libretro_vfs_implementation_file *stream,
+ int64_t offset, int seek_position)
+{
+ int whence = -1;
+ switch (seek_position)
+ {
+ case RETRO_VFS_SEEK_POSITION_START:
+ whence = SEEK_SET;
+ break;
+ case RETRO_VFS_SEEK_POSITION_CURRENT:
+ whence = SEEK_CUR;
+ break;
+ case RETRO_VFS_SEEK_POSITION_END:
+ whence = SEEK_END;
+ break;
+ }
+
+ return retro_vfs_file_seek_internal(stream, offset, whence);
+}
+
+int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream,
+ void *s, uint64_t len)
+{
+ if (!stream || !s)
+ return -1;
+
+ if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
+ {
+#ifdef HAVE_CDROM
+ if (stream->scheme == VFS_SCHEME_CDROM)
+ return retro_vfs_file_read_cdrom(stream, s, len);
+#endif
+#ifdef ORBIS
+ if (orbisRead(stream->fd, s, (size_t)len) < 0)
+ return -1;
+ return 0;
+#else
+ return fread(s, 1, (size_t)len, stream->fp);
+#endif
+ }
+#ifdef HAVE_MMAP
+ if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
+ {
+ if (stream->mappos > stream->mapsize)
+ return -1;
+
+ if (stream->mappos + len > stream->mapsize)
+ len = stream->mapsize - stream->mappos;
+
+ memcpy(s, &stream->mapped[stream->mappos], len);
+ stream->mappos += len;
+
+ return len;
+ }
+#endif
+
+ return read(stream->fd, s, (size_t)len);
+}
+
+int64_t retro_vfs_file_write_impl(libretro_vfs_implementation_file *stream, const void *s, uint64_t len)
+{
+ if (!stream)
+ return -1;
+
+ if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
+ {
+#ifdef ORBIS
+ if (orbisWrite(stream->fd, s, (size_t)len) < 0)
+ return -1;
+ return 0;
+#else
+ return fwrite(s, 1, (size_t)len, stream->fp);
+#endif
+ }
+
+#ifdef HAVE_MMAP
+ if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
+ return -1;
+#endif
+ return write(stream->fd, s, (size_t)len);
+}
+
+int retro_vfs_file_flush_impl(libretro_vfs_implementation_file *stream)
+{
+ if (!stream)
+ return -1;
+#ifdef ORBIS
+ return 0;
+#else
+ return fflush(stream->fp) == 0 ? 0 : -1;
+#endif
+}
+
+int retro_vfs_file_remove_impl(const char *path)
+{
+#if defined(_WIN32) && !defined(_XBOX)
+ /* Win32 (no Xbox) */
+
+#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500
+ char *path_local = NULL;
+#else
+ wchar_t *path_wide = NULL;
+#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)
+ 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;
+#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)
+{
+#if defined(_WIN32) && !defined(_XBOX)
+ /* Win32 (no Xbox) */
+ int ret = -1;
+#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500
+ char *old_path_local = NULL;
+#else
+ wchar_t *old_path_wide = NULL;
+#endif
+
+ if (!old_path || !*old_path || !new_path || !*new_path)
+ return -1;
+
+#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500
+ old_path_local = utf8_to_local_string_alloc(old_path);
+
+ if (old_path_local)
+ {
+ char *new_path_local = utf8_to_local_string_alloc(new_path);
+
+ if (new_path_local)
+ {
+ if (rename(old_path_local, new_path_local) == 0)
+ ret = 0;
+ free(new_path_local);
+ }
+
+ free(old_path_local);
+ }
+#else
+ old_path_wide = utf8_to_utf16_string_alloc(old_path);
+
+ if (old_path_wide)
+ {
+ wchar_t *new_path_wide = utf8_to_utf16_string_alloc(new_path);
+
+ if (new_path_wide)
+ {
+ if (_wrename(old_path_wide, new_path_wide) == 0)
+ ret = 0;
+ free(new_path_wide);
+ }
+
+ free(old_path_wide);
+ }
+#endif
+ return ret;
+
+#elif defined(ORBIS)
+ /* Orbis */
+ /* TODO/FIXME - Stub for now */
+ if (!old_path || !*old_path || !new_path || !*new_path)
+ return -1;
+ return 0;
+
+#else
+ /* Every other platform */
+ if (!old_path || !*old_path || !new_path || !*new_path)
+ return -1;
+ return rename(old_path, new_path) == 0 ? 0 : -1;
+#endif
+}
+
+const char *retro_vfs_file_get_path_impl(
+ libretro_vfs_implementation_file *stream)
+{
+ /* should never happen, do something noisy so caller can be fixed */
+ if (!stream)
+ abort();
+ return stream->orig_path;
+}
+
+int retro_vfs_stat_impl(const char *path, int32_t *size)
+{
+ bool is_dir = false;
+ bool is_character_special = false;
+#if defined(VITA) || defined(PSP)
+ /* Vita / PSP */
+ SceIoStat buf;
+ int dir_ret;
+ char *tmp = NULL;
+ size_t len = 0;
+
+ if (!path || !*path)
+ return 0;
+
+ tmp = strdup(path);
+ len = strlen(tmp);
+ if (tmp[len-1] == '/')
+ tmp[len-1] = '\0';
+
+ dir_ret = sceIoGetstat(tmp, &buf);
+ free(tmp);
+ if (dir_ret < 0)
+ return 0;
+
+ if (size)
+ *size = (int32_t)buf.st_size;
+
+ is_dir = FIO_S_ISDIR(buf.st_mode);
+#elif defined(ORBIS)
+ /* Orbis */
+ int dir_ret = 0;
+
+ if (!path || !*path)
+ return 0;
+
+ if (size)
+ *size = (int32_t)buf.st_size;
+
+ dir_ret = orbisDopen(path);
+ is_dir = dir_ret > 0;
+ orbisDclose(dir_ret);
+
+ is_character_special = S_ISCHR(buf.st_mode);
+#elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
+ /* CellOS Lv2 */
+ CellFsStat buf;
+
+ if (!path || !*path)
+ return 0;
+ if (cellFsStat(path, &buf) < 0)
+ return 0;
+
+ if (size)
+ *size = (int32_t)buf.st_size;
+
+ is_dir = ((buf.st_mode & S_IFMT) == S_IFDIR);
+
+#elif defined(_WIN32)
+ /* Windows */
+ DWORD file_info;
+ struct _stat buf;
+#if defined(LEGACY_WIN32)
+ char *path_local = NULL;
+#else
+ wchar_t *path_wide = NULL;
+#endif
+
+ if (!path || !*path)
+ return 0;
+
+#if defined(LEGACY_WIN32)
+ path_local = utf8_to_local_string_alloc(path);
+ file_info = GetFileAttributes(path_local);
+
+ if (!string_is_empty(path_local))
+ _stat(path_local, &buf);
+
+ if (path_local)
+ free(path_local);
+#else
+ path_wide = utf8_to_utf16_string_alloc(path);
+ file_info = GetFileAttributesW(path_wide);
+
+ _wstat(path_wide, &buf);
+
+ if (path_wide)
+ free(path_wide);
+#endif
+
+ if (file_info == INVALID_FILE_ATTRIBUTES)
+ return 0;
+
+ if (size)
+ *size = (int32_t)buf.st_size;
+
+ is_dir = (file_info & FILE_ATTRIBUTE_DIRECTORY);
+
+#elif defined(GEKKO)
+ /* On GEKKO platforms, paths cannot have
+ * trailing slashes - we must therefore
+ * remove them */
+ char *path_buf = NULL;
+ int stat_ret = -1;
+ struct stat stat_buf;
+ size_t len;
+
+ if (string_is_empty(path))
+ return 0;
+
+ path_buf = strdup(path);
+ if (!path_buf)
+ return 0;
+
+ len = strlen(path_buf);
+ if (len > 0)
+ if (path_buf[len - 1] == '/')
+ path_buf[len - 1] = '\0';
+
+ stat_ret = stat(path_buf, &stat_buf);
+ free(path_buf);
+
+ if (stat_ret < 0)
+ return 0;
+
+ if (size)
+ *size = (int32_t)stat_buf.st_size;
+
+ is_dir = S_ISDIR(stat_buf.st_mode);
+ is_character_special = S_ISCHR(stat_buf.st_mode);
+
+#else
+ /* Every other platform */
+ struct stat buf;
+
+ if (!path || !*path)
+ return 0;
+ if (stat(path, &buf) < 0)
+ return 0;
+
+ if (size)
+ *size = (int32_t)buf.st_size;
+
+ is_dir = S_ISDIR(buf.st_mode);
+ is_character_special = S_ISCHR(buf.st_mode);
+#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);
+}
+
+#if defined(VITA)
+#define path_mkdir_error(ret) (((ret) == SCE_ERROR_ERRNO_EEXIST))
+#elif defined(PSP) || defined(PS2) || defined(_3DS) || defined(WIIU) || defined(SWITCH) || defined(ORBIS)
+#define path_mkdir_error(ret) ((ret) == -1)
+#else
+#define path_mkdir_error(ret) ((ret) < 0 && errno == EEXIST)
+#endif
+
+int retro_vfs_mkdir_impl(const char *dir)
+{
+#if defined(_WIN32)
+#ifdef LEGACY_WIN32
+ int ret = _mkdir(dir);
+#else
+ wchar_t *dir_w = utf8_to_utf16_string_alloc(dir);
+ int ret = -1;
+
+ if (dir_w)
+ {
+ ret = _wmkdir(dir_w);
+ free(dir_w);
+ }
+#endif
+#elif defined(IOS)
+ int ret = mkdir(dir, 0755);
+#elif defined(VITA) || defined(PSP)
+ int ret = sceIoMkdir(dir, 0777);
+#elif defined(ORBIS)
+ int ret = orbisMkdir(dir, 0755);
+#elif defined(__QNX__)
+ int ret = mkdir(dir, 0777);
+#elif defined(GEKKO)
+ /* On GEKKO platforms, mkdir() fails if
+ * the path has a trailing slash. We must
+ * therefore remove it. */
+ int ret = -1;
+ if (!string_is_empty(dir))
+ {
+ char *dir_buf = strdup(dir);
+
+ if (dir_buf)
+ {
+ size_t len = strlen(dir_buf);
+
+ if (len > 0)
+ if (dir_buf[len - 1] == '/')
+ dir_buf[len - 1] = '\0';
+
+ ret = mkdir(dir_buf, 0750);
+
+ free(dir_buf);
+ }
+ }
+#else
+ int ret = mkdir(dir, 0750);
+#endif
+
+ if (path_mkdir_error(ret))
+ return -2;
+ return ret < 0 ? -1 : 0;
+}
+
+#ifdef VFS_FRONTEND
+struct retro_vfs_dir_handle
+#else
+struct libretro_vfs_implementation_dir
+#endif
+{
+ char* orig_path;
+#if defined(_WIN32)
+#if defined(LEGACY_WIN32)
+ WIN32_FIND_DATA entry;
+#else
+ WIN32_FIND_DATAW entry;
+#endif
+ HANDLE directory;
+ bool next;
+ char path[PATH_MAX_LENGTH];
+#elif defined(VITA) || defined(PSP)
+ SceUID directory;
+ SceIoDirent entry;
+#elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
+ CellFsErrno error;
+ int directory;
+ CellFsDirent entry;
+#elif defined(ORBIS)
+ int directory;
+ struct dirent entry;
+#else
+ DIR *directory;
+ const struct dirent *entry;
+#endif
+};
+
+static bool dirent_check_error(libretro_vfs_implementation_dir *rdir)
+{
+#if defined(_WIN32)
+ return (rdir->directory == INVALID_HANDLE_VALUE);
+#elif defined(VITA) || defined(PSP) || defined(ORBIS)
+ return (rdir->directory < 0);
+#elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
+ return (rdir->error != CELL_FS_SUCCEEDED);
+#else
+ return !(rdir->directory);
+#endif
+}
+
+libretro_vfs_implementation_dir *retro_vfs_opendir_impl(
+ const char *name, bool include_hidden)
+{
+#if defined(_WIN32)
+ unsigned path_len;
+ char path_buf[1024];
+ size_t copied = 0;
+#if defined(LEGACY_WIN32)
+ char *path_local = NULL;
+#else
+ wchar_t *path_wide = NULL;
+#endif
+#endif
+ libretro_vfs_implementation_dir *rdir;
+
+ /*Reject null or empty string paths*/
+ if (!name || (*name == 0))
+ return NULL;
+
+ /*Allocate RDIR struct. Tidied later with retro_closedir*/
+ rdir = (libretro_vfs_implementation_dir*)calloc(1, sizeof(*rdir));
+ if (!rdir)
+ return NULL;
+
+ rdir->orig_path = strdup(name);
+
+#if defined(_WIN32)
+ path_buf[0] = '\0';
+ path_len = strlen(name);
+
+ copied = strlcpy(path_buf, name, sizeof(path_buf));
+
+ /* Non-NT platforms don't like extra slashes in the path */
+ if (name[path_len - 1] != '\\')
+ path_buf[copied++] = '\\';
+
+ path_buf[copied] = '*';
+ path_buf[copied+1] = '\0';
+
+#if defined(LEGACY_WIN32)
+ path_local = utf8_to_local_string_alloc(path_buf);
+ rdir->directory = FindFirstFile(path_local, &rdir->entry);
+
+ if (path_local)
+ free(path_local);
+#else
+ path_wide = utf8_to_utf16_string_alloc(path_buf);
+ rdir->directory = FindFirstFileW(path_wide, &rdir->entry);
+
+ if (path_wide)
+ free(path_wide);
+#endif
+
+#elif defined(VITA) || defined(PSP)
+ rdir->directory = sceIoDopen(name);
+#elif defined(_3DS)
+ rdir->directory = !string_is_empty(name) ? opendir(name) : NULL;
+ rdir->entry = NULL;
+#elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
+ rdir->error = cellFsOpendir(name, &rdir->directory);
+#elif defined(ORBIS)
+ rdir->directory = orbisDopen(name);
+#else
+ rdir->directory = opendir(name);
+ rdir->entry = NULL;
+#endif
+
+#ifdef _WIN32
+ if (include_hidden)
+ rdir->entry.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
+ else
+ rdir->entry.dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
+#endif
+
+ if (rdir->directory && !dirent_check_error(rdir))
+ return rdir;
+
+ retro_vfs_closedir_impl(rdir);
+ return NULL;
+}
+
+bool retro_vfs_readdir_impl(libretro_vfs_implementation_dir *rdir)
+{
+#if defined(_WIN32)
+ if (rdir->next)
+#if defined(LEGACY_WIN32)
+ return (FindNextFile(rdir->directory, &rdir->entry) != 0);
+#else
+ return (FindNextFileW(rdir->directory, &rdir->entry) != 0);
+#endif
+
+ rdir->next = true;
+ return (rdir->directory != INVALID_HANDLE_VALUE);
+#elif defined(VITA) || defined(PSP)
+ return (sceIoDread(rdir->directory, &rdir->entry) > 0);
+#elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
+ uint64_t nread;
+ rdir->error = cellFsReaddir(rdir->directory, &rdir->entry, &nread);
+ return (nread != 0);
+#elif defined(ORBIS)
+ return (orbisDread(rdir->directory, &rdir->entry) > 0);
+#else
+ return ((rdir->entry = readdir(rdir->directory)) != NULL);
+#endif
+}
+
+const char *retro_vfs_dirent_get_name_impl(libretro_vfs_implementation_dir *rdir)
+{
+#if defined(_WIN32)
+#if defined(LEGACY_WIN32)
+ char *name = local_to_utf8_string_alloc(rdir->entry.cFileName);
+#else
+ char *name = utf16_to_utf8_string_alloc(rdir->entry.cFileName);
+#endif
+ memset(rdir->entry.cFileName, 0, sizeof(rdir->entry.cFileName));
+ strlcpy((char*)rdir->entry.cFileName, name, sizeof(rdir->entry.cFileName));
+ if (name)
+ free(name);
+ return (char*)rdir->entry.cFileName;
+#elif defined(VITA) || defined(PSP) || defined(__CELLOS_LV2__) && !defined(__PSL1GHT__) || defined(ORBIS)
+ return rdir->entry.d_name;
+#else
+ if (!rdir || !rdir->entry)
+ return NULL;
+ return rdir->entry->d_name;
+#endif
+}
+
+bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *rdir)
+{
+#if defined(_WIN32)
+ const WIN32_FIND_DATA *entry = (const WIN32_FIND_DATA*)&rdir->entry;
+ return entry->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
+#elif defined(PSP) || defined(VITA)
+ const SceIoDirent *entry = (const SceIoDirent*)&rdir->entry;
+#if defined(PSP)
+ return (entry->d_stat.st_attr & FIO_SO_IFDIR) == FIO_SO_IFDIR;
+#elif defined(VITA)
+ return SCE_S_ISDIR(entry->d_stat.st_mode);
+#endif
+#elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
+ CellFsDirent *entry = (CellFsDirent*)&rdir->entry;
+ return (entry->d_type == CELL_FS_TYPE_DIRECTORY);
+#elif defined(ORBIS)
+ const struct dirent *entry = &rdir->entry;
+ if (entry->d_type == DT_DIR)
+ return true;
+ if (!(entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK))
+ return false;
+#else
+ struct stat buf;
+ char path[PATH_MAX_LENGTH];
+#if defined(DT_DIR)
+ const struct dirent *entry = (const struct dirent*)rdir->entry;
+ if (entry->d_type == DT_DIR)
+ return true;
+ /* This can happen on certain file systems. */
+ if (!(entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK))
+ return false;
+#endif
+ /* dirent struct doesn't have d_type, do it the slow way ... */
+ path[0] = '\0';
+ fill_pathname_join(path, rdir->orig_path, retro_vfs_dirent_get_name_impl(rdir), sizeof(path));
+ if (stat(path, &buf) < 0)
+ return false;
+ return S_ISDIR(buf.st_mode);
+#endif
+}
+
+int retro_vfs_closedir_impl(libretro_vfs_implementation_dir *rdir)
+{
+ if (!rdir)
+ return -1;
+
+#if defined(_WIN32)
+ if (rdir->directory != INVALID_HANDLE_VALUE)
+ FindClose(rdir->directory);
+#elif defined(VITA) || defined(PSP)
+ sceIoDclose(rdir->directory);
+#elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
+ rdir->error = cellFsClosedir(rdir->directory);
+#elif defined(ORBIS)
+ orbisDclose(rdir->directory);
+#else
+ if (rdir->directory)
+ closedir(rdir->directory);
+#endif
+
+ if (rdir->orig_path)
+ free(rdir->orig_path);
+ free(rdir);
+ return 0;
+}
* See the COPYING file in the top-level directory.
*/
-#include <compat/fopen_utf8.h>
-
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int i, ret;
FILE *f;
- f = fopen_utf8(cd_fname, "rb");
+ f = fopen(cd_fname, "rb");
if (f == NULL) {
err("missing file: %s: ", cd_fname);
perror(NULL);
char *ext;
FILE *f = NULL;
+ printf("%s cd_file=%d\n", __func__, cd_file == NULL ? 0 : 1);
+
if (cd_file != NULL)
return 0; // it's already open
return -1;
}
- f = fopen_utf8(table_fname, "rb");
+ f = fopen(table_fname, "rb");
if (f == NULL) {
err("missing file: %s: ", table_fname);
perror(NULL);
break;
}
- cd_file = fopen_utf8(cd_fname, "rb");
+ cd_file = fopen(cd_fname, "rb");
if (cd_file == NULL) {
err("failed to open: %s: ", table_fname);
perror(NULL);