Update VFS-related parts of libretro-common

This commit is contained in:
Vladimir Serbinenko 2020-03-30 13:23:50 +02:00
parent 6ce9d90685
commit dc45854223
5 changed files with 387 additions and 332 deletions

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010-2019 The RetroArch team
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (file_path.c).
@ -32,8 +32,6 @@
#include <file/file_path.h>
#include <retro_assert.h>
#include <string/stdstring.h>
#define VFS_FRONTEND
#include <vfs/vfs_implementation.h>
/* TODO: There are probably some unnecessary things on this huge include list now but I'm too afraid to touch it */
#ifdef __APPLE__
@ -87,7 +85,7 @@
#include <fileXio.h>
#endif
#if defined(__CELLOS_LV2__)
#if defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
#include <cell/cell_fs.h>
#endif
@ -114,137 +112,6 @@
#endif
static retro_vfs_stat_t path_stat_cb = NULL;
static retro_vfs_mkdir_t path_mkdir_cb = NULL;
void path_vfs_init(const struct retro_vfs_interface_info* vfs_info)
{
const struct retro_vfs_interface*
vfs_iface = vfs_info->iface;
path_stat_cb = NULL;
path_mkdir_cb = NULL;
if (vfs_info->required_interface_version < PATH_REQUIRED_VFS_VERSION || !vfs_iface)
return;
path_stat_cb = vfs_iface->stat;
path_mkdir_cb = vfs_iface->mkdir;
}
#define path_stat_internal(path, size) ((path_stat_cb != NULL) ? path_stat_cb((path), (size)) : retro_vfs_stat_impl((path), (size)))
#define path_mkdir_norecurse(dir) ((path_mkdir_cb != NULL) ? path_mkdir_cb((dir)) : retro_vfs_mkdir_impl((dir)))
int path_stat(const char *path)
{
return path_stat_internal(path, NULL);
}
/**
* 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)
{
return (path_stat_internal(path, NULL) & RETRO_VFS_STAT_IS_DIRECTORY) != 0;
}
bool path_is_character_special(const char *path)
{
return (path_stat_internal(path, NULL) & RETRO_VFS_STAT_IS_CHARACTER_SPECIAL) != 0;
}
bool path_is_valid(const char *path)
{
return (path_stat_internal(path, NULL) & RETRO_VFS_STAT_IS_VALID) != 0;
}
int32_t path_get_size(const char *path)
{
int32_t filesize = 0;
if (path_stat_internal(path, &filesize) != 0)
return filesize;
return -1;
}
/**
* 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)
{
bool sret = false;
bool norecurse = false;
char *basedir = NULL;
if (!(dir && *dir))
return false;
/* Use heap. Real chance of stack
* overflow if we recurse too hard. */
basedir = strdup(dir);
if (!basedir)
return false;
path_parent_dir(basedir);
if (!*basedir || !strcmp(basedir, dir))
{
free(basedir);
return false;
}
#if defined(GEKKO)
{
size_t len = strlen(basedir);
/* path_parent_dir() keeps the trailing slash.
* On Wii, mkdir() fails if the path has a
* trailing slash...
* We must therefore remove it. */
if (len > 0)
if (basedir[len - 1] == '/')
basedir[len - 1] = '\0';
}
#endif
if (path_is_directory(basedir))
norecurse = true;
else
{
sret = path_mkdir(basedir);
if (sret)
norecurse = true;
}
free(basedir);
if (norecurse)
{
int ret = path_mkdir_norecurse(dir);
/* Don't treat this as an error. */
if (ret == -2 && path_is_directory(dir))
return true;
return (ret == 0);
}
return sret;
}
/**
* path_get_archive_delim:
* @path : path
@ -393,11 +260,11 @@ void fill_pathname(char *out_path, const char *in_path,
* present in 'in_path', it will be ignored.
*
*/
void fill_pathname_noext(char *out_path, const char *in_path,
size_t fill_pathname_noext(char *out_path, const char *in_path,
const char *replace, size_t size)
{
strlcpy(out_path, in_path, size);
strlcat(out_path, replace, size);
return strlcat(out_path, replace, size);
}
char *find_last_slash(const char *str)
@ -435,12 +302,8 @@ void fill_pathname_slash(char *path, size_t size)
/* Try to preserve slash type. */
if (last_slash != (path + path_len - 1))
{
char join_str[2];
join_str[0] = '\0';
strlcpy(join_str, last_slash, sizeof(join_str));
strlcat(path, join_str, size);
path[path_len] = last_slash[0];
path[path_len+1] = '\0';
}
}
@ -461,7 +324,7 @@ void fill_pathname_slash(char *path, size_t size)
* E.g..: in_dir = "/tmp/some_dir", in_basename = "/some_content/foo.c",
* replace = ".asm" => in_dir = "/tmp/some_dir/foo.c.asm"
**/
void fill_pathname_dir(char *in_dir, const char *in_basename,
size_t fill_pathname_dir(char *in_dir, const char *in_basename,
const char *replace, size_t size)
{
const char *base = NULL;
@ -469,7 +332,7 @@ void fill_pathname_dir(char *in_dir, const char *in_basename,
fill_pathname_slash(in_dir, size);
base = path_basename(in_basename);
strlcat(in_dir, base, size);
strlcat(in_dir, replace, size);
return strlcat(in_dir, replace, size);
}
/**
@ -480,14 +343,14 @@ void fill_pathname_dir(char *in_dir, const char *in_basename,
*
* Copies basename of @in_path into @out_path.
**/
void fill_pathname_base(char *out, const char *in_path, size_t size)
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;
strlcpy(out, ptr, size);
return strlcpy(out, ptr, size);
}
void fill_pathname_base_noext(char *out,
@ -497,12 +360,12 @@ void fill_pathname_base_noext(char *out,
path_remove_extension(out);
}
void fill_pathname_base_ext(char *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);
strlcat(out, ext, size);
return strlcat(out, ext, size);
}
/**
@ -597,7 +460,7 @@ void fill_pathname_parent_dir(char *out_dir,
* E.g.:
* out_filename = "RetroArch-{month}{day}-{Hours}{Minutes}.{@ext}"
**/
void fill_dated_filename(char *out_filename,
size_t fill_dated_filename(char *out_filename,
const char *ext, size_t size)
{
time_t cur_time = time(NULL);
@ -605,7 +468,7 @@ void fill_dated_filename(char *out_filename,
strftime(out_filename, size,
"RetroArch-%m%d-%H%M%S", tm_);
strlcat(out_filename, ext, size);
return strlcat(out_filename, ext, size);
}
/**
@ -629,11 +492,20 @@ void fill_str_dated_filename(char *out_filename,
const struct tm* tm_ = localtime(&cur_time);
format[0] = '\0';
strftime(format, sizeof(format), "-%y%m%d-%H%M%S.", tm_);
fill_pathname_join_concat_noext(out_filename,
in_str, format, ext,
size);
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);
}
}
/**
@ -646,6 +518,7 @@ void fill_str_dated_filename(char *out_filename,
void path_basedir(char *path)
{
char *last = NULL;
if (strlen(path) < 2)
return;
@ -747,35 +620,131 @@ bool path_is_absolute(const char *path)
/**
* path_resolve_realpath:
* @buf : buffer for path
* @buf : input and output buffer for path
* @size : size of buffer
* @resolve_symlinks : whether to resolve symlinks or not
*
* Turns relative paths into absolute paths and
* resolves use of "." and ".." in absolute paths.
* If relative, rebases on current working dir.
* 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
**/
void path_resolve_realpath(char *buf, size_t size)
char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks)
{
#if !defined(RARCH_CONSOLE) && defined(RARCH_INTERNAL)
char tmp[PATH_MAX_LENGTH];
tmp[0] = '\0';
strlcpy(tmp, buf, sizeof(tmp));
#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;
/* 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);
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;
}
/**
@ -792,10 +761,10 @@ void path_resolve_realpath(char *buf, size_t size)
*
* E.g. path /a/b/e/f.cg with base /a/b/c/d/ turns into ../../e/f.cg
**/
void path_relative_to(char *out,
size_t path_relative_to(char *out,
const char *path, const char *base, size_t size)
{
unsigned i;
size_t i;
const char *trimmed_path, *trimmed_base;
#ifdef _WIN32
@ -803,10 +772,7 @@ void path_relative_to(char *out,
if (strlen(path) >= 2 && strlen(base) >= 2
&& path[1] == ':' && base[1] == ':'
&& path[0] != base[0])
{
out[0] = '\0';
strlcat(out, path, size);
}
return strlcpy(out, path, size);
#endif
/* Trim common beginning */
@ -818,9 +784,10 @@ void path_relative_to(char *out,
/* Each segment of base turns into ".." */
out[0] = '\0';
for (i = 0; trimmed_base[i]; i++)
if (trimmed_base[i] == '/' || trimmed_base[i] == '\\')
strlcat(out, "../", size); /* Use '/' as universal separator */
strlcat(out, trimmed_path, size);
if (trimmed_base[i] == path_default_slash_c())
strlcat(out, ".." path_default_slash(), size);
return strlcat(out, trimmed_path, size);
}
/**
@ -846,7 +813,7 @@ void fill_pathname_resolve_relative(char *out_path,
fill_pathname_basedir(out_path, in_refpath, size);
strlcat(out_path, in_path, size);
path_resolve_realpath(out_path, size);
path_resolve_realpath(out_path, size, false);
}
/**
@ -860,7 +827,7 @@ void fill_pathname_resolve_relative(char *out_path,
* Makes sure not to get two consecutive slashes
* between directory and path.
**/
void fill_pathname_join(char *out_path,
size_t fill_pathname_join(char *out_path,
const char *dir, const char *path, size_t size)
{
if (out_path != dir)
@ -869,10 +836,10 @@ void fill_pathname_join(char *out_path,
if (*out_path)
fill_pathname_slash(out_path, size);
strlcat(out_path, path, size);
return strlcat(out_path, path, size);
}
void fill_pathname_join_special_ext(char *out_path,
size_t fill_pathname_join_special_ext(char *out_path,
const char *dir, const char *path,
const char *last, const char *ext,
size_t size)
@ -882,26 +849,25 @@ void fill_pathname_join_special_ext(char *out_path,
fill_pathname_slash(out_path, size);
strlcat(out_path, last, size);
strlcat(out_path, ext, size);
return strlcat(out_path, ext, size);
}
void fill_pathname_join_concat_noext(
char *out_path,
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);
strlcat(out_path, concat, size);
return strlcat(out_path, concat, size);
}
void fill_pathname_join_concat(char *out_path,
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);
strlcat(out_path, concat, size);
return strlcat(out_path, concat, size);
}
void fill_pathname_join_noext(char *out_path,
@ -922,7 +888,7 @@ void fill_pathname_join_noext(char *out_path,
* Joins a directory (@dir) and path (@path) together
* using the given delimiter (@delim).
**/
void fill_pathname_join_delim(char *out_path, const char *dir,
size_t fill_pathname_join_delim(char *out_path, const char *dir,
const char *path, const char delim, size_t size)
{
size_t copied;
@ -936,15 +902,16 @@ void fill_pathname_join_delim(char *out_path, const char *dir,
out_path[copied+1] = '\0';
if (path)
strlcat(out_path, path, size);
copied = strlcat(out_path, path, size);
return copied;
}
void fill_pathname_join_delim_concat(char *out_path, const char *dir,
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);
strlcat(out_path, concat, size);
return strlcat(out_path, concat, size);
}
/**
@ -962,7 +929,7 @@ void fill_pathname_join_delim_concat(char *out_path, const char *dir,
* E.g.: "/path/to/game.img" -> game.img
* "/path/to/myarchive.7z#folder/to/game.img" -> game.img
*/
void fill_short_pathname_representation(char* out_rep,
size_t fill_short_pathname_representation(char* out_rep,
const char *in_path, size_t size)
{
char path_short[PATH_MAX_LENGTH];
@ -972,7 +939,7 @@ void fill_short_pathname_representation(char* out_rep,
fill_pathname(path_short, path_basename(in_path), "",
sizeof(path_short));
strlcpy(out_rep, path_short, size);
return strlcpy(out_rep, path_short, size);
}
void fill_short_pathname_representation_noext(char* out_rep,
@ -1189,10 +1156,27 @@ void fill_pathname_application_path(char *s, size_t len)
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__)
@ -1265,8 +1249,8 @@ void fill_pathname_home_dir(char *s, size_t len)
bool is_path_accessible_using_standard_io(const char *path)
{
bool result = true;
#ifdef __WINRT__
bool result;
size_t path_sizeof = PATH_MAX_LENGTH * sizeof(char);
char *relative_path_abbrev = (char*)malloc(path_sizeof);
fill_pathname_abbreviate_special(relative_path_abbrev, path, path_sizeof);
@ -1274,8 +1258,6 @@ bool is_path_accessible_using_standard_io(const char *path)
result = strlen(relative_path_abbrev) >= 2 && (relative_path_abbrev[0] == ':' || relative_path_abbrev[0] == '~') && path_char_is_slash(relative_path_abbrev[1]);
free(relative_path_abbrev);
return result;
#else
return true;
#endif
return result;
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010-2019 The RetroArch team
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (file_path.h).
@ -147,14 +147,22 @@ void path_parent_dir(char *path);
/**
* path_resolve_realpath:
* @buf : buffer for path
* @buf : input and output buffer for path
* @size : size of buffer
* @resolve_symlinks : whether to resolve symlinks or not
*
* Turns relative paths into absolute paths and
* resolves use of "." and ".." in absolute paths.
* If relative, rebases on current working dir.
* 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
**/
void path_resolve_realpath(char *buf, size_t size);
char *path_resolve_realpath(char *buf, size_t size, bool resolve_symlinks);
/**
* path_relative_to:
@ -170,7 +178,7 @@ void path_resolve_realpath(char *buf, size_t size);
*
* E.g. path /a/b/e/f.cgp with base /a/b/c/d/ turns into ../../e/f.cgp
**/
void path_relative_to(char *out, const char *path, const char *base, size_t size);
size_t path_relative_to(char *out, const char *path, const char *base, size_t size);
/**
* path_is_absolute:
@ -218,7 +226,7 @@ void fill_pathname(char *out_path, const char *in_path,
* E.g.:
* out_filename = "RetroArch-{month}{day}-{Hours}{Minutes}.{@ext}"
**/
void fill_dated_filename(char *out_filename,
size_t fill_dated_filename(char *out_filename,
const char *ext, size_t size);
/**
@ -251,7 +259,7 @@ void fill_str_dated_filename(char *out_filename,
* present in 'in_path', it will be ignored.
*
*/
void fill_pathname_noext(char *out_path, const char *in_path,
size_t fill_pathname_noext(char *out_path, const char *in_path,
const char *replace, size_t size);
/**
@ -281,7 +289,7 @@ char *find_last_slash(const char *str);
* E.g..: in_dir = "/tmp/some_dir", in_basename = "/some_content/foo.c",
* replace = ".asm" => in_dir = "/tmp/some_dir/foo.c.asm"
**/
void fill_pathname_dir(char *in_dir, const char *in_basename,
size_t fill_pathname_dir(char *in_dir, const char *in_basename,
const char *replace, size_t size);
/**
@ -292,12 +300,12 @@ void fill_pathname_dir(char *in_dir, const char *in_basename,
*
* Copies basename of @in_path into @out_path.
**/
void fill_pathname_base(char *out_path, const char *in_path, size_t size);
size_t fill_pathname_base(char *out_path, const char *in_path, size_t size);
void fill_pathname_base_noext(char *out_dir,
const char *in_path, size_t size);
void fill_pathname_base_ext(char *out,
size_t fill_pathname_base_ext(char *out,
const char *in_path, const char *ext,
size_t size);
@ -368,21 +376,20 @@ void fill_pathname_resolve_relative(char *out_path, const char *in_refpath,
* Makes sure not to get two consecutive slashes
* between directory and path.
**/
void fill_pathname_join(char *out_path, const char *dir,
size_t fill_pathname_join(char *out_path, const char *dir,
const char *path, size_t size);
void fill_pathname_join_special_ext(char *out_path,
size_t fill_pathname_join_special_ext(char *out_path,
const char *dir, const char *path,
const char *last, const char *ext,
size_t size);
void fill_pathname_join_concat_noext(
char *out_path,
size_t fill_pathname_join_concat_noext(char *out_path,
const char *dir, const char *path,
const char *concat,
size_t size);
void fill_pathname_join_concat(char *out_path,
size_t fill_pathname_join_concat(char *out_path,
const char *dir, const char *path,
const char *concat,
size_t size);
@ -401,10 +408,10 @@ void fill_pathname_join_noext(char *out_path,
* Joins a directory (@dir) and path (@path) together
* using the given delimiter (@delim).
**/
void fill_pathname_join_delim(char *out_path, const char *dir,
size_t fill_pathname_join_delim(char *out_path, const char *dir,
const char *path, const char delim, size_t size);
void fill_pathname_join_delim_concat(char *out_path, const char *dir,
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);
@ -423,7 +430,7 @@ void fill_pathname_join_delim_concat(char *out_path, const char *dir,
* E.g.: "/path/to/game.img" -> game.img
* "/path/to/myarchive.7z#folder/to/game.img" -> game.img
*/
void fill_short_pathname_representation(char* out_rep,
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,

View File

@ -39,7 +39,7 @@
#include <Xtl.h>
#endif
#if defined(__CELLOS_LV2__)
#if defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
#include <sys/fs_external.h>
#endif
@ -75,7 +75,7 @@ static INLINE bool bits_any_set(uint32_t* ptr, uint32_t count)
}
#ifndef PATH_MAX_LENGTH
#if defined(__CELLOS_LV2__)
#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)
#define PATH_MAX_LENGTH 512

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010-2019 The RetroArch team
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (vfs_implementation.h).
@ -23,30 +23,13 @@
#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>
/* 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
#ifdef __cplusplus
extern "C" {
#endif
RETRO_BEGIN_DECLS
libretro_vfs_implementation_file *retro_vfs_file_open_impl(const char *path, unsigned mode, unsigned hints);
@ -88,8 +71,6 @@ bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *dirstream);
int retro_vfs_closedir_impl(libretro_vfs_implementation_dir *dirstream);
#ifdef __cplusplus
}
#endif
RETRO_END_DECLS
#endif

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010-2019 The RetroArch team
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (vfs_implementation.c).
@ -41,11 +41,12 @@
# include <xtl.h>
# define INVALID_FILE_ATTRIBUTES -1
# else
# include <io.h>
# include <fcntl.h>
# include <direct.h>
# include <windows.h>
# endif
# include <io.h>
#else
# if defined(PSP)
# include <pspiofilemgr.h>
@ -67,7 +68,7 @@
# endif
#endif
#ifdef __CELLOS_LV2__
#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
@ -150,7 +151,7 @@
#include <fileXio.h>
#endif
#if defined(__CELLOS_LV2__)
#if defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
#include <cell/cell_fs.h>
#endif
@ -171,18 +172,12 @@
#endif
#if defined(_WIN32) && !defined(_XBOX)
#if defined(_WIN32)
#if !defined(_MSC_VER) || (defined(_MSC_VER) && _MSC_VER >= 1400)
#define ATLEAST_VC2005
#endif
#endif
#ifdef RARCH_INTERNAL
#ifndef VFS_FRONTEND
#define VFS_FRONTEND
#endif
#endif
#include <vfs/vfs_implementation.h>
#include <libretro.h>
#include <memmap.h>
@ -190,26 +185,11 @@
#include <compat/fopen_utf8.h>
#include <file/file_path.h>
#define RFILE_HINT_UNBUFFERED (1 << 8)
#ifdef HAVE_CDROM
#include <vfs/vfs_implementation_cdrom.h>
#endif
#ifdef VFS_FRONTEND
struct retro_vfs_file_handle
#else
struct libretro_vfs_implementation_file
#endif
{
int fd;
unsigned hints;
int64_t size;
char *buf;
FILE *fp;
char* orig_path;
#if defined(HAVE_MMAP)
uint64_t mappos;
uint64_t mapsize;
uint8_t *mapped;
#endif
};
#define RFILE_HINT_UNBUFFERED (1 << 8)
int64_t retro_vfs_file_seek_internal(libretro_vfs_implementation_file *stream, int64_t offset, int whence)
{
@ -218,22 +198,30 @@ int64_t retro_vfs_file_seek_internal(libretro_vfs_implementation_file *stream, i
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
/* VC2005 and up have a special 64-bit fseek */
#ifdef ATLEAST_VC2005
return _fseeki64(stream->fp, offset, whence);
#elif defined(__CELLOS_LV2__) || defined(_MSC_VER) && _MSC_VER <= 1310
return fseek(stream->fp, (long)offset, whence);
#elif defined(PS2)
int64_t ret = fileXioLseek(fileno(stream->fp), (off_t)offset, whence);
/* fileXioLseek could return positive numbers */
if (ret > 0)
return 0;
return ret;
{
int64_t ret = fileXioLseek(fileno(stream->fp), (off_t)offset, whence);
/* fileXioLseek could return positive numbers */
if (ret > 0)
return 0;
return ret;
}
#elif defined(ORBIS)
int ret = orbisLseek(stream->fd, offset, whence);
if (ret < 0)
return -1;
return 0;
{
int ret = orbisLseek(stream->fd, offset, whence);
if (ret < 0)
return -1;
return 0;
}
#else
return fseeko(stream->fp, (off_t)offset, whence);
#endif
@ -298,12 +286,14 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
const char *mode_str = NULL;
libretro_vfs_implementation_file *stream = (libretro_vfs_implementation_file*)
calloc(1, sizeof(*stream));
#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(dumb_prefix);
int dumb_prefix_len = (int)dumb_prefix_siz;
int path_len = (int)strlen(path);
if (path_len >= dumb_prefix_len)
{
@ -312,6 +302,23 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
}
#endif
#ifdef HAVE_CDROM
{
const char *cdrom_prefix = "cdrom://";
size_t cdrom_prefix_siz = strlen(cdrom_prefix);
int cdrom_prefix_len = (int)cdrom_prefix_siz;
if (path_len > cdrom_prefix_len)
{
if (!memcmp(path, cdrom_prefix, cdrom_prefix_len))
{
path += cdrom_prefix_siz;
stream->scheme = VFS_SCHEME_CDROM;
}
}
}
#endif
if (!stream)
return NULL;
@ -398,11 +405,29 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
}
stream->fd = fd;
#else
FILE *fp = (FILE*)fopen_utf8(path, mode_str);
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;
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
@ -413,10 +438,13 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
* 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 */
stream->fp = fp;
#if !defined(PS2) && !defined(PSP)
stream->buf = (char*)calloc(1, 0x4000);
setvbuf(stream->fp, stream->buf, _IOFBF, 0x4000);
if (stream->scheme != VFS_SCHEME_CDROM)
{
stream->buf = (char*)calloc(1, 0x4000);
if (stream->fp)
setvbuf(stream->fp, stream->buf, _IOFBF, 0x4000);
}
#endif
#endif
}
@ -465,12 +493,26 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(
stream->size = orbisLseek(stream->fd, 0, SEEK_END);
orbisLseek(stream->fd, 0, SEEK_SET);
#else
retro_vfs_file_seek_internal(stream, 0, SEEK_SET);
retro_vfs_file_seek_internal(stream, 0, SEEK_END);
#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);
stream->size = retro_vfs_file_tell_impl(stream);
retro_vfs_file_seek_internal(stream, 0, SEEK_SET);
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;
@ -484,10 +526,20 @@ 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
{
@ -506,10 +558,17 @@ int retro_vfs_file_close_impl(libretro_vfs_implementation_file *stream)
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;
@ -517,6 +576,10 @@ int retro_vfs_file_close_impl(libretro_vfs_implementation_file *stream)
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;
@ -555,13 +618,19 @@ int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream)
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;
{
int64_t ret = orbisLseek(stream->fd, 0, SEEK_CUR);
if (ret < 0)
return -1;
return ret;
}
#else
/* VC2005 and up have a special 64-bit ftell */
/* VC2005 and up have a special 64-bit ftell */
#ifdef ATLEAST_VC2005
return _ftelli64(stream->fp);
#else
@ -609,6 +678,10 @@ int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream,
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;
@ -867,14 +940,16 @@ int retro_vfs_stat_impl(const char *path, int32_t *size)
/* if fileXioGetStat fails */
int dir_ret = fileXioDopen(path);
is_dir = dir_ret > 0;
fileXioDclose(dir_ret);
if (is_dir) {
fileXioDclose(dir_ret);
}
}
else
is_dir = FIO_S_ISDIR(buf.mode);
return RETRO_VFS_STAT_IS_VALID | (is_dir ? RETRO_VFS_STAT_IS_DIRECTORY : 0) | (is_character_special ? RETRO_VFS_STAT_IS_CHARACTER_SPECIAL : 0);
#elif defined(__CELLOS_LV2__)
#elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
/* CellOS Lv2 */
bool is_dir;
bool is_character_special = false;
@ -1020,7 +1095,7 @@ struct libretro_vfs_implementation_dir
#elif defined(PS2)
int directory;
iox_dirent_t entry;
#elif defined(__CELLOS_LV2__)
#elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
CellFsErrno error;
int directory;
CellFsDirent entry;
@ -1039,18 +1114,20 @@ static bool dirent_check_error(libretro_vfs_implementation_dir *rdir)
return (rdir->directory == INVALID_HANDLE_VALUE);
#elif defined(VITA) || defined(PSP) || defined(PS2) || defined(ORBIS)
return (rdir->directory < 0);
#elif defined(__CELLOS_LV2__)
#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)
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
@ -1074,21 +1151,24 @@ libretro_vfs_implementation_dir *retro_vfs_opendir_impl(const char *name, bool i
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] == '\\')
snprintf(path_buf, sizeof(path_buf), "%s*", name);
else
snprintf(path_buf, sizeof(path_buf), "%s\\*", name);
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);
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);
path_wide = utf8_to_utf16_string_alloc(path_buf);
rdir->directory = FindFirstFileW(path_wide, &rdir->entry);
if (path_wide)
free(path_wide);
@ -1101,7 +1181,7 @@ libretro_vfs_implementation_dir *retro_vfs_opendir_impl(const char *name, bool i
#elif defined(_3DS)
rdir->directory = !string_is_empty(name) ? opendir(name) : NULL;
rdir->entry = NULL;
#elif defined(__CELLOS_LV2__)
#elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
rdir->error = cellFsOpendir(name, &rdir->directory);
#elif defined(ORBIS)
rdir->directory = orbisDopen(name);
@ -1143,7 +1223,7 @@ bool retro_vfs_readdir_impl(libretro_vfs_implementation_dir *rdir)
int ret = ps2fileXioDread(rdir->directory, &record);
rdir->entry = record;
return ( ret > 0);
#elif defined(__CELLOS_LV2__)
#elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
uint64_t nread;
rdir->error = cellFsReaddir(rdir->directory, &rdir->entry, &nread);
return (nread != 0);
@ -1158,27 +1238,32 @@ const char *retro_vfs_dirent_get_name_impl(libretro_vfs_implementation_dir *rdir
{
#if defined(_WIN32)
#if defined(LEGACY_WIN32)
char *name_local = local_to_utf8_string_alloc(rdir->entry.cFileName);
memset(rdir->entry.cFileName, 0, sizeof(rdir->entry.cFileName));
strlcpy(rdir->entry.cFileName, name_local, sizeof(rdir->entry.cFileName));
{
char *name_local = local_to_utf8_string_alloc(rdir->entry.cFileName);
memset(rdir->entry.cFileName, 0, sizeof(rdir->entry.cFileName));
strlcpy(rdir->entry.cFileName, name_local, sizeof(rdir->entry.cFileName));
if (name_local)
free(name_local);
if (name_local)
free(name_local);
}
#else
char *name = utf16_to_utf8_string_alloc(rdir->entry.cFileName);
memset(rdir->entry.cFileName, 0, sizeof(rdir->entry.cFileName));
strlcpy((char*)rdir->entry.cFileName, name, sizeof(rdir->entry.cFileName));
{
char *name = utf16_to_utf8_string_alloc(rdir->entry.cFileName);
memset(rdir->entry.cFileName, 0, sizeof(rdir->entry.cFileName));
strlcpy((char*)rdir->entry.cFileName, name, sizeof(rdir->entry.cFileName));
if (name)
free(name);
if (name)
free(name);
}
#endif
return (char*)rdir->entry.cFileName;
#elif defined(VITA) || defined(PSP) || defined(__CELLOS_LV2__) || defined(ORBIS)
#elif defined(VITA) || defined(PSP) || defined(__CELLOS_LV2__) && !defined(__PSL1GHT__) || defined(ORBIS)
return rdir->entry.d_name;
#elif defined(PS2)
return rdir->entry.name;
#else
if (!rdir || !rdir->entry)
return NULL;
return rdir->entry->d_name;
#endif
}
@ -1198,7 +1283,7 @@ bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *rdir)
#elif defined(PS2)
const iox_dirent_t *entry = (const iox_dirent_t*)&rdir->entry;
return FIO_S_ISDIR(entry->stat.mode);
#elif defined(__CELLOS_LV2__)
#elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
CellFsDirent *entry = (CellFsDirent*)&rdir->entry;
return (entry->d_type == CELL_FS_TYPE_DIRECTORY);
#elif defined(ORBIS)
@ -1239,7 +1324,7 @@ int retro_vfs_closedir_impl(libretro_vfs_implementation_dir *rdir)
sceIoDclose(rdir->directory);
#elif defined(PS2)
ps2fileXioDclose(rdir->directory);
#elif defined(__CELLOS_LV2__)
#elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
rdir->error = cellFsClosedir(rdir->directory);
#elif defined(ORBIS)
orbisDclose(rdir->directory);