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>
37 # define setmode _setmode
42 # define INVALID_FILE_ATTRIBUTES -1
52 # include <pspiofilemgr.h>
54 # include <sys/types.h>
55 # include <sys/stat.h>
61 # include <sys/fcntl.h>
62 # include <sys/dirent.h>
63 # include <orbisFile.h>
67 #if defined (__CELLOS_LV2__) && !defined(__PSL1GHT__)
68 #include <cell/cell_fs.h>
69 #define O_RDONLY CELL_FS_O_RDONLY
70 #define O_WRONLY CELL_FS_O_WRONLY
71 #define O_CREAT CELL_FS_O_CREAT
72 #define O_TRUNC CELL_FS_O_TRUNC
73 #define O_RDWR CELL_FS_O_RDWR
78 /* TODO: Some things are duplicated but I'm really afraid of breaking other platforms by touching this */
80 # include <psp2/io/fcntl.h>
81 # include <psp2/io/dirent.h>
82 # include <psp2/io/stat.h>
84 # include <orbisFile.h>
86 # include <sys/dirent.h>
87 # include <sys/fcntl.h>
88 #elif !defined(_WIN32)
90 # include <pspiofilemgr.h>
92 # include <sys/types.h>
93 # include <sys/stat.h>
98 #if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(__QNX__) || defined(PSP)
99 #include <unistd.h> /* stat() is defined here */
103 #include <CoreFoundation/CoreFoundation.h>
106 #include <kernel/image.h>
109 #include <compat/strl.h>
110 #include <compat/posix_string.h>
112 #include <compat/strcasestr.h>
113 #include <retro_miscellaneous.h>
114 #include <encodings/utf.h>
118 #if defined(_MSC_VER) && _MSC_VER <= 1200
119 #define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
123 #define SCE_ERROR_ERRNO_EEXIST 0x80010011
124 #include <psp2/io/fcntl.h>
125 #include <psp2/io/dirent.h>
126 #include <psp2/io/stat.h>
128 #include <sys/types.h>
129 #include <sys/stat.h>
134 #include <orbisFile.h>
135 #include <sys/fcntl.h>
136 #include <sys/dirent.h>
139 #include <pspkernel.h>
142 #if defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
143 #include <cell/cell_fs.h>
147 #define FIO_S_ISDIR SCE_S_ISDIR
150 #if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(__QNX__) || defined(PSP)
151 #include <unistd.h> /* stat() is defined here */
154 /* Assume W-functions do not work below Win2K and Xbox platforms */
155 #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
164 #if defined(_MSC_VER) && _MSC_VER >= 1400
165 #define ATLEAST_VC2005
169 #include <vfs/vfs_implementation.h>
170 #include <libretro.h>
172 #include <encodings/utf.h>
173 #include <compat/fopen_utf8.h>
174 #include <file/file_path.h>
177 #include <vfs/vfs_implementation_cdrom.h>
180 #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)
181 #ifndef HAVE_64BIT_OFFSETS
182 #define HAVE_64BIT_OFFSETS
186 #define RFILE_HINT_UNBUFFERED (1 << 8)
188 int64_t retro_vfs_file_seek_internal(
189 libretro_vfs_implementation_file *stream,
190 int64_t offset, int whence)
195 if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
198 if (stream->scheme == VFS_SCHEME_CDROM)
199 return retro_vfs_file_seek_cdrom(stream, offset, whence);
201 #ifdef ATLEAST_VC2005
202 /* VC2005 and up have a special 64-bit fseek */
203 return _fseeki64(stream->fp, offset, whence);
206 int ret = orbisLseek(stream->fd, offset, whence);
211 #elif defined(HAVE_64BIT_OFFSETS)
212 return fseeko(stream->fp, (off_t)offset, whence);
214 return fseek(stream->fp, (long)offset, whence);
218 /* Need to check stream->mapped because this function is
219 * called in filestream_open() */
220 if (stream->mapped && stream->hints &
221 RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
223 /* fseek() returns error on under/overflow but
224 * allows cursor > EOF for
225 read-only file descriptors. */
232 stream->mappos = offset;
236 if ( (offset < 0 && stream->mappos + offset > stream->mappos) ||
237 (offset > 0 && stream->mappos + offset < stream->mappos))
240 stream->mappos += offset;
244 if (stream->mapsize + offset < stream->mapsize)
247 stream->mappos = stream->mapsize + offset;
250 return stream->mappos;
254 if (lseek(stream->fd, (off_t)offset, whence) < 0)
261 * retro_vfs_file_open_impl:
262 * @path : path to file
263 * @mode : file mode to use when opening (read/write)
266 * Opens a file for reading or writing, depending on the requested mode.
267 * Returns a pointer to an RFILE if opened successfully, otherwise NULL.
270 libretro_vfs_implementation_file *retro_vfs_file_open_impl(
271 const char *path, unsigned mode, unsigned hints)
273 #if defined(VFS_FRONTEND) || defined(HAVE_CDROM)
274 int path_len = (int)strlen(path);
277 const char *dumb_prefix = "vfsonly://";
278 size_t dumb_prefix_siz = STRLEN_CONST("vfsonly://");
279 int dumb_prefix_len = (int)dumb_prefix_siz;
282 const char *cdrom_prefix = "cdrom://";
283 size_t cdrom_prefix_siz = STRLEN_CONST("cdrom://");
284 int cdrom_prefix_len = (int)cdrom_prefix_siz;
287 const char *mode_str = NULL;
288 libretro_vfs_implementation_file *stream =
289 (libretro_vfs_implementation_file*)
290 malloc(sizeof(*stream));
296 stream->hints = hints;
303 stream->orig_path = NULL;
306 stream->mapped = NULL;
307 stream->scheme = VFS_SCHEME_NONE;
310 if (path_len >= dumb_prefix_len)
311 if (!memcmp(path, dumb_prefix, dumb_prefix_len))
312 path += dumb_prefix_siz;
316 stream->cdrom.cue_buf = NULL;
317 stream->cdrom.cue_len = 0;
318 stream->cdrom.byte_pos = 0;
319 stream->cdrom.drive = 0;
320 stream->cdrom.cur_min = 0;
321 stream->cdrom.cur_sec = 0;
322 stream->cdrom.cur_frame = 0;
323 stream->cdrom.cur_track = 0;
324 stream->cdrom.cur_lba = 0;
325 stream->cdrom.last_frame_lba = 0;
326 stream->cdrom.last_frame[0] = '\0';
327 stream->cdrom.last_frame_valid = false;
329 if (path_len > cdrom_prefix_len)
331 if (!memcmp(path, cdrom_prefix, cdrom_prefix_len))
333 path += cdrom_prefix_siz;
334 stream->scheme = VFS_SCHEME_CDROM;
339 stream->orig_path = strdup(path);
342 if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS && mode == RETRO_VFS_FILE_ACCESS_READ)
343 stream->hints |= RFILE_HINT_UNBUFFERED;
346 stream->hints &= ~RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS;
350 case RETRO_VFS_FILE_ACCESS_READ:
359 case RETRO_VFS_FILE_ACCESS_WRITE:
362 flags = O_WRONLY | O_CREAT | O_TRUNC;
365 flags |= S_IRUSR | S_IWUSR;
372 case RETRO_VFS_FILE_ACCESS_READ_WRITE:
374 flags = O_RDWR | O_CREAT | O_TRUNC;
377 flags |= S_IRUSR | S_IWUSR;
384 case RETRO_VFS_FILE_ACCESS_WRITE | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING:
385 case RETRO_VFS_FILE_ACCESS_READ_WRITE | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING:
391 flags |= S_IRUSR | S_IWUSR;
402 if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
405 int fd = orbisOpen(path, flags, 0644);
415 if (stream->scheme == VFS_SCHEME_CDROM)
417 retro_vfs_file_open_cdrom(stream, path, mode, hints);
418 #if defined(_WIN32) && !defined(_XBOX)
429 fp = (FILE*)fopen_utf8(path, mode_str);
436 /* Regarding setvbuf:
438 * https://www.freebsd.org/cgi/man.cgi?query=setvbuf&apropos=0&sektion=0&manpath=FreeBSD+11.1-RELEASE&arch=default&format=html
440 * If the size argument is not zero but buf is NULL,
441 * a buffer of the given size will be allocated immediately, and
442 * released on close. This is an extension to ANSI C.
444 * Since C89 does not support specifying a NULL buffer
445 * with a non-zero size, we create and track our own buffer for it.
447 /* TODO: this is only useful for a few platforms,
448 * find which and add ifdef */
450 if (stream->scheme != VFS_SCHEME_CDROM)
452 stream->buf = (char*)calloc(1, 0x10000);
454 setvbuf(stream->fp, stream->buf, _IOFBF, 0x10000);
457 if (stream->scheme != VFS_SCHEME_CDROM)
459 stream->buf = (char*)calloc(1, 0x4000);
461 setvbuf(stream->fp, stream->buf, _IOFBF, 0x4000);
468 #if defined(_WIN32) && !defined(_XBOX)
469 #if defined(LEGACY_WIN32)
470 char *path_local = utf8_to_local_string_alloc(path);
471 stream->fd = open(path_local, flags, 0);
475 wchar_t * path_wide = utf8_to_utf16_string_alloc(path);
476 stream->fd = _wopen(path_wide, flags, 0);
481 stream->fd = open(path, flags, 0);
484 if (stream->fd == -1)
488 if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
491 stream->mapped = NULL;
492 stream->mapsize = retro_vfs_file_seek_internal(stream, 0, SEEK_END);
494 if (stream->mapsize == (uint64_t)-1)
497 retro_vfs_file_seek_internal(stream, 0, SEEK_SET);
499 stream->mapped = (uint8_t*)mmap((void*)0,
500 stream->mapsize, PROT_READ, MAP_SHARED, stream->fd, 0);
502 if (stream->mapped == MAP_FAILED)
503 stream->hints &= ~RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS;
508 stream->size = orbisLseek(stream->fd, 0, SEEK_END);
509 orbisLseek(stream->fd, 0, SEEK_SET);
512 if (stream->scheme == VFS_SCHEME_CDROM)
514 retro_vfs_file_seek_cdrom(stream, 0, SEEK_SET);
515 retro_vfs_file_seek_cdrom(stream, 0, SEEK_END);
517 stream->size = retro_vfs_file_tell_impl(stream);
519 retro_vfs_file_seek_cdrom(stream, 0, SEEK_SET);
524 retro_vfs_file_seek_internal(stream, 0, SEEK_SET);
525 retro_vfs_file_seek_internal(stream, 0, SEEK_END);
527 stream->size = retro_vfs_file_tell_impl(stream);
529 retro_vfs_file_seek_internal(stream, 0, SEEK_SET);
535 retro_vfs_file_close_impl(stream);
539 int retro_vfs_file_close_impl(libretro_vfs_implementation_file *stream)
545 if (stream->scheme == VFS_SCHEME_CDROM)
547 retro_vfs_file_close_cdrom(stream);
552 if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
560 if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
561 munmap(stream->mapped, stream->mapsize);
568 orbisClose(stream->fd);
576 if (stream->cdrom.cue_buf)
577 free(stream->cdrom.cue_buf);
582 if (stream->orig_path)
583 free(stream->orig_path);
590 int retro_vfs_file_error_impl(libretro_vfs_implementation_file *stream)
593 if (stream->scheme == VFS_SCHEME_CDROM)
594 return retro_vfs_file_error_cdrom(stream);
597 /* TODO/FIXME - implement this? */
600 return ferror(stream->fp);
604 int64_t retro_vfs_file_size_impl(libretro_vfs_implementation_file *stream)
611 int64_t retro_vfs_file_truncate_impl(libretro_vfs_implementation_file *stream, int64_t length)
617 if (_chsize(_fileno(stream->fp), length) != 0)
619 #elif !defined(VITA) && !defined(PSP) && !defined(PS2) && !defined(ORBIS) && (!defined(SWITCH) || defined(HAVE_LIBNX))
620 if (ftruncate(fileno(stream->fp), (off_t)length) != 0)
627 int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream)
632 if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
635 if (stream->scheme == VFS_SCHEME_CDROM)
636 return retro_vfs_file_tell_cdrom(stream);
640 int64_t ret = orbisLseek(stream->fd, 0, SEEK_CUR);
646 #ifdef ATLEAST_VC2005
647 /* VC2005 and up have a special 64-bit ftell */
648 return _ftelli64(stream->fp);
649 #elif defined(HAVE_64BIT_OFFSETS)
650 return ftello(stream->fp);
652 return ftell(stream->fp);
657 /* Need to check stream->mapped because this function
658 * is called in filestream_open() */
659 if (stream->mapped && stream->hints &
660 RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
661 return stream->mappos;
663 if (lseek(stream->fd, 0, SEEK_CUR) < 0)
669 int64_t retro_vfs_file_seek_impl(libretro_vfs_implementation_file *stream,
670 int64_t offset, int seek_position)
673 switch (seek_position)
675 case RETRO_VFS_SEEK_POSITION_START:
678 case RETRO_VFS_SEEK_POSITION_CURRENT:
681 case RETRO_VFS_SEEK_POSITION_END:
686 return retro_vfs_file_seek_internal(stream, offset, whence);
689 int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream,
690 void *s, uint64_t len)
695 if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
698 if (stream->scheme == VFS_SCHEME_CDROM)
699 return retro_vfs_file_read_cdrom(stream, s, len);
702 if (orbisRead(stream->fd, s, (size_t)len) < 0)
706 return fread(s, 1, (size_t)len, stream->fp);
710 if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
712 if (stream->mappos > stream->mapsize)
715 if (stream->mappos + len > stream->mapsize)
716 len = stream->mapsize - stream->mappos;
718 memcpy(s, &stream->mapped[stream->mappos], len);
719 stream->mappos += len;
725 return read(stream->fd, s, (size_t)len);
728 int64_t retro_vfs_file_write_impl(libretro_vfs_implementation_file *stream, const void *s, uint64_t len)
733 if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0)
736 if (orbisWrite(stream->fd, s, (size_t)len) < 0)
740 return fwrite(s, 1, (size_t)len, stream->fp);
745 if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS)
748 return write(stream->fd, s, (size_t)len);
751 int retro_vfs_file_flush_impl(libretro_vfs_implementation_file *stream)
758 return fflush(stream->fp) == 0 ? 0 : -1;
762 int retro_vfs_file_remove_impl(const char *path)
764 #if defined(_WIN32) && !defined(_XBOX)
765 /* Win32 (no Xbox) */
767 #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500
768 char *path_local = NULL;
770 wchar_t *path_wide = NULL;
774 #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500
775 path_local = utf8_to_local_string_alloc(path);
779 int ret = remove(path_local);
786 path_wide = utf8_to_utf16_string_alloc(path);
790 int ret = _wremove(path_wide);
800 * TODO/FIXME - stub for now */
803 if (remove(path) == 0)
809 int retro_vfs_file_rename_impl(const char *old_path, const char *new_path)
811 #if defined(_WIN32) && !defined(_XBOX)
812 /* Win32 (no Xbox) */
814 #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500
815 char *old_path_local = NULL;
817 wchar_t *old_path_wide = NULL;
820 if (!old_path || !*old_path || !new_path || !*new_path)
823 #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500
824 old_path_local = utf8_to_local_string_alloc(old_path);
828 char *new_path_local = utf8_to_local_string_alloc(new_path);
832 if (rename(old_path_local, new_path_local) == 0)
834 free(new_path_local);
837 free(old_path_local);
840 old_path_wide = utf8_to_utf16_string_alloc(old_path);
844 wchar_t *new_path_wide = utf8_to_utf16_string_alloc(new_path);
848 if (_wrename(old_path_wide, new_path_wide) == 0)
860 /* TODO/FIXME - Stub for now */
861 if (!old_path || !*old_path || !new_path || !*new_path)
866 /* Every other platform */
867 if (!old_path || !*old_path || !new_path || !*new_path)
869 return rename(old_path, new_path) == 0 ? 0 : -1;
873 const char *retro_vfs_file_get_path_impl(
874 libretro_vfs_implementation_file *stream)
876 /* should never happen, do something noisy so caller can be fixed */
879 return stream->orig_path;
882 int retro_vfs_stat_impl(const char *path, int32_t *size)
885 bool is_character_special = false;
886 #if defined(VITA) || defined(PSP)
898 if (tmp[len-1] == '/')
901 dir_ret = sceIoGetstat(tmp, &buf);
907 *size = (int32_t)buf.st_size;
909 is_dir = FIO_S_ISDIR(buf.st_mode);
918 *size = (int32_t)buf.st_size;
920 dir_ret = orbisDopen(path);
921 is_dir = dir_ret > 0;
922 orbisDclose(dir_ret);
924 is_character_special = S_ISCHR(buf.st_mode);
925 #elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
931 if (cellFsStat(path, &buf) < 0)
935 *size = (int32_t)buf.st_size;
937 is_dir = ((buf.st_mode & S_IFMT) == S_IFDIR);
939 #elif defined(_WIN32)
943 #if defined(LEGACY_WIN32)
944 char *path_local = NULL;
946 wchar_t *path_wide = NULL;
952 #if defined(LEGACY_WIN32)
953 path_local = utf8_to_local_string_alloc(path);
954 file_info = GetFileAttributes(path_local);
956 if (!string_is_empty(path_local))
957 _stat(path_local, &buf);
962 path_wide = utf8_to_utf16_string_alloc(path);
963 file_info = GetFileAttributesW(path_wide);
965 _wstat(path_wide, &buf);
971 if (file_info == INVALID_FILE_ATTRIBUTES)
975 *size = (int32_t)buf.st_size;
977 is_dir = (file_info & FILE_ATTRIBUTE_DIRECTORY);
980 /* On GEKKO platforms, paths cannot have
981 * trailing slashes - we must therefore
983 char *path_buf = NULL;
985 struct stat stat_buf;
988 if (string_is_empty(path))
991 path_buf = strdup(path);
995 len = strlen(path_buf);
997 if (path_buf[len - 1] == '/')
998 path_buf[len - 1] = '\0';
1000 stat_ret = stat(path_buf, &stat_buf);
1007 *size = (int32_t)stat_buf.st_size;
1009 is_dir = S_ISDIR(stat_buf.st_mode);
1010 is_character_special = S_ISCHR(stat_buf.st_mode);
1013 /* Every other platform */
1016 if (!path || !*path)
1018 if (stat(path, &buf) < 0)
1022 *size = (int32_t)buf.st_size;
1024 is_dir = S_ISDIR(buf.st_mode);
1025 is_character_special = S_ISCHR(buf.st_mode);
1027 return RETRO_VFS_STAT_IS_VALID | (is_dir ? RETRO_VFS_STAT_IS_DIRECTORY : 0) | (is_character_special ? RETRO_VFS_STAT_IS_CHARACTER_SPECIAL : 0);
1031 #define path_mkdir_error(ret) (((ret) == SCE_ERROR_ERRNO_EEXIST))
1032 #elif defined(PSP) || defined(PS2) || defined(_3DS) || defined(WIIU) || defined(SWITCH) || defined(ORBIS)
1033 #define path_mkdir_error(ret) ((ret) == -1)
1035 #define path_mkdir_error(ret) ((ret) < 0 && errno == EEXIST)
1038 int retro_vfs_mkdir_impl(const char *dir)
1042 int ret = _mkdir(dir);
1044 wchar_t *dir_w = utf8_to_utf16_string_alloc(dir);
1049 ret = _wmkdir(dir_w);
1054 int ret = mkdir(dir, 0755);
1055 #elif defined(VITA) || defined(PSP)
1056 int ret = sceIoMkdir(dir, 0777);
1057 #elif defined(ORBIS)
1058 int ret = orbisMkdir(dir, 0755);
1059 #elif defined(__QNX__)
1060 int ret = mkdir(dir, 0777);
1061 #elif defined(GEKKO)
1062 /* On GEKKO platforms, mkdir() fails if
1063 * the path has a trailing slash. We must
1064 * therefore remove it. */
1066 if (!string_is_empty(dir))
1068 char *dir_buf = strdup(dir);
1072 size_t len = strlen(dir_buf);
1075 if (dir_buf[len - 1] == '/')
1076 dir_buf[len - 1] = '\0';
1078 ret = mkdir(dir_buf, 0750);
1084 int ret = mkdir(dir, 0750);
1087 if (path_mkdir_error(ret))
1089 return ret < 0 ? -1 : 0;
1093 struct retro_vfs_dir_handle
1095 struct libretro_vfs_implementation_dir
1100 #if defined(LEGACY_WIN32)
1101 WIN32_FIND_DATA entry;
1103 WIN32_FIND_DATAW entry;
1107 char path[PATH_MAX_LENGTH];
1108 #elif defined(VITA) || defined(PSP)
1111 #elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
1115 #elif defined(ORBIS)
1117 struct dirent entry;
1120 const struct dirent *entry;
1124 static bool dirent_check_error(libretro_vfs_implementation_dir *rdir)
1127 return (rdir->directory == INVALID_HANDLE_VALUE);
1128 #elif defined(VITA) || defined(PSP) || defined(ORBIS)
1129 return (rdir->directory < 0);
1130 #elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
1131 return (rdir->error != CELL_FS_SUCCEEDED);
1133 return !(rdir->directory);
1137 libretro_vfs_implementation_dir *retro_vfs_opendir_impl(
1138 const char *name, bool include_hidden)
1142 char path_buf[1024];
1144 #if defined(LEGACY_WIN32)
1145 char *path_local = NULL;
1147 wchar_t *path_wide = NULL;
1150 libretro_vfs_implementation_dir *rdir;
1152 /*Reject null or empty string paths*/
1153 if (!name || (*name == 0))
1156 /*Allocate RDIR struct. Tidied later with retro_closedir*/
1157 rdir = (libretro_vfs_implementation_dir*)calloc(1, sizeof(*rdir));
1161 rdir->orig_path = strdup(name);
1165 path_len = strlen(name);
1167 copied = strlcpy(path_buf, name, sizeof(path_buf));
1169 /* Non-NT platforms don't like extra slashes in the path */
1170 if (name[path_len - 1] != '\\')
1171 path_buf[copied++] = '\\';
1173 path_buf[copied] = '*';
1174 path_buf[copied+1] = '\0';
1176 #if defined(LEGACY_WIN32)
1177 path_local = utf8_to_local_string_alloc(path_buf);
1178 rdir->directory = FindFirstFile(path_local, &rdir->entry);
1183 path_wide = utf8_to_utf16_string_alloc(path_buf);
1184 rdir->directory = FindFirstFileW(path_wide, &rdir->entry);
1190 #elif defined(VITA) || defined(PSP)
1191 rdir->directory = sceIoDopen(name);
1193 rdir->directory = !string_is_empty(name) ? opendir(name) : NULL;
1195 #elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
1196 rdir->error = cellFsOpendir(name, &rdir->directory);
1197 #elif defined(ORBIS)
1198 rdir->directory = orbisDopen(name);
1200 rdir->directory = opendir(name);
1206 rdir->entry.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
1208 rdir->entry.dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
1211 if (rdir->directory && !dirent_check_error(rdir))
1214 retro_vfs_closedir_impl(rdir);
1218 bool retro_vfs_readdir_impl(libretro_vfs_implementation_dir *rdir)
1222 #if defined(LEGACY_WIN32)
1223 return (FindNextFile(rdir->directory, &rdir->entry) != 0);
1225 return (FindNextFileW(rdir->directory, &rdir->entry) != 0);
1229 return (rdir->directory != INVALID_HANDLE_VALUE);
1230 #elif defined(VITA) || defined(PSP)
1231 return (sceIoDread(rdir->directory, &rdir->entry) > 0);
1232 #elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
1234 rdir->error = cellFsReaddir(rdir->directory, &rdir->entry, &nread);
1235 return (nread != 0);
1236 #elif defined(ORBIS)
1237 return (orbisDread(rdir->directory, &rdir->entry) > 0);
1239 return ((rdir->entry = readdir(rdir->directory)) != NULL);
1243 const char *retro_vfs_dirent_get_name_impl(libretro_vfs_implementation_dir *rdir)
1246 #if defined(LEGACY_WIN32)
1247 char *name = local_to_utf8_string_alloc(rdir->entry.cFileName);
1249 char *name = utf16_to_utf8_string_alloc(rdir->entry.cFileName);
1251 memset(rdir->entry.cFileName, 0, sizeof(rdir->entry.cFileName));
1252 strlcpy((char*)rdir->entry.cFileName, name, sizeof(rdir->entry.cFileName));
1255 return (char*)rdir->entry.cFileName;
1256 #elif defined(VITA) || defined(PSP) || defined(__CELLOS_LV2__) && !defined(__PSL1GHT__) || defined(ORBIS)
1257 return rdir->entry.d_name;
1259 if (!rdir || !rdir->entry)
1261 return rdir->entry->d_name;
1265 bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *rdir)
1268 const WIN32_FIND_DATA *entry = (const WIN32_FIND_DATA*)&rdir->entry;
1269 return entry->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
1270 #elif defined(PSP) || defined(VITA)
1271 const SceIoDirent *entry = (const SceIoDirent*)&rdir->entry;
1273 return (entry->d_stat.st_attr & FIO_SO_IFDIR) == FIO_SO_IFDIR;
1275 return SCE_S_ISDIR(entry->d_stat.st_mode);
1277 #elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
1278 CellFsDirent *entry = (CellFsDirent*)&rdir->entry;
1279 return (entry->d_type == CELL_FS_TYPE_DIRECTORY);
1280 #elif defined(ORBIS)
1281 const struct dirent *entry = &rdir->entry;
1282 if (entry->d_type == DT_DIR)
1284 if (!(entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK))
1288 char path[PATH_MAX_LENGTH];
1290 const struct dirent *entry = (const struct dirent*)rdir->entry;
1291 if (entry->d_type == DT_DIR)
1293 /* This can happen on certain file systems. */
1294 if (!(entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK))
1297 /* dirent struct doesn't have d_type, do it the slow way ... */
1299 fill_pathname_join(path, rdir->orig_path, retro_vfs_dirent_get_name_impl(rdir), sizeof(path));
1300 if (stat(path, &buf) < 0)
1302 return S_ISDIR(buf.st_mode);
1306 int retro_vfs_closedir_impl(libretro_vfs_implementation_dir *rdir)
1312 if (rdir->directory != INVALID_HANDLE_VALUE)
1313 FindClose(rdir->directory);
1314 #elif defined(VITA) || defined(PSP)
1315 sceIoDclose(rdir->directory);
1316 #elif defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
1317 rdir->error = cellFsClosedir(rdir->directory);
1318 #elif defined(ORBIS)
1319 orbisDclose(rdir->directory);
1321 if (rdir->directory)
1322 closedir(rdir->directory);
1325 if (rdir->orig_path)
1326 free(rdir->orig_path);