From 4cc4bdb9a9302b3d32abe6a4a2ad767c74e7b451 Mon Sep 17 00:00:00 2001 From: Craig Carnell <1188869+cscd98@users.noreply.github.com> Date: Wed, 21 Jan 2026 12:01:36 +0000 Subject: [PATCH] vfs: add retro_vfs_stat64_t --- libretro-common/file/file_path_io.c | 40 ++++++++++++++----- .../formats/logiqx_dat/logiqx_dat.c | 2 +- libretro-common/include/file/file_path.h | 3 +- libretro-common/include/libretro.h | 17 ++++++++ .../include/vfs/vfs_implementation.h | 2 + .../include/vfs/vfs_implementation_saf.h | 2 +- libretro-common/vfs/vfs_implementation.c | 40 ++++++++++++++----- libretro-common/vfs/vfs_implementation_saf.c | 4 +- libretro-common/vfs/vfs_implementation_smb.c | 4 +- libretro-common/vfs/vfs_implementation_smb.h | 2 +- .../vfs/vfs_implementation_uwp.cpp | 19 ++++++++- runloop.c | 6 ++- 12 files changed, 110 insertions(+), 31 deletions(-) diff --git a/libretro-common/file/file_path_io.c b/libretro-common/file/file_path_io.c index d8f8187c8b9d..ff2ec6265a83 100644 --- a/libretro-common/file/file_path_io.c +++ b/libretro-common/file/file_path_io.c @@ -43,7 +43,8 @@ #endif /* TODO/FIXME - globals */ -static retro_vfs_stat_t path_stat_cb = retro_vfs_stat_impl; +static retro_vfs_stat_t path_stat32_cb = retro_vfs_stat_impl; +static retro_vfs_stat_64_t path_stat64_cb = retro_vfs_stat_64_impl; static retro_vfs_mkdir_t path_mkdir_cb = retro_vfs_mkdir_impl; void path_vfs_init(const struct retro_vfs_interface_info* vfs_info) @@ -51,19 +52,26 @@ 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 = retro_vfs_stat_impl; + path_stat32_cb = retro_vfs_stat_impl; + path_stat64_cb = retro_vfs_stat_64_impl; path_mkdir_cb = retro_vfs_mkdir_impl; if (vfs_info->required_interface_version < PATH_REQUIRED_VFS_VERSION || !vfs_iface) return; - path_stat_cb = vfs_iface->stat; + path_stat32_cb = vfs_iface->stat; path_mkdir_cb = vfs_iface->mkdir; + + if (vfs_info->required_interface_version >= STAT64_REQUIRED_VFS_VERSION) + path_stat64_cb = vfs_iface->stat_64; + else + path_stat64_cb = NULL; } int path_stat(const char *path) { - return path_stat_cb(path, NULL); + /* Use 64‑bit stat if available, else fallback */ + return path_stat64_cb ? path_stat64_cb(path, NULL) : path_stat32_cb(path, NULL); } /** @@ -76,25 +84,37 @@ int path_stat(const char *path) */ bool path_is_directory(const char *path) { - return (path_stat_cb(path, NULL) & RETRO_VFS_STAT_IS_DIRECTORY) != 0; + if (path_stat64_cb) + return (path_stat64_cb(path, NULL) & RETRO_VFS_STAT_IS_DIRECTORY) != 0; + return (path_stat32_cb(path, NULL) & RETRO_VFS_STAT_IS_DIRECTORY) != 0; } bool path_is_character_special(const char *path) { - return (path_stat_cb(path, NULL) & RETRO_VFS_STAT_IS_CHARACTER_SPECIAL) != 0; + if (path_stat64_cb) + return (path_stat64_cb(path, NULL) & RETRO_VFS_STAT_IS_CHARACTER_SPECIAL) != 0; + return (path_stat32_cb(path, NULL) & RETRO_VFS_STAT_IS_CHARACTER_SPECIAL) != 0; } bool path_is_valid(const char *path) { - return (path_stat_cb(path, NULL) & RETRO_VFS_STAT_IS_VALID) != 0; + if (path_stat64_cb) + return (path_stat64_cb(path, NULL) & RETRO_VFS_STAT_IS_VALID) != 0; + return (path_stat32_cb(path, NULL) & RETRO_VFS_STAT_IS_VALID) != 0; } -int32_t path_get_size(const char *path) +int64_t path_get_size(const char *path) { - int32_t filesize = 0; - if (path_stat_cb(path, &filesize) != 0) + int64_t filesize = 0; + int32_t filesize32 = 0; + + if (path_stat64_cb && path_stat64_cb(path, &filesize) != 0) return filesize; + /* Fallback: 32-bit stat */ + if (path_stat32_cb && path_stat32_cb(path, &filesize32) != 0) + return (int64_t)filesize32; + return -1; } diff --git a/libretro-common/formats/logiqx_dat/logiqx_dat.c b/libretro-common/formats/logiqx_dat/logiqx_dat.c index 7ec79df8f847..9c250a25f0c8 100644 --- a/libretro-common/formats/logiqx_dat/logiqx_dat.c +++ b/libretro-common/formats/logiqx_dat/logiqx_dat.c @@ -57,7 +57,7 @@ const char *logiqx_dat_html_code_list[][2] = { bool logiqx_dat_path_is_valid(const char *path, uint64_t *file_size) { const char *file_ext = NULL; - int32_t file_size_int; + int64_t file_size_int; if (string_is_empty(path)) return false; diff --git a/libretro-common/include/file/file_path.h b/libretro-common/include/file/file_path.h index d559ccf389e1..87c8233431e1 100644 --- a/libretro-common/include/file/file_path.h +++ b/libretro-common/include/file/file_path.h @@ -36,6 +36,7 @@ RETRO_BEGIN_DECLS #define PATH_REQUIRED_VFS_VERSION 3 +#define STAT64_REQUIRED_VFS_VERSION 4 void path_vfs_init(const struct retro_vfs_interface_info* vfs_info); @@ -681,7 +682,7 @@ int path_stat(const char *path); bool path_is_valid(const char *path); -int32_t path_get_size(const char *path); +int64_t path_get_size(const char *path); bool is_path_accessible_using_standard_io(const char *path); diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h index a63f4e2144c1..4341c83a13b0 100644 --- a/libretro-common/include/libretro.h +++ b/libretro-common/include/libretro.h @@ -2923,6 +2923,19 @@ typedef int (RETRO_CALLCONV *retro_vfs_rename_t)(const char *old_path, const cha */ typedef int (RETRO_CALLCONV *retro_vfs_stat_t)(const char *path, int32_t *size); +/** + * Gets information about the given file (64-bit size). + * + * @param path The path to the file to query. + * @param[out] size The reported size of the file in bytes. + * May be \c NULL, in which case this value is ignored. + * @return A bitmask of \c RETRO_VFS_STAT flags, + * or 0 if \c path doesn't refer to a valid file. + * @see RETRO_VFS_STAT + * @since VFS API v4 + */ +typedef int (RETRO_CALLCONV *retro_vfs_stat_64_t)(const char *path, int64_t *size); + /** * Creates a directory at the given path. * @@ -3078,6 +3091,10 @@ struct retro_vfs_interface /** @copydoc retro_vfs_closedir_t */ retro_vfs_closedir_t closedir; + + /* VFS API v4 */ + /** @copydoc retro_vfs_stat_64_t */ + retro_vfs_stat_64_t stat_64; }; /** diff --git a/libretro-common/include/vfs/vfs_implementation.h b/libretro-common/include/vfs/vfs_implementation.h index b88d2f3d2b87..56d11a36de02 100644 --- a/libretro-common/include/vfs/vfs_implementation.h +++ b/libretro-common/include/vfs/vfs_implementation.h @@ -59,6 +59,8 @@ const char *retro_vfs_file_get_path_impl(libretro_vfs_implementation_file *strea int retro_vfs_stat_impl(const char *path, int32_t *size); +int retro_vfs_stat_64_impl(const char *path, int64_t *size); + int retro_vfs_mkdir_impl(const char *dir); libretro_vfs_implementation_dir *retro_vfs_opendir_impl(const char *dir, bool include_hidden); diff --git a/libretro-common/include/vfs/vfs_implementation_saf.h b/libretro-common/include/vfs/vfs_implementation_saf.h index b348d87c7f91..02ebee404325 100644 --- a/libretro-common/include/vfs/vfs_implementation_saf.h +++ b/libretro-common/include/vfs/vfs_implementation_saf.h @@ -84,7 +84,7 @@ int retro_vfs_file_remove_saf(const char *tree, const char *path); int retro_vfs_file_rename_saf(const char *old_tree, const char *old_path, const char *new_tree, const char *new_path); -int retro_vfs_stat_saf(const char *tree, const char *path, int32_t *size); +int retro_vfs_stat_saf(const char *tree, const char *path, int64_t *size); int retro_vfs_mkdir_saf(const char *tree, const char *dir); diff --git a/libretro-common/vfs/vfs_implementation.c b/libretro-common/vfs/vfs_implementation.c index 58fd9101e303..f82a4f4b96d0 100644 --- a/libretro-common/vfs/vfs_implementation.c +++ b/libretro-common/vfs/vfs_implementation.c @@ -1040,7 +1040,7 @@ const char *retro_vfs_file_get_path_impl( return stream->orig_path; } -int retro_vfs_stat_impl(const char *path, int32_t *size) +int retro_vfs_stat_64_impl(const char *path, int64_t *size) { int ret = RETRO_VFS_STAT_IS_VALID; @@ -1088,7 +1088,7 @@ int retro_vfs_stat_impl(const char *path, int32_t *size) return 0; if (size) - *size = (int32_t)stat_buf.st_size; + *size = (int64_t)stat_buf.st_size; if (FIO_S_ISDIR(stat_buf.st_mode)) ret |= RETRO_VFS_STAT_IS_DIRECTORY; @@ -1100,14 +1100,15 @@ int retro_vfs_stat_impl(const char *path, int32_t *size) return 0; if (size) - *size = (int32_t)stat_buf.st_size; + *size = (int64_t)stat_buf.st_size; if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) ret |= RETRO_VFS_STAT_IS_DIRECTORY; #elif defined(_WIN32) /* Windows */ - struct _stat stat_buf; + struct _stat64 stat_buf; #if defined(LEGACY_WIN32) + /* 32-bit only */ char *path_local = utf8_to_local_string_alloc(path); DWORD file_info = GetFileAttributes(path_local); @@ -1120,7 +1121,7 @@ int retro_vfs_stat_impl(const char *path, int32_t *size) wchar_t *path_wide = utf8_to_utf16_string_alloc(path); DWORD file_info = GetFileAttributesW(path_wide); - _wstat(path_wide, &stat_buf); + _wstat64(path_wide, &stat_buf); if (path_wide) free(path_wide); @@ -1129,7 +1130,7 @@ int retro_vfs_stat_impl(const char *path, int32_t *size) return 0; if (size) - *size = (int32_t)stat_buf.st_size; + *size = (int64_t)stat_buf.st_size; if (file_info & FILE_ATTRIBUTE_DIRECTORY) ret |= RETRO_VFS_STAT_IS_DIRECTORY; @@ -1157,7 +1158,7 @@ int retro_vfs_stat_impl(const char *path, int32_t *size) free(path_buf); if (size) - *size = (int32_t)stat_buf.st_size; + *size = (int64_t)stat_buf.st_size; if (S_ISDIR(stat_buf.st_mode)) ret |= RETRO_VFS_STAT_IS_DIRECTORY; @@ -1165,13 +1166,19 @@ int retro_vfs_stat_impl(const char *path, int32_t *size) ret |= RETRO_VFS_STAT_IS_CHARACTER_SPECIAL; #else /* Every other platform */ +#if defined(_LARGEFILE64_SOURCE) + struct stat64 stat_buf; + if (stat64(path, &stat_buf) < 0) + return 0; +#else struct stat stat_buf; if (stat(path, &stat_buf) < 0) return 0; +#endif if (size) - *size = (int32_t)stat_buf.st_size; + *size = (int64_t)stat_buf.st_size; if (S_ISDIR(stat_buf.st_mode)) ret |= RETRO_VFS_STAT_IS_DIRECTORY; @@ -1182,6 +1189,21 @@ int retro_vfs_stat_impl(const char *path, int32_t *size) return ret; } +int retro_vfs_stat_impl(const char *path, int32_t *size) +{ + int64_t size64 = 0; + int ret = retro_vfs_stat_64_impl(path, size ? &size64 : NULL); + + /* if a file is larger than 2 GB, size64 will hold the correct value + * but the cast to int32_t will truncate it. + * new code should migrate to retro_vfs_stat_64_t + */ + if (size) + *size = (int32_t)size64; + + return ret; +} + #if defined(VITA) #define path_mkdir_err(ret) (((ret) == SCE_ERROR_ERRNO_EEXIST)) #elif defined(PSP) || defined(PS2) || defined(_3DS) || defined(WIIU) || defined(SWITCH) @@ -1523,7 +1545,7 @@ bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *rdir) { char full[PATH_MAX_LENGTH]; const char *name = retro_vfs_dirent_get_name_impl(rdir); - int32_t sz = 0; + int64_t sz = 0; int st = 0; if (!name) diff --git a/libretro-common/vfs/vfs_implementation_saf.c b/libretro-common/vfs/vfs_implementation_saf.c index 278b5154b4f4..dcc2a2276a5b 100644 --- a/libretro-common/vfs/vfs_implementation_saf.c +++ b/libretro-common/vfs/vfs_implementation_saf.c @@ -458,7 +458,7 @@ int retro_vfs_file_rename_saf(const char *old_tree, const char *old_path, const return -1; } -int retro_vfs_stat_saf(const char *tree, const char *path, int32_t *size) +int retro_vfs_stat_saf(const char *tree, const char *path, int64_t *size) { JNIEnv *env; jstring tree_object; @@ -515,7 +515,7 @@ int retro_vfs_stat_saf(const char *tree, const char *path, int32_t *size) if ((*env)->ExceptionOccurred(env)) goto error; if (size != NULL) - *size = saf_stat_size > INT32_MAX ? INT32_MAX : (int32_t)saf_stat_size; + *size = saf_stat_size > INT64_MAX ? INT64_MAX : (int64_t)saf_stat_size; (*env)->PopLocalFrame(env, NULL); return saf_stat_is_directory ? RETRO_VFS_STAT_IS_VALID | RETRO_VFS_STAT_IS_DIRECTORY : RETRO_VFS_STAT_IS_VALID; diff --git a/libretro-common/vfs/vfs_implementation_smb.c b/libretro-common/vfs/vfs_implementation_smb.c index 573e41c9e6a2..9916616515bd 100644 --- a/libretro-common/vfs/vfs_implementation_smb.c +++ b/libretro-common/vfs/vfs_implementation_smb.c @@ -544,7 +544,7 @@ int retro_vfs_closedir_smb(smb_dir_handle* dh) return 0; } -int retro_vfs_stat_smb(const char *path, int32_t *size) +int retro_vfs_stat_smb(const char *path, int64_t *size) { char rel_path[PATH_MAX_LENGTH]; struct smb2_stat_64 st; @@ -572,7 +572,7 @@ int retro_vfs_stat_smb(const char *path, int32_t *size) return 0; if (size) - *size = (int32_t)st.smb2_size; + *size = (int64_t)st.smb2_size; return RETRO_VFS_STAT_IS_VALID | (st.smb2_type == SMB2_TYPE_DIRECTORY ? RETRO_VFS_STAT_IS_DIRECTORY : 0); diff --git a/libretro-common/vfs/vfs_implementation_smb.h b/libretro-common/vfs/vfs_implementation_smb.h index c16b46909556..aeefc333ebe9 100644 --- a/libretro-common/vfs/vfs_implementation_smb.h +++ b/libretro-common/vfs/vfs_implementation_smb.h @@ -60,7 +60,7 @@ struct smbc_dirent* retro_vfs_readdir_smb(smb_dir_handle* dh); int retro_vfs_closedir_smb(smb_dir_handle* dh); /* Stat */ -int retro_vfs_stat_smb(const char *path, int32_t *size); +int retro_vfs_stat_smb(const char *path, int64_t *size); /* Errors */ int retro_vfs_file_error_smb(libretro_vfs_implementation_file *stream); diff --git a/libretro-common/vfs/vfs_implementation_uwp.cpp b/libretro-common/vfs/vfs_implementation_uwp.cpp index c6eeb5ffc83f..921357df85e8 100644 --- a/libretro-common/vfs/vfs_implementation_uwp.cpp +++ b/libretro-common/vfs/vfs_implementation_uwp.cpp @@ -662,7 +662,7 @@ const char *retro_vfs_file_get_path_impl(libretro_vfs_implementation_file *strea return stream->orig_path; } -int retro_vfs_stat_impl(const char *path, int32_t *size) +int retro_vfs_stat_64_impl(const char *path, int64_t *size) { wchar_t *path_wide; _WIN32_FILE_ATTRIBUTE_DATA attribdata; @@ -699,6 +699,21 @@ int retro_vfs_stat_impl(const char *path, int32_t *size) return 0; } +int retro_vfs_stat_impl(const char *path, int32_t *size) +{ + int64_t size64 = 0; + int ret = retro_vfs_stat_64_impl(path, size ? &size64 : NULL); + + /* if a file is larger than 2 GB, size64 will hold the correct value + * but the cast to int32_t will truncate it. + * new code should migrate to retro_vfs_stat_64_t + */ + if (size) + *size = (int32_t)size64; + + return ret; +} + #ifdef VFS_FRONTEND struct retro_vfs_dir_handle #else @@ -832,7 +847,7 @@ bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir* rdir) return false; fill_pathname_join_special(full, rdir->orig_path, name, sizeof(full)); - int32_t sz = 0; + int64_t sz = 0; int st = retro_vfs_stat_smb(full, &sz); return (st & RETRO_VFS_STAT_IS_DIRECTORY) != 0; diff --git a/runloop.c b/runloop.c index b2c8227ad1cb..eacfba5326a2 100644 --- a/runloop.c +++ b/runloop.c @@ -3044,7 +3044,7 @@ bool runloop_environment_cb(unsigned cmd, void *data) case RETRO_ENVIRONMENT_GET_VFS_INTERFACE: { - const uint32_t supported_vfs_version = 3; + const uint32_t supported_vfs_version = 4; static struct retro_vfs_interface vfs_iface = { /* VFS API v1 */ @@ -3068,7 +3068,9 @@ bool runloop_environment_cb(unsigned cmd, void *data) retro_vfs_readdir_impl, retro_vfs_dirent_get_name_impl, retro_vfs_dirent_is_dir_impl, - retro_vfs_closedir_impl + retro_vfs_closedir_impl, + /* VFS API v4 */ + retro_vfs_stat_64_impl, }; struct retro_vfs_interface_info *vfs_iface_info = (struct retro_vfs_interface_info *) data;