1 /* Copyright (C) 2010-2020 The RetroArch team
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (vfs_implementation.c).
5 * ---------------------------------------------------------------------------------------
7 * Permission is hereby granted, free of charge,
8 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 #include <sys/types.h>
29 #include <string/stdstring.h> /* string_is_empty */
37 # define setmode _setmode
42 # define INVALID_FILE_ATTRIBUTES -1
52 # include <pspiofilemgr.h>
54 # include <sys/types.h>
55 # include <sys/stat.h>
67 /* TODO: Some things are duplicated but I'm really afraid of breaking other platforms by touching this */
69 # include <psp2/io/fcntl.h>
70 # include <psp2/io/dirent.h>
71 # include <psp2/io/stat.h>
72 #elif !defined(_WIN32)
74 # include <pspiofilemgr.h>
76 # include <sys/types.h>
77 # include <sys/stat.h>
82 #if defined(__QNX__) || defined(PSP)
83 #include <unistd.h> /* stat() is defined here */
87 #include <CoreFoundation/CoreFoundation.h>
90 #include <kernel/image.h>
93 #include <compat/strl.h>
94 #include <compat/posix_string.h>
96 #include <compat/strcasestr.h>
97 #include <retro_miscellaneous.h>
98 #include <encodings/utf.h>
102 #if defined(_MSC_VER) && _MSC_VER <= 1200
103 #define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
107 #define SCE_ERROR_ERRNO_EEXIST 0x80010011
108 #include <psp2/io/fcntl.h>
109 #include <psp2/io/dirent.h>
110 #include <psp2/io/stat.h>
112 #include <sys/types.h>
113 #include <sys/stat.h>
119 #include <pspkernel.h>
122 #if defined(__PS3__) || defined(__PSL1GHT__)
123 #define FS_SUCCEEDED 0
124 #define FS_TYPE_DIR 1
126 #include <lv2/sysfs.h>
128 #define O_RDONLY SYS_O_RDONLY
131 #define O_WRONLY SYS_O_WRONLY
134 #define O_CREAT SYS_O_CREAT
137 #define O_TRUNC SYS_O_TRUNC
140 #define O_RDWR SYS_O_RDWR
143 #include <cell/cell_fs.h>
145 #define O_RDONLY CELL_FS_O_RDONLY
148 #define O_WRONLY CELL_FS_O_WRONLY
151 #define O_CREAT CELL_FS_O_CREAT
154 #define O_TRUNC CELL_FS_O_TRUNC
157 #define O_RDWR CELL_FS_O_RDWR
160 #define sysFsStat cellFsStat
163 #define sysFSDirent CellFsDirent
166 #define sysFsOpendir cellFsOpendir
169 #define sysFsReaddir cellFsReaddir
172 #define sysFSDirent CellFsDirent
174 #ifndef sysFsClosedir
175 #define sysFsClosedir cellFsClosedir
181 #define FIO_S_ISDIR SCE_S_ISDIR
184 #if defined(__QNX__) || defined(PSP)
185 #include <unistd.h> /* stat() is defined here */
188 /* Assume W-functions do not work below Win2K and Xbox platforms */
189 #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
198 #if defined(_MSC_VER) && _MSC_VER >= 1400
199 #define ATLEAST_VC2005
203 #include <vfs/vfs_implementation.h>
204 #include <libretro.h>
205 #if defined(HAVE_MMAP)
208 #include <encodings/utf.h>
209 #include <compat/fopen_utf8.h>
210 #include <file/file_path.h>
213 #include <vfs/vfs_implementation_cdrom.h>
216 #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)
217 #ifndef HAVE_64BIT_OFFSETS
218 #define HAVE_64BIT_OFFSETS
222 #define RFILE_HINT_UNBUFFERED (1 << 8)
224 int64_t retro_vfs_file_seek_internal(
225 libretro_vfs_implementation_file *stream,
226 int64_t offset, int whence)
231 if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
234 if (stream->scheme == VFS_SCHEME_CDROM)
235 return retro_vfs_file_seek_cdrom(stream, offset, whence);
237 #ifdef ATLEAST_VC2005
238 /* VC2005 and up have a special 64-bit fseek */
239 return _fseeki64(stream->fp, offset, whence);
240 #elif defined(HAVE_64BIT_OFFSETS)
241 return fseeko(stream->fp, (off_t)offset, whence);
243 return fseek(stream->fp, (long)offset, whence);
247 /* Need to check stream->mapped because this function is
248 * called in filestream_open() */
249 if (stream->mapped && stream->hints &
250 RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
252 /* fseek() returns error on under/overflow but
253 * allows cursor > EOF for
254 read-only file descriptors. */
261 stream->mappos = offset;
265 if ( (offset < 0 && stream->mappos + offset > stream->mappos) ||
266 (offset > 0 && stream->mappos + offset < stream->mappos))
269 stream->mappos += offset;
273 if (stream->mapsize + offset < stream->mapsize)
276 stream->mappos = stream->mapsize + offset;
279 return stream->mappos;
283 if (lseek(stream->fd, (off_t)offset, whence) < 0)
290 * retro_vfs_file_open_impl:
291 * @path : path to file
292 * @mode : file mode to use when opening (read/write)
295 * Opens a file for reading or writing, depending on the requested mode.
296 * Returns a pointer to an RFILE if opened successfully, otherwise NULL.
299 libretro_vfs_implementation_file *retro_vfs_file_open_impl(
300 const char *path, unsigned mode, unsigned hints)
303 const char *mode_str = NULL;
304 libretro_vfs_implementation_file *stream =
305 (libretro_vfs_implementation_file*)
306 malloc(sizeof(*stream));
312 stream->hints = hints;
319 stream->orig_path = NULL;
322 stream->mapped = NULL;
323 stream->scheme = VFS_SCHEME_NONE;
337 path += sizeof("vfsonly://")-1;
341 stream->cdrom.cue_buf = NULL;
342 stream->cdrom.cue_len = 0;
343 stream->cdrom.byte_pos = 0;
344 stream->cdrom.drive = 0;
345 stream->cdrom.cur_min = 0;
346 stream->cdrom.cur_sec = 0;
347 stream->cdrom.cur_frame = 0;
348 stream->cdrom.cur_track = 0;
349 stream->cdrom.cur_lba = 0;
350 stream->cdrom.last_frame_lba = 0;
351 stream->cdrom.last_frame[0] = '\0';
352 stream->cdrom.last_frame_valid = false;
365 path += sizeof("cdrom://")-1;
366 stream->scheme = VFS_SCHEME_CDROM;
370 stream->orig_path = strdup(path);
373 if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS && mode == RETRO_VFS_FILE_ACCESS_READ)
374 stream->hints |= RFILE_HINT_UNBUFFERED;
377 stream->hints &= ~RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS;
381 case RETRO_VFS_FILE_ACCESS_READ:
390 case RETRO_VFS_FILE_ACCESS_WRITE:
393 flags = O_WRONLY | O_CREAT | O_TRUNC;
395 flags |= S_IRUSR | S_IWUSR;
401 case RETRO_VFS_FILE_ACCESS_READ_WRITE:
403 flags = O_RDWR | O_CREAT | O_TRUNC;
405 flags |= S_IRUSR | S_IWUSR;
411 case RETRO_VFS_FILE_ACCESS_WRITE | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING:
412 case RETRO_VFS_FILE_ACCESS_READ_WRITE | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING:
417 flags |= S_IRUSR | S_IWUSR;
427 if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
431 if (stream->scheme == VFS_SCHEME_CDROM)
433 retro_vfs_file_open_cdrom(stream, path, mode, hints);
434 #if defined(_WIN32) && !defined(_XBOX)
445 if (!(fp = (FILE*)fopen_utf8(path, mode_str)))
451 /* Regarding setvbuf:
453 * https://www.freebsd.org/cgi/man.cgi?query=setvbuf&apropos=0&sektion=0&manpath=FreeBSD+11.1-RELEASE&arch=default&format=html
455 * If the size argument is not zero but buf is NULL,
456 * a buffer of the given size will be allocated immediately, and
457 * released on close. This is an extension to ANSI C.
459 * Since C89 does not support specifying a NULL buffer
460 * with a non-zero size, we create and track our own buffer for it.
462 /* TODO: this is only useful for a few platforms,
463 * find which and add ifdef */
465 if (stream->scheme != VFS_SCHEME_CDROM)
467 stream->buf = (char*)calloc(1, 0x10000);
469 setvbuf(stream->fp, stream->buf, _IOFBF, 0x10000);
472 if (stream->scheme != VFS_SCHEME_CDROM)
474 const int bufsize = 128 * 1024;
475 stream->buf = (char*)memalign(0x40, bufsize);
477 setvbuf(stream->fp, stream->buf, _IOFBF, bufsize);
478 stream->buf = (char*)calloc(1, 0x4000);
480 setvbuf(stream->fp, stream->buf, _IOFBF, 0x4000);
486 #if defined(_WIN32) && !defined(_XBOX)
487 #if defined(LEGACY_WIN32)
488 char *path_local = utf8_to_local_string_alloc(path);
489 stream->fd = open(path_local, flags, 0);
493 wchar_t * path_wide = utf8_to_utf16_string_alloc(path);
494 stream->fd = _wopen(path_wide, flags, 0);
499 stream->fd = open(path, flags, 0);
502 if (stream->fd == -1)
506 if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
509 stream->mapped = NULL;
510 stream->mapsize = retro_vfs_file_seek_internal(stream, 0, SEEK_END);
512 if (stream->mapsize == (uint64_t)-1)
515 retro_vfs_file_seek_internal(stream, 0, SEEK_SET);
517 if ((stream->mapped = (uint8_t*)mmap((void*)0,
518 stream->mapsize, PROT_READ, MAP_SHARED, stream->fd, 0)) == MAP_FAILED)
519 stream->hints &= ~RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS;
524 if (stream->scheme == VFS_SCHEME_CDROM)
526 retro_vfs_file_seek_cdrom(stream, 0, SEEK_SET);
527 retro_vfs_file_seek_cdrom(stream, 0, SEEK_END);
529 stream->size = retro_vfs_file_tell_impl(stream);
531 retro_vfs_file_seek_cdrom(stream, 0, SEEK_SET);
536 retro_vfs_file_seek_internal(stream, 0, SEEK_SET);
537 retro_vfs_file_seek_internal(stream, 0, SEEK_END);
539 stream->size = retro_vfs_file_tell_impl(stream);
541 retro_vfs_file_seek_internal(stream, 0, SEEK_SET);
546 retro_vfs_file_close_impl(stream);
550 int retro_vfs_file_close_impl(libretro_vfs_implementation_file *stream)
556 if (stream->scheme == VFS_SCHEME_CDROM)
558 retro_vfs_file_close_cdrom(stream);
563 if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
571 if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
572 munmap(stream->mapped, stream->mapsize);
580 if (stream->cdrom.cue_buf)
581 free(stream->cdrom.cue_buf);
586 if (stream->orig_path)
587 free(stream->orig_path);
594 int retro_vfs_file_error_impl(libretro_vfs_implementation_file *stream)
597 if (stream->scheme == VFS_SCHEME_CDROM)
598 return retro_vfs_file_error_cdrom(stream);
600 return ferror(stream->fp);
603 int64_t retro_vfs_file_size_impl(libretro_vfs_implementation_file *stream)
610 int64_t retro_vfs_file_truncate_impl(libretro_vfs_implementation_file *stream, int64_t length)
613 if (stream && _chsize(_fileno(stream->fp), length) == 0)
615 stream->size = length;
618 #elif !defined(VITA) && !defined(PSP) && !defined(PS2) && !defined(ORBIS) && (!defined(SWITCH) || defined(HAVE_LIBNX))
619 if (stream && ftruncate(fileno(stream->fp), (off_t)length) == 0)
621 stream->size = length;
628 int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream)
633 if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
636 if (stream->scheme == VFS_SCHEME_CDROM)
637 return retro_vfs_file_tell_cdrom(stream);
639 #ifdef ATLEAST_VC2005
640 /* VC2005 and up have a special 64-bit ftell */
641 return _ftelli64(stream->fp);
642 #elif defined(HAVE_64BIT_OFFSETS)
643 return ftello(stream->fp);
645 return ftell(stream->fp);
649 /* Need to check stream->mapped because this function
650 * is called in filestream_open() */
651 if (stream->mapped && stream->hints &
652 RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
653 return stream->mappos;
655 if (lseek(stream->fd, 0, SEEK_CUR) < 0)
661 int64_t retro_vfs_file_seek_impl(libretro_vfs_implementation_file *stream,
662 int64_t offset, int seek_position)
664 return retro_vfs_file_seek_internal(stream, offset, seek_position);
667 int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream,
668 void *s, uint64_t len)
673 if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
676 if (stream->scheme == VFS_SCHEME_CDROM)
677 return retro_vfs_file_read_cdrom(stream, s, len);
679 return fread(s, 1, (size_t)len, stream->fp);
682 if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
684 if (stream->mappos > stream->mapsize)
687 if (stream->mappos + len > stream->mapsize)
688 len = stream->mapsize - stream->mappos;
690 memcpy(s, &stream->mapped[stream->mappos], len);
691 stream->mappos += len;
697 return read(stream->fd, s, (size_t)len);
700 int64_t retro_vfs_file_write_impl(libretro_vfs_implementation_file *stream, const void *s, uint64_t len)
708 if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
710 pos = retro_vfs_file_tell_impl(stream);
711 result = fwrite(s, 1, (size_t)len, stream->fp);
713 if (result != -1 && pos + result > stream->size)
714 stream->size = pos + result;
719 if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
723 pos = retro_vfs_file_tell_impl(stream);
724 result = write(stream->fd, s, (size_t)len);
726 if (result != -1 && pos + result > stream->size)
727 stream->size = pos + result;
732 int retro_vfs_file_flush_impl(libretro_vfs_implementation_file *stream)
734 if (stream && fflush(stream->fp) == 0)
739 int retro_vfs_file_remove_impl(const char *path)
741 #if defined(_WIN32) && !defined(_XBOX)
742 /* Win32 (no Xbox) */
744 #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500
745 char *path_local = NULL;
747 wchar_t *path_wide = NULL;
751 #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500
752 if ((path_local = utf8_to_local_string_alloc(path)))
754 int ret = remove(path_local);
761 if ((path_wide = utf8_to_utf16_string_alloc(path)))
763 int ret = _wremove(path_wide);
771 if (remove(path) == 0)
777 int retro_vfs_file_rename_impl(const char *old_path, const char *new_path)
779 #if defined(_WIN32) && !defined(_XBOX)
780 /* Win32 (no Xbox) */
782 #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500
783 char *old_path_local = NULL;
785 wchar_t *old_path_wide = NULL;
788 if (!old_path || !*old_path || !new_path || !*new_path)
791 #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500
792 old_path_local = utf8_to_local_string_alloc(old_path);
796 char *new_path_local = utf8_to_local_string_alloc(new_path);
800 if (rename(old_path_local, new_path_local) == 0)
802 free(new_path_local);
805 free(old_path_local);
808 old_path_wide = utf8_to_utf16_string_alloc(old_path);
812 wchar_t *new_path_wide = utf8_to_utf16_string_alloc(new_path);
816 if (_wrename(old_path_wide, new_path_wide) == 0)
827 /* Every other platform */
828 if (!old_path || !*old_path || !new_path || !*new_path)
830 return rename(old_path, new_path) == 0 ? 0 : -1;
834 const char *retro_vfs_file_get_path_impl(
835 libretro_vfs_implementation_file *stream)
837 /* should never happen, do something noisy so caller can be fixed */
840 return stream->orig_path;
843 int retro_vfs_stat_impl(const char *path, int32_t *size)
846 bool is_character_special = false;
859 if (tmp[len-1] == '/')
862 dir_ret = sceIoGetstat(tmp, &buf);
868 *size = (int32_t)buf.st_size;
870 is_dir = FIO_S_ISDIR(buf.st_mode);
871 #elif defined(__PSL1GHT__) || defined(__PS3__)
877 if (sysFsStat(path, &buf) < 0)
881 *size = (int32_t)buf.st_size;
883 is_dir = ((buf.st_mode & S_IFMT) == S_IFDIR);
884 #elif defined(_WIN32)
888 #if defined(LEGACY_WIN32)
889 char *path_local = NULL;
891 wchar_t *path_wide = NULL;
897 #if defined(LEGACY_WIN32)
898 path_local = utf8_to_local_string_alloc(path);
899 file_info = GetFileAttributes(path_local);
901 if (!string_is_empty(path_local))
902 _stat(path_local, &buf);
907 path_wide = utf8_to_utf16_string_alloc(path);
908 file_info = GetFileAttributesW(path_wide);
910 _wstat(path_wide, &buf);
916 if (file_info == INVALID_FILE_ATTRIBUTES)
920 *size = (int32_t)buf.st_size;
922 is_dir = (file_info & FILE_ATTRIBUTE_DIRECTORY);
925 /* On GEKKO platforms, paths cannot have
926 * trailing slashes - we must therefore
928 char *path_buf = NULL;
930 struct stat stat_buf;
933 if (string_is_empty(path))
936 if (!(path_buf = strdup(path)))
939 if ((len = strlen(path_buf)) > 0)
940 if (path_buf[len - 1] == '/')
941 path_buf[len - 1] = '\0';
943 stat_ret = stat(path_buf, &stat_buf);
950 *size = (int32_t)stat_buf.st_size;
952 is_dir = S_ISDIR(stat_buf.st_mode);
953 is_character_special = S_ISCHR(stat_buf.st_mode);
956 /* Every other platform */
961 if (stat(path, &buf) < 0)
965 *size = (int32_t)buf.st_size;
967 is_dir = S_ISDIR(buf.st_mode);
968 is_character_special = S_ISCHR(buf.st_mode);
970 return RETRO_VFS_STAT_IS_VALID | (is_dir ? RETRO_VFS_STAT_IS_DIRECTORY : 0) | (is_character_special ? RETRO_VFS_STAT_IS_CHARACTER_SPECIAL : 0);
974 #define path_mkdir_error(ret) (((ret) == SCE_ERROR_ERRNO_EEXIST))
975 #elif defined(PSP) || defined(PS2) || defined(_3DS) || defined(WIIU) || defined(SWITCH)
976 #define path_mkdir_error(ret) ((ret) == -1)
978 #define path_mkdir_error(ret) ((ret) < 0 && errno == EEXIST)
981 int retro_vfs_mkdir_impl(const char *dir)
985 int ret = _mkdir(dir);
987 wchar_t *dir_w = utf8_to_utf16_string_alloc(dir);
992 ret = _wmkdir(dir_w);
997 int ret = mkdir(dir, 0755);
999 int ret = sceIoMkdir(dir, 0777);
1000 #elif defined(__QNX__)
1001 int ret = mkdir(dir, 0777);
1002 #elif defined(GEKKO) || defined(WIIU)
1003 /* On GEKKO platforms, mkdir() fails if
1004 * the path has a trailing slash. We must
1005 * therefore remove it. */
1007 if (!string_is_empty(dir))
1009 char *dir_buf = strdup(dir);
1013 size_t len = strlen(dir_buf);
1016 if (dir_buf[len - 1] == '/')
1017 dir_buf[len - 1] = '\0';
1019 ret = mkdir(dir_buf, 0750);
1025 int ret = mkdir(dir, 0750);
1028 if (path_mkdir_error(ret))
1030 return ret < 0 ? -1 : 0;
1034 struct retro_vfs_dir_handle
1036 struct libretro_vfs_implementation_dir
1041 #if defined(LEGACY_WIN32)
1042 WIN32_FIND_DATA entry;
1044 WIN32_FIND_DATAW entry;
1048 char path[PATH_MAX_LENGTH];
1052 #elif defined(__PSL1GHT__) || defined(__PS3__)
1058 const struct dirent *entry;
1062 static bool dirent_check_error(libretro_vfs_implementation_dir *rdir)
1065 return (rdir->directory == INVALID_HANDLE_VALUE);
1066 #elif defined(VITA) || defined(ORBIS)
1067 return (rdir->directory < 0);
1068 #elif defined(__PSL1GHT__) || defined(__PS3__)
1069 return (rdir->error != FS_SUCCEEDED);
1071 return !(rdir->directory);
1075 libretro_vfs_implementation_dir *retro_vfs_opendir_impl(
1076 const char *name, bool include_hidden)
1079 char path_buf[1024];
1081 #if defined(LEGACY_WIN32)
1082 char *path_local = NULL;
1084 wchar_t *path_wide = NULL;
1087 libretro_vfs_implementation_dir *rdir;
1089 /* Reject NULL or empty string paths*/
1090 if (!name || (*name == 0))
1093 /*Allocate RDIR struct. Tidied later with retro_closedir*/
1094 if (!(rdir = (libretro_vfs_implementation_dir*)
1095 calloc(1, sizeof(*rdir))))
1098 rdir->orig_path = strdup(name);
1101 copied = strlcpy(path_buf, name, sizeof(path_buf));
1103 /* Non-NT platforms don't like extra slashes in the path */
1104 if (path_buf[copied - 1] != '\\')
1105 path_buf [copied++] = '\\';
1107 path_buf[copied ] = '*';
1108 path_buf[copied+1] = '\0';
1110 #if defined(LEGACY_WIN32)
1111 path_local = utf8_to_local_string_alloc(path_buf);
1112 rdir->directory = FindFirstFile(path_local, &rdir->entry);
1117 path_wide = utf8_to_utf16_string_alloc(path_buf);
1118 rdir->directory = FindFirstFileW(path_wide, &rdir->entry);
1125 rdir->directory = sceIoDopen(name);
1127 rdir->directory = !string_is_empty(name) ? opendir(name) : NULL;
1129 #elif defined(__PSL1GHT__) || defined(__PS3__)
1130 rdir->error = sysFsOpendir(name, &rdir->directory);
1132 rdir->directory = opendir(name);
1138 rdir->entry.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
1140 rdir->entry.dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
1143 if (rdir->directory && !dirent_check_error(rdir))
1146 retro_vfs_closedir_impl(rdir);
1150 bool retro_vfs_readdir_impl(libretro_vfs_implementation_dir *rdir)
1154 #if defined(LEGACY_WIN32)
1155 return (FindNextFile(rdir->directory, &rdir->entry) != 0);
1157 return (FindNextFileW(rdir->directory, &rdir->entry) != 0);
1161 return (rdir->directory != INVALID_HANDLE_VALUE);
1163 return (sceIoDread(rdir->directory, &rdir->entry) > 0);
1164 #elif defined(__PSL1GHT__) || defined(__PS3__)
1166 rdir->error = sysFsReaddir(rdir->directory, &rdir->entry, &nread);
1167 return (nread != 0);
1169 return ((rdir->entry = readdir(rdir->directory)) != NULL);
1173 const char *retro_vfs_dirent_get_name_impl(libretro_vfs_implementation_dir *rdir)
1176 #if defined(LEGACY_WIN32)
1177 char *name = local_to_utf8_string_alloc(rdir->entry.cFileName);
1179 char *name = utf16_to_utf8_string_alloc(rdir->entry.cFileName);
1181 memset(rdir->entry.cFileName, 0, sizeof(rdir->entry.cFileName));
1182 strlcpy((char*)rdir->entry.cFileName, name, sizeof(rdir->entry.cFileName));
1185 return (char*)rdir->entry.cFileName;
1186 #elif defined(VITA) || defined(__PSL1GHT__) || defined(__PS3__)
1187 return rdir->entry.d_name;
1189 if (!rdir || !rdir->entry)
1191 return rdir->entry->d_name;
1195 bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *rdir)
1198 const WIN32_FIND_DATA *entry = (const WIN32_FIND_DATA*)&rdir->entry;
1199 return entry->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
1201 const SceIoDirent *entry = (const SceIoDirent*)&rdir->entry;
1202 return SCE_S_ISDIR(entry->d_stat.st_mode);
1203 #elif defined(__PSL1GHT__) || defined(__PS3__)
1204 sysFSDirent *entry = (sysFSDirent*)&rdir->entry;
1205 return (entry->d_type == FS_TYPE_DIR);
1208 char path[PATH_MAX_LENGTH];
1210 const struct dirent *entry = (const struct dirent*)rdir->entry;
1211 if (entry->d_type == DT_DIR)
1213 /* This can happen on certain file systems. */
1214 if (!(entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK))
1217 /* dirent struct doesn't have d_type, do it the slow way ... */
1218 fill_pathname_join_special(path, rdir->orig_path, retro_vfs_dirent_get_name_impl(rdir), sizeof(path));
1219 if (stat(path, &buf) < 0)
1221 return S_ISDIR(buf.st_mode);
1225 int retro_vfs_closedir_impl(libretro_vfs_implementation_dir *rdir)
1231 if (rdir->directory != INVALID_HANDLE_VALUE)
1232 FindClose(rdir->directory);
1234 sceIoDclose(rdir->directory);
1235 #elif defined(__PSL1GHT__) || defined(__PS3__)
1236 rdir->error = sysFsClosedir(rdir->directory);
1238 if (rdir->directory)
1239 closedir(rdir->directory);
1242 if (rdir->orig_path)
1243 free(rdir->orig_path);