Merge pull request #7995 from krzys-h/vfs-v3

VFS v3: stat, mkdir, directory listings
This commit is contained in:
Twinaphex 2019-01-16 02:38:54 +01:00 committed by GitHub
commit d4b33ac1ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 702 additions and 441 deletions

View File

@ -1927,9 +1927,10 @@ bool rarch_environment_cb(unsigned cmd, void *data)
case RETRO_ENVIRONMENT_GET_VFS_INTERFACE:
{
const uint32_t supported_vfs_version = 1;
const uint32_t supported_vfs_version = 3;
static struct retro_vfs_interface vfs_iface =
{
{
/* VFS API v1 */
retro_vfs_file_get_path_impl,
retro_vfs_file_open_impl,
retro_vfs_file_close_impl,
@ -1939,15 +1940,32 @@ bool rarch_environment_cb(unsigned cmd, void *data)
retro_vfs_file_read_impl,
retro_vfs_file_write_impl,
retro_vfs_file_flush_impl,
retro_vfs_file_remove_impl
retro_vfs_file_remove_impl,
retro_vfs_file_rename_impl,
/* VFS API v2 */
retro_vfs_file_truncate_impl,
/* VFS API v3 */
retro_vfs_stat_impl,
retro_vfs_mkdir_impl,
retro_vfs_opendir_impl,
retro_vfs_readdir_impl,
retro_vfs_dirent_get_name_impl,
retro_vfs_dirent_is_dir_impl,
retro_vfs_closedir_impl
};
struct retro_vfs_interface_info *vfs_iface_info = (struct retro_vfs_interface_info *) data;
if (vfs_iface_info->required_interface_version <= supported_vfs_version)
{
RARCH_LOG("Core requested VFS version >= v%d, providing v%d\n", vfs_iface_info->required_interface_version, supported_vfs_version);
vfs_iface_info->required_interface_version = supported_vfs_version;
vfs_iface_info->iface = &vfs_iface;
}
else
{
RARCH_WARN("Core requested VFS version v%d which is higher than what we support (v%d)\n", vfs_iface_info->required_interface_version, supported_vfs_version);
return false;
}
break;
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010-2018 The RetroArch team
/* Copyright (C) 2010-2019 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (file_path.c).
@ -32,7 +32,10 @@
#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__
#include <CoreFoundation/CoreFoundation.h>
#endif
@ -116,118 +119,39 @@
#endif
enum stat_mode
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)
{
IS_DIRECTORY = 0,
IS_CHARACTER_SPECIAL,
IS_VALID
};
const struct retro_vfs_interface* vfs_iface;
static bool path_stat(const char *path, enum stat_mode mode, int32_t *size)
path_stat_cb = NULL;
path_mkdir_cb = NULL;
vfs_iface = vfs_info->iface;
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;
}
static int path_stat(const char *path, int32_t *size)
{
#if defined(ORBIS)
return false; /* for now */
#endif
#if defined(VITA) || defined(PSP)
SceIoStat buf;
char *tmp = strdup(path);
size_t len = strlen(tmp);
if (tmp[len-1] == '/')
tmp[len-1] = '\0';
if (path_stat_cb != NULL)
return path_stat_cb(path, NULL);
else
return retro_vfs_stat_impl(path, NULL);
}
if (sceIoGetstat(tmp, &buf) < 0)
{
free(tmp);
return false;
}
free(tmp);
#elif defined(PS2)
iox_stat_t buf;
char *tmp = strdup(path);
size_t len = strlen(tmp);
if (tmp[len-1] == '/')
tmp[len-1] = '\0';
if (fileXioGetStat(tmp, &buf) < 0)
{
free(tmp);
return false;
}
free(tmp);
#elif defined(__CELLOS_LV2__)
CellFsStat buf;
if (cellFsStat(path, &buf) < 0)
return false;
#elif defined(_WIN32)
DWORD file_info;
struct _stat buf;
char *path_local = NULL;
wchar_t *path_wide = NULL;
if (!path || !*path)
return false;
(void)path_wide;
(void)path_local;
(void)file_info;
#if defined(LEGACY_WIN32)
path_local = utf8_to_local_string_alloc(path);
file_info = GetFileAttributes(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 false;
#else
struct stat buf;
if (stat(path, &buf) < 0)
return false;
#endif
if (size)
#if defined(PS2)
*size = (int32_t)buf.size;
#else
*size = (int32_t)buf.st_size;
#endif
switch (mode)
{
case IS_DIRECTORY:
#if defined(VITA) || defined(PSP)
return FIO_S_ISDIR(buf.st_mode);
#elif defined(PS2)
return FIO_S_ISDIR(buf.mode);
#elif defined(__CELLOS_LV2__)
return ((buf.st_mode & S_IFMT) == S_IFDIR);
#elif defined(_WIN32)
return (file_info & FILE_ATTRIBUTE_DIRECTORY);
#else
return S_ISDIR(buf.st_mode);
#endif
case IS_CHARACTER_SPECIAL:
#if defined(VITA) || defined(PSP) || defined(PS2) || defined(__CELLOS_LV2__) || defined(_WIN32)
return false;
#else
return S_ISCHR(buf.st_mode);
#endif
case IS_VALID:
return true;
}
return false;
static int path_mkdir_norecurse(const char *dir)
{
if (path_mkdir_cb != NULL)
return path_mkdir_cb(dir);
else
return retro_vfs_mkdir_impl(dir);
}
/**
@ -241,6 +165,7 @@ static bool path_stat(const char *path, enum stat_mode mode, int32_t *size)
bool path_is_directory(const char *path)
{
#ifdef ORBIS
/* TODO: This should be moved to the VFS module */
int dfd;
if (!path)
return false;
@ -250,40 +175,29 @@ bool path_is_directory(const char *path)
orbisDclose(dfd);
return true;
#else
return path_stat(path, IS_DIRECTORY, NULL);
return (path_stat(path, NULL) & RETRO_VFS_STAT_IS_DIRECTORY) != 0;
#endif
}
bool path_is_character_special(const char *path)
{
return path_stat(path, IS_CHARACTER_SPECIAL, NULL);
return (path_stat(path, NULL) & RETRO_VFS_STAT_IS_CHARACTER_SPECIAL) != 0;
}
bool path_is_valid(const char *path)
{
return path_stat(path, IS_VALID, NULL);
return (path_stat(path, NULL) & RETRO_VFS_STAT_IS_VALID) != 0;
}
int32_t path_get_size(const char *path)
{
int32_t filesize = 0;
if (path_stat(path, IS_VALID, &filesize))
if (path_stat(path, &filesize) != 0)
return filesize;
return -1;
}
static bool path_mkdir_error(int ret)
{
#if defined(VITA)
return (ret == SCE_ERROR_ERRNO_EEXIST);
#elif defined(PSP) || defined(PS2) || defined(_3DS) || defined(WIIU) || defined(SWITCH) || defined(ORBIS)
return (ret == -1);
#else
return (ret < 0 && errno == EEXIST);
#endif
}
/**
* path_mkdir:
* @dir : directory
@ -329,35 +243,10 @@ bool path_mkdir(const char *dir)
if (norecurse)
{
#if defined(_WIN32)
#ifdef LEGACY_WIN32
int ret = _mkdir(dir);
#else
wchar_t *dirW = utf8_to_utf16_string_alloc(dir);
int ret = -1;
if (dirW)
{
ret = _wmkdir(dirW);
free(dirW);
}
#endif
#elif defined(IOS)
int ret = mkdir(dir, 0755);
#elif defined(VITA) || defined(PSP)
int ret = sceIoMkdir(dir, 0777);
#elif defined(PS2)
int ret =fileXioMkdir(dir, 0777);
#elif defined(ORBIS)
int ret =orbisMkdir(dir, 0755);
#elif defined(__QNX__)
int ret = mkdir(dir, 0777);
#else
int ret = mkdir(dir, 0750);
#endif
int ret = path_mkdir_norecurse(dir);
/* Don't treat this as an error. */
if (path_mkdir_error(ret) && path_is_directory(dir))
if (ret == -2 && path_is_directory(dir))
ret = 0;
if (ret < 0)

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010-2018 The RetroArch team
/* Copyright (C) 2010-2019 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (retro_dirent.c).
@ -28,313 +28,95 @@
#include <boolean.h>
#include <retro_dirent.h>
#include <encodings/utf.h>
#include <compat/strl.h>
#include <string/stdstring.h>
#define VFS_FRONTEND
#include <vfs/vfs_implementation.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>
# endif
#elif 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>
#else
# if defined(PSP)
# include <pspiofilemgr.h>
# endif
# if defined(PS2)
# include <fileXio_rpc.h>
# endif
# include <sys/types.h>
# include <sys/stat.h>
# include <dirent.h>
# include <unistd.h>
#endif
static retro_vfs_opendir_t dirent_opendir_cb = NULL;
static retro_vfs_readdir_t dirent_readdir_cb = NULL;
static retro_vfs_dirent_get_name_t dirent_dirent_get_name_cb = NULL;
static retro_vfs_dirent_is_dir_t dirent_dirent_is_dir_cb = NULL;
static retro_vfs_closedir_t dirent_closedir_cb = NULL;
#ifdef __CELLOS_LV2__
#include <cell/cell_fs.h>
#endif
#if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(__QNX__) || defined(PSP) || defined(PS2)
#include <unistd.h> /* stat() is defined here */
#endif
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
#ifndef LEGACY_WIN32
#define LEGACY_WIN32
#endif
#endif
struct RDIR
void dirent_vfs_init(const struct retro_vfs_interface_info* vfs_info)
{
#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(PS2)
int directory;
iox_dirent_t entry;
#elif defined(__CELLOS_LV2__)
CellFsErrno error;
int directory;
CellFsDirent entry;
#elif defined(ORBIS)
int directory;
struct dirent entry;
#else
DIR *directory;
const struct dirent *entry;
#endif
};
const struct retro_vfs_interface* vfs_iface;
dirent_opendir_cb = NULL;
dirent_readdir_cb = NULL;
dirent_dirent_get_name_cb = NULL;
dirent_dirent_is_dir_cb = NULL;
dirent_closedir_cb = NULL;
vfs_iface = vfs_info->iface;
if (vfs_info->required_interface_version < DIRENT_REQUIRED_VFS_VERSION || !vfs_iface)
return;
dirent_opendir_cb = vfs_iface->opendir;
dirent_readdir_cb = vfs_iface->readdir;
dirent_dirent_get_name_cb = vfs_iface->dirent_get_name;
dirent_dirent_is_dir_cb = vfs_iface->dirent_is_dir;
dirent_closedir_cb = vfs_iface->closedir;
}
struct RDIR *retro_opendir_include_hidden(const char *name, bool include_hidden)
{
if (dirent_opendir_cb != NULL)
return (struct RDIR *)dirent_opendir_cb(name, include_hidden);
else
return (struct RDIR *)retro_vfs_opendir_impl(name, include_hidden);
}
struct RDIR *retro_opendir(const char *name)
{
#if defined(_WIN32)
char path_buf[1024];
char *path_local = NULL;
wchar_t *path_wide = NULL;
unsigned path_len;
#endif
struct RDIR *rdir;
/*Reject null or empty string paths*/
if (!name||(*name==0))
return NULL;
/*Allocate RDIR struct. Tidied later with retro_closedir*/
rdir = (struct RDIR*)calloc(1, sizeof(*rdir));
if (!rdir)
return NULL;
#if defined(_WIN32)
(void)path_wide;
(void)path_local;
path_buf[0] = '\0';
path_len = strlen(name);
/* 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 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(PS2)
rdir->directory = fileXioDopen(name);
#elif defined(_3DS)
rdir->directory = !string_is_empty(name) ? opendir(name) : NULL;
rdir->entry = NULL;
#elif defined(__CELLOS_LV2__)
rdir->error = cellFsOpendir(name, &rdir->directory);
#elif defined(ORBIS)
rdir->directory = orbisDopen(name);
#else
rdir->directory = opendir(name);
rdir->entry = NULL;
#endif
if (rdir->directory)
return rdir;
free(rdir);
return NULL;
return retro_opendir_include_hidden(name, false);
}
bool retro_dirent_error(struct RDIR *rdir)
{
#if defined(_WIN32)
return (rdir->directory == INVALID_HANDLE_VALUE);
#elif defined(VITA) || defined(PSP) || defined(PS2) || defined(ORBIS)
return (rdir->directory < 0);
#elif defined(__CELLOS_LV2__)
return (rdir->error != CELL_FS_SUCCEEDED);
#else
return !(rdir->directory);
#endif
/* Left for compatibility */
return false;
}
int retro_readdir(struct RDIR *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(PS2)
iox_dirent_t record;
int ret = fileXioDread(rdir->directory, &record);
rdir->entry = record;
return ( ret > 0);
#elif defined(__CELLOS_LV2__)
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
if (dirent_readdir_cb != NULL)
return dirent_readdir_cb((struct retro_vfs_dir_handle *)rdir);
else
return retro_vfs_readdir_impl((struct retro_vfs_dir_handle *)rdir);
}
const char *retro_dirent_get_name(struct RDIR *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));
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));
if (name)
free(name);
#endif
return (char*)rdir->entry.cFileName;
#elif defined(VITA) || defined(PSP) || defined(__CELLOS_LV2__) || defined(ORBIS)
return rdir->entry.d_name;
#elif defined(PS2)
return rdir->entry.name;
#else
return rdir->entry->d_name;
#endif
if (dirent_dirent_get_name_cb != NULL)
return dirent_dirent_get_name_cb((struct retro_vfs_dir_handle *)rdir);
else
return retro_vfs_dirent_get_name_impl((struct retro_vfs_dir_handle *)rdir);
}
/**
*
* retro_dirent_is_dir:
* @rdir : pointer to the directory entry.
* @path : path to the directory entry.
* @unused : deprecated, included for compatibility reasons, pass NULL
*
* Is the directory listing entry a directory?
*
* Returns: true if directory listing entry is
* a directory, false if not.
*/
bool retro_dirent_is_dir(struct RDIR *rdir, const char *path)
bool retro_dirent_is_dir(struct RDIR *rdir, const char *unused)
{
#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(PS2)
const iox_dirent_t *entry = (const iox_dirent_t*)&rdir->entry;
return FIO_S_ISDIR(entry->stat.mode);
#elif defined(__CELLOS_LV2__)
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;
#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 ... */
if (stat(path, &buf) < 0)
return false;
return S_ISDIR(buf.st_mode);
#endif
}
void retro_dirent_include_hidden(struct RDIR *rdir, bool include_hidden)
{
#ifdef _WIN32
if (include_hidden)
rdir->entry.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
if (dirent_dirent_is_dir_cb != NULL)
return dirent_dirent_is_dir_cb((struct retro_vfs_dir_handle *)rdir);
else
rdir->entry.dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
#endif
return retro_vfs_dirent_is_dir_impl((struct retro_vfs_dir_handle *)rdir);
}
void retro_closedir(struct RDIR *rdir)
{
if (!rdir)
return;
#if defined(_WIN32)
if (rdir->directory != INVALID_HANDLE_VALUE)
FindClose(rdir->directory);
#elif defined(VITA) || defined(PSP)
sceIoDclose(rdir->directory);
#elif defined(PS2)
fileXioDclose(rdir->directory);
#elif defined(__CELLOS_LV2__)
rdir->error = cellFsClosedir(rdir->directory);
#elif defined(ORBIS)
orbisDclose(rdir->directory);
#else
if (rdir->directory)
closedir(rdir->directory);
#endif
free(rdir);
if (dirent_closedir_cb != NULL)
dirent_closedir_cb((struct retro_vfs_dir_handle *)rdir);
else
retro_vfs_closedir_impl((struct retro_vfs_dir_handle *)rdir);
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010-2018 The RetroArch team
/* Copyright (C) 2010-2019 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (file_path.h).
@ -28,12 +28,17 @@
#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

View File

@ -1100,6 +1100,10 @@ enum retro_mod
* Introduced in VFS API v1 */
struct retro_vfs_file_handle;
/* Opaque directory handle
* Introduced in VFS API v3 */
struct retro_vfs_dir_handle;
/* File open flags
* Introduced in VFS API v1 */
#define RETRO_VFS_FILE_ACCESS_READ (1 << 0) /* Read only mode */
@ -1119,6 +1123,12 @@ struct retro_vfs_file_handle;
#define RETRO_VFS_SEEK_POSITION_CURRENT 1
#define RETRO_VFS_SEEK_POSITION_END 2
/* stat() result flags
* Introduced in VFS API v3 */
#define RETRO_VFS_STAT_IS_VALID (1 << 0)
#define RETRO_VFS_STAT_IS_DIRECTORY (1 << 1)
#define RETRO_VFS_STAT_IS_CHARACTER_SPECIAL (1 << 2)
/* Get path from opaque handle. Returns the exact same path passed to file_open when getting the handle
* Introduced in VFS API v1 */
typedef const char *(RETRO_CALLCONV *retro_vfs_get_path_t)(struct retro_vfs_file_handle *stream);
@ -1128,7 +1138,7 @@ typedef const char *(RETRO_CALLCONV *retro_vfs_get_path_t)(struct retro_vfs_file
* Introduced in VFS API v1 */
typedef struct retro_vfs_file_handle *(RETRO_CALLCONV *retro_vfs_open_t)(const char *path, unsigned mode, unsigned hints);
/* Close the file and release its resources. Must be called if open_file returns non-NULL. Returns 0 on succes, -1 on failure.
/* Close the file and release its resources. Must be called if open_file returns non-NULL. Returns 0 on success, -1 on failure.
* Whether the call succeeds ot not, the handle passed as parameter becomes invalid and should no longer be used.
* Introduced in VFS API v1 */
typedef int (RETRO_CALLCONV *retro_vfs_close_t)(struct retro_vfs_file_handle *stream);
@ -1141,7 +1151,7 @@ typedef int64_t (RETRO_CALLCONV *retro_vfs_size_t)(struct retro_vfs_file_handle
* Introduced in VFS API v2 */
typedef int64_t (RETRO_CALLCONV *retro_vfs_truncate_t)(struct retro_vfs_file_handle *stream, int64_t length);
/* Get the current read / write position for the file. Returns - 1 for error.
/* Get the current read / write position for the file. Returns -1 for error.
* Introduced in VFS API v1 */
typedef int64_t (RETRO_CALLCONV *retro_vfs_tell_t)(struct retro_vfs_file_handle *stream);
@ -1169,6 +1179,39 @@ typedef int (RETRO_CALLCONV *retro_vfs_remove_t)(const char *path);
* Introduced in VFS API v1 */
typedef int (RETRO_CALLCONV *retro_vfs_rename_t)(const char *old_path, const char *new_path);
/* Stat the specified file. Retruns a bitmask of RETRO_VFS_STAT_* flags, none are set if path was not valid.
* Additionally stores file size in given variable, unless NULL is given.
* Introduced in VFS API v3 */
typedef int (RETRO_CALLCONV *retro_vfs_stat_t)(const char *path, int32_t *size);
/* Create the specified directory. Returns 0 on success, -1 on unknown failure, -2 if already exists.
* Introduced in VFS API v3 */
typedef int (RETRO_CALLCONV *retro_vfs_mkdir_t)(const char *dir);
/* Open the specified directory for listing. Returns the opaque dir handle, or NULL for error.
* Support for the include_hidden argument may vary depending on the platform.
* Introduced in VFS API v3 */
typedef struct retro_vfs_dir_handle *(RETRO_CALLCONV *retro_vfs_opendir_t)(const char *dir, bool include_hidden);
/* Read the directory entry at the current position, and move the read pointer to the next position.
* Returns true on success, false if already on the last entry.
* Introduced in VFS API v3 */
typedef bool (RETRO_CALLCONV *retro_vfs_readdir_t)(struct retro_vfs_dir_handle *dirstream);
/* Get the name of the last entry read. Returns a string on success, or NULL for error.
* The returned string pointer is valid until the next call to readdir or closedir.
* Introduced in VFS API v3 */
typedef const char *(RETRO_CALLCONV *retro_vfs_dirent_get_name_t)(struct retro_vfs_dir_handle *dirstream);
/* Check if the last entry read was a directory. Returns true if it was, false otherwise (or on error).
* Introduced in VFS API v3 */
typedef bool (RETRO_CALLCONV *retro_vfs_dirent_is_dir_t)(struct retro_vfs_dir_handle *dirstream);
/* Close the directory and release its resources. Must be called if opendir returns non-NULL. Returns 0 on success, -1 on failure.
* Whether the call succeeds ot not, the handle passed as parameter becomes invalid and should no longer be used.
* Introduced in VFS API v3 */
typedef int (RETRO_CALLCONV *retro_vfs_closedir_t)(struct retro_vfs_dir_handle *dirstream);
struct retro_vfs_interface
{
/* VFS API v1 */
@ -1185,6 +1228,14 @@ struct retro_vfs_interface
retro_vfs_rename_t rename;
/* VFS API v2 */
retro_vfs_truncate_t truncate;
/* VFS API v3 */
retro_vfs_stat_t stat;
retro_vfs_mkdir_t mkdir;
retro_vfs_opendir_t opendir;
retro_vfs_readdir_t readdir;
retro_vfs_dirent_get_name_t dirent_get_name;
retro_vfs_dirent_is_dir_t dirent_is_dir;
retro_vfs_closedir_t closedir;
};
struct retro_vfs_interface_info

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010-2018 The RetroArch team
/* Copyright (C) 2010-2019 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (retro_dirent.h).
@ -23,6 +23,7 @@
#ifndef __RETRO_DIRENT_H
#define __RETRO_DIRENT_H
#include <libretro.h>
#include <retro_common_api.h>
#include <retro_miscellaneous.h>
@ -30,6 +31,10 @@
RETRO_BEGIN_DECLS
#define DIRENT_REQUIRED_VFS_VERSION 3
void dirent_vfs_init(const struct retro_vfs_interface_info* vfs_info);
typedef struct RDIR RDIR;
/**
@ -44,25 +49,27 @@ typedef struct RDIR RDIR;
*/
struct RDIR *retro_opendir(const char *name);
struct RDIR *retro_opendir_include_hidden(const char *name, bool include_hidden);
int retro_readdir(struct RDIR *rdir);
/* Deprecated, returns false, left for compatibility */
bool retro_dirent_error(struct RDIR *rdir);
void retro_dirent_include_hidden(struct RDIR *rdir, bool include_hidden);
const char *retro_dirent_get_name(struct RDIR *rdir);
/**
*
* retro_dirent_is_dir:
* @rdir : pointer to the directory entry.
* @unused : deprecated, included for compatibility reasons, pass NULL
*
* Is the directory listing entry a directory?
*
* Returns: true if directory listing entry is
* a directory, false if not.
*/
bool retro_dirent_is_dir(struct RDIR *rdir, const char *path);
bool retro_dirent_is_dir(struct RDIR *rdir, const char *unused);
void retro_closedir(struct RDIR *rdir);

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010-2018 The RetroArch team
/* Copyright (C) 2010-2019 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (vfs_implementation.h).
@ -38,6 +38,12 @@ typedef struct retro_vfs_file_handle libretro_vfs_implementation_file;
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
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);
@ -64,4 +70,18 @@ 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);
#endif

View File

@ -171,13 +171,11 @@ static int dir_list_read(const char *dir,
bool include_dirs, bool include_hidden,
bool include_compressed, bool recursive)
{
struct RDIR *entry = retro_opendir(dir);
struct RDIR *entry = retro_opendir_include_hidden(dir, include_hidden);
if (!entry || retro_dirent_error(entry))
goto error;
retro_dirent_include_hidden(entry, include_hidden);
while (retro_readdir(entry))
{
char file_path[PATH_MAX_LENGTH];
@ -189,7 +187,7 @@ static int dir_list_read(const char *dir,
file_path[0] = '\0';
fill_pathname_join(file_path, dir, name, sizeof(file_path));
is_dir = retro_dirent_is_dir(entry, file_path);
is_dir = retro_dirent_is_dir(entry, NULL);
if(!is_dir)
file_ext = path_get_extension(name);

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010-2018 The RetroArch team
/* Copyright (C) 2010-2019 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (vfs_implementation.c).
@ -34,6 +34,7 @@
# ifdef _MSC_VER
# define setmode _setmode
# endif
#include <sys/stat.h>
# ifdef _XBOX
# include <xtl.h>
# define INVALID_FILE_ATTRIBUTES -1
@ -73,6 +74,90 @@
#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
# if defined(PS2)
# include <fileXio_rpc.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) || defined(PS2)
#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(PS2)
#include <fileXio_rpc.h>
#include <fileXio.h>
#endif
#if defined(__CELLOS_LV2__)
#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)
@ -99,6 +184,7 @@
#include <memmap.h>
#include <encodings/utf.h>
#include <compat/fopen_utf8.h>
#include <file/file_path.h>
#define RFILE_HINT_UNBUFFERED (1 << 8)
@ -712,3 +798,408 @@ const char *retro_vfs_file_get_path_impl(libretro_vfs_implementation_file *strea
abort();
return stream->orig_path;
}
int retro_vfs_stat_impl(const char *path, int32_t *size)
{
bool is_dir, is_character_special;
#if defined(ORBIS)
return 0; /* for now */
#endif
#if defined(VITA) || defined(PSP)
SceIoStat buf;
char *tmp = strdup(path);
size_t len = strlen(tmp);
if (tmp[len-1] == '/')
tmp[len-1] = '\0';
if (sceIoGetstat(tmp, &buf) < 0)
{
free(tmp);
return 0;
}
free(tmp);
#elif defined(PS2)
iox_stat_t buf;
char *tmp = strdup(path);
size_t len = strlen(tmp);
if (tmp[len-1] == '/')
tmp[len-1] = '\0';
if (fileXioGetStat(tmp, &buf) < 0)
{
free(tmp);
return 0;
}
free(tmp);
#elif defined(__CELLOS_LV2__)
CellFsStat buf;
if (cellFsStat(path, &buf) < 0)
return 0;
#elif defined(_WIN32)
DWORD file_info;
struct _stat buf;
char *path_local = NULL;
wchar_t *path_wide = NULL;
if (!path || !*path)
return 0;
(void)path_wide;
(void)path_local;
(void)file_info;
#if defined(LEGACY_WIN32)
path_local = utf8_to_local_string_alloc(path);
file_info = GetFileAttributes(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;
#else
struct stat buf;
if (stat(path, &buf) < 0)
return 0;
#endif
if (size)
#if defined(PS2)
*size = (int32_t)buf.size;
#else
*size = (int32_t)buf.st_size;
#endif
#if defined(VITA) || defined(PSP)
is_dir = FIO_S_ISDIR(buf.st_mode);
#elif defined(PS2)
is_dir = FIO_S_ISDIR(buf.mode);
#elif defined(__CELLOS_LV2__)
is_dir = ((buf.st_mode & S_IFMT) == S_IFDIR);
#elif defined(_WIN32)
is_dir = (file_info & FILE_ATTRIBUTE_DIRECTORY);
#else
is_dir = S_ISDIR(buf.st_mode);
#endif
#if defined(VITA) || defined(PSP) || defined(PS2) || defined(__CELLOS_LV2__) || defined(_WIN32)
is_character_special = false;
#else
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);
}
static bool path_mkdir_error(int ret)
{
#if defined(VITA)
return (ret == SCE_ERROR_ERRNO_EEXIST);
#elif defined(PSP) || defined(PS2) || defined(_3DS) || defined(WIIU) || defined(SWITCH) || defined(ORBIS)
return (ret == -1);
#else
return (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 *dirW = utf8_to_utf16_string_alloc(dir);
int ret = -1;
if (dirW)
{
ret = _wmkdir(dirW);
free(dirW);
}
#endif
#elif defined(IOS)
int ret = mkdir(dir, 0755);
#elif defined(VITA) || defined(PSP)
int ret = sceIoMkdir(dir, 0777);
#elif defined(PS2)
int ret =fileXioMkdir(dir, 0777);
#elif defined(ORBIS)
int ret =orbisMkdir(dir, 0755);
#elif defined(__QNX__)
int ret = mkdir(dir, 0777);
#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(PS2)
int directory;
iox_dirent_t entry;
#elif defined(__CELLOS_LV2__)
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(PS2) || defined(ORBIS)
return (rdir->directory < 0);
#elif defined(__CELLOS_LV2__)
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)
char path_buf[1024];
char *path_local = NULL;
wchar_t *path_wide = NULL;
unsigned path_len;
#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)
(void)path_wide;
(void)path_local;
path_buf[0] = '\0';
path_len = strlen(name);
/* 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 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(PS2)
rdir->directory = fileXioDopen(name);
#elif defined(_3DS)
rdir->directory = !string_is_empty(name) ? opendir(name) : NULL;
rdir->entry = NULL;
#elif defined(__CELLOS_LV2__)
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;
free(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(PS2)
iox_dirent_t record;
int ret = fileXioDread(rdir->directory, &record);
rdir->entry = record;
return ( ret > 0);
#elif defined(__CELLOS_LV2__)
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 = 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);
#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));
if (name)
free(name);
#endif
return (char*)rdir->entry.cFileName;
#elif defined(VITA) || defined(PSP) || defined(__CELLOS_LV2__) || defined(ORBIS)
return rdir->entry.d_name;
#elif defined(PS2)
return rdir->entry.name;
#else
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(PS2)
const iox_dirent_t *entry = (const iox_dirent_t*)&rdir->entry;
return FIO_S_ISDIR(entry->stat.mode);
#elif defined(__CELLOS_LV2__)
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;
#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 ... */
char path[PATH_MAX_LENGTH];
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(PS2)
fileXioDclose(rdir->directory);
#elif defined(__CELLOS_LV2__)
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;
}