Commit | Line | Data |
---|---|---|
3719602c PC |
1 | /* Copyright (C) 2010-2020 The RetroArch team |
2 | * | |
3 | * --------------------------------------------------------------------------------------- | |
4 | * The following license statement only applies to this file (vfs_implementation.c). | |
5 | * --------------------------------------------------------------------------------------- | |
6 | * | |
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: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
14 | * | |
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. | |
21 | */ | |
22 | ||
23 | #include <stdio.h> | |
24 | #include <stdlib.h> | |
25 | #include <string.h> | |
26 | #include <errno.h> | |
27 | #include <sys/types.h> | |
28 | ||
29 | #include <string/stdstring.h> /* string_is_empty */ | |
30 | ||
31 | #ifdef HAVE_CONFIG_H | |
32 | #include "config.h" | |
33 | #endif | |
34 | ||
35 | #if defined(_WIN32) | |
36 | # ifdef _MSC_VER | |
37 | # define setmode _setmode | |
38 | # endif | |
39 | #include <sys/stat.h> | |
40 | # ifdef _XBOX | |
41 | # include <xtl.h> | |
42 | # define INVALID_FILE_ATTRIBUTES -1 | |
43 | # else | |
44 | ||
45 | # include <fcntl.h> | |
46 | # include <direct.h> | |
47 | # include <windows.h> | |
48 | # endif | |
49 | # include <io.h> | |
50 | #else | |
51 | # if defined(PSP) | |
52 | # include <pspiofilemgr.h> | |
53 | # endif | |
54 | # include <sys/types.h> | |
55 | # include <sys/stat.h> | |
56 | # if !defined(VITA) | |
57 | # include <dirent.h> | |
58 | # endif | |
59 | # include <unistd.h> | |
60 | # if defined(WIIU) | |
61 | # include <malloc.h> | |
62 | # endif | |
63 | #endif | |
64 | ||
65 | #include <fcntl.h> | |
66 | ||
67 | /* TODO: Some things are duplicated but I'm really afraid of breaking other platforms by touching this */ | |
68 | #if defined(VITA) | |
69 | # include <psp2/io/fcntl.h> | |
70 | # include <psp2/io/dirent.h> | |
71 | # include <psp2/io/stat.h> | |
72 | #elif !defined(_WIN32) | |
73 | # if defined(PSP) | |
74 | # include <pspiofilemgr.h> | |
75 | # endif | |
76 | # include <sys/types.h> | |
77 | # include <sys/stat.h> | |
78 | # include <dirent.h> | |
79 | # include <unistd.h> | |
80 | #endif | |
81 | ||
82 | #if defined(__QNX__) || defined(PSP) | |
83 | #include <unistd.h> /* stat() is defined here */ | |
84 | #endif | |
85 | ||
86 | #ifdef __APPLE__ | |
87 | #include <CoreFoundation/CoreFoundation.h> | |
88 | #endif | |
89 | #ifdef __HAIKU__ | |
90 | #include <kernel/image.h> | |
91 | #endif | |
92 | #ifndef __MACH__ | |
93 | #include <compat/strl.h> | |
94 | #include <compat/posix_string.h> | |
95 | #endif | |
96 | #include <compat/strcasestr.h> | |
97 | #include <retro_miscellaneous.h> | |
98 | #include <encodings/utf.h> | |
99 | ||
100 | #if defined(_WIN32) | |
101 | #ifndef _XBOX | |
102 | #if defined(_MSC_VER) && _MSC_VER <= 1200 | |
103 | #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) | |
104 | #endif | |
105 | #endif | |
106 | #elif defined(VITA) | |
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> | |
111 | #else | |
112 | #include <sys/types.h> | |
113 | #include <sys/stat.h> | |
114 | #include <unistd.h> | |
115 | #endif | |
116 | ||
117 | ||
118 | #if defined(PSP) | |
119 | #include <pspkernel.h> | |
120 | #endif | |
121 | ||
122 | #if defined(__PS3__) || defined(__PSL1GHT__) | |
123 | #define FS_SUCCEEDED 0 | |
124 | #define FS_TYPE_DIR 1 | |
125 | #ifdef __PSL1GHT__ | |
126 | #include <lv2/sysfs.h> | |
127 | #ifndef O_RDONLY | |
128 | #define O_RDONLY SYS_O_RDONLY | |
129 | #endif | |
130 | #ifndef O_WRONLY | |
131 | #define O_WRONLY SYS_O_WRONLY | |
132 | #endif | |
133 | #ifndef O_CREAT | |
134 | #define O_CREAT SYS_O_CREAT | |
135 | #endif | |
136 | #ifndef O_TRUNC | |
137 | #define O_TRUNC SYS_O_TRUNC | |
138 | #endif | |
139 | #ifndef O_RDWR | |
140 | #define O_RDWR SYS_O_RDWR | |
141 | #endif | |
142 | #else | |
143 | #include <cell/cell_fs.h> | |
144 | #ifndef O_RDONLY | |
145 | #define O_RDONLY CELL_FS_O_RDONLY | |
146 | #endif | |
147 | #ifndef O_WRONLY | |
148 | #define O_WRONLY CELL_FS_O_WRONLY | |
149 | #endif | |
150 | #ifndef O_CREAT | |
151 | #define O_CREAT CELL_FS_O_CREAT | |
152 | #endif | |
153 | #ifndef O_TRUNC | |
154 | #define O_TRUNC CELL_FS_O_TRUNC | |
155 | #endif | |
156 | #ifndef O_RDWR | |
157 | #define O_RDWR CELL_FS_O_RDWR | |
158 | #endif | |
159 | #ifndef sysFsStat | |
160 | #define sysFsStat cellFsStat | |
161 | #endif | |
162 | #ifndef sysFSDirent | |
163 | #define sysFSDirent CellFsDirent | |
164 | #endif | |
165 | #ifndef sysFsOpendir | |
166 | #define sysFsOpendir cellFsOpendir | |
167 | #endif | |
168 | #ifndef sysFsReaddir | |
169 | #define sysFsReaddir cellFsReaddir | |
170 | #endif | |
171 | #ifndef sysFSDirent | |
172 | #define sysFSDirent CellFsDirent | |
173 | #endif | |
174 | #ifndef sysFsClosedir | |
175 | #define sysFsClosedir cellFsClosedir | |
176 | #endif | |
177 | #endif | |
178 | #endif | |
179 | ||
180 | #if defined(VITA) | |
181 | #define FIO_S_ISDIR SCE_S_ISDIR | |
182 | #endif | |
183 | ||
184 | #if defined(__QNX__) || defined(PSP) | |
185 | #include <unistd.h> /* stat() is defined here */ | |
186 | #endif | |
187 | ||
188 | /* Assume W-functions do not work below Win2K and Xbox platforms */ | |
189 | #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX) | |
190 | ||
191 | #ifndef LEGACY_WIN32 | |
192 | #define LEGACY_WIN32 | |
193 | #endif | |
194 | ||
195 | #endif | |
196 | ||
197 | #if defined(_WIN32) | |
198 | #if defined(_MSC_VER) && _MSC_VER >= 1400 | |
199 | #define ATLEAST_VC2005 | |
200 | #endif | |
201 | #endif | |
202 | ||
203 | #include <vfs/vfs_implementation.h> | |
204 | #include <libretro.h> | |
205 | #if defined(HAVE_MMAP) | |
206 | #include <memmap.h> | |
207 | #endif | |
208 | #include <encodings/utf.h> | |
209 | #include <compat/fopen_utf8.h> | |
210 | #include <file/file_path.h> | |
211 | ||
212 | #ifdef HAVE_CDROM | |
213 | #include <vfs/vfs_implementation_cdrom.h> | |
214 | #endif | |
215 | ||
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 | |
219 | #endif | |
220 | #endif | |
221 | ||
222 | #define RFILE_HINT_UNBUFFERED (1 << 8) | |
223 | ||
224 | int64_t retro_vfs_file_seek_internal( | |
225 | libretro_vfs_implementation_file *stream, | |
226 | int64_t offset, int whence) | |
227 | { | |
228 | if (!stream) | |
229 | return -1; | |
230 | ||
231 | if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) | |
232 | { | |
233 | #ifdef HAVE_CDROM | |
234 | if (stream->scheme == VFS_SCHEME_CDROM) | |
235 | return retro_vfs_file_seek_cdrom(stream, offset, whence); | |
236 | #endif | |
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); | |
242 | #else | |
243 | return fseek(stream->fp, (long)offset, whence); | |
244 | #endif | |
245 | } | |
246 | #ifdef HAVE_MMAP | |
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) | |
251 | { | |
252 | /* fseek() returns error on under/overflow but | |
253 | * allows cursor > EOF for | |
254 | read-only file descriptors. */ | |
255 | switch (whence) | |
256 | { | |
257 | case SEEK_SET: | |
258 | if (offset < 0) | |
259 | return -1; | |
260 | ||
261 | stream->mappos = offset; | |
262 | break; | |
263 | ||
264 | case SEEK_CUR: | |
265 | if ( (offset < 0 && stream->mappos + offset > stream->mappos) || | |
266 | (offset > 0 && stream->mappos + offset < stream->mappos)) | |
267 | return -1; | |
268 | ||
269 | stream->mappos += offset; | |
270 | break; | |
271 | ||
272 | case SEEK_END: | |
273 | if (stream->mapsize + offset < stream->mapsize) | |
274 | return -1; | |
275 | ||
276 | stream->mappos = stream->mapsize + offset; | |
277 | break; | |
278 | } | |
279 | return stream->mappos; | |
280 | } | |
281 | #endif | |
282 | ||
283 | if (lseek(stream->fd, (off_t)offset, whence) < 0) | |
284 | return -1; | |
285 | ||
286 | return 0; | |
287 | } | |
288 | ||
289 | /** | |
290 | * retro_vfs_file_open_impl: | |
291 | * @path : path to file | |
292 | * @mode : file mode to use when opening (read/write) | |
293 | * @hints : | |
294 | * | |
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. | |
297 | **/ | |
298 | ||
299 | libretro_vfs_implementation_file *retro_vfs_file_open_impl( | |
300 | const char *path, unsigned mode, unsigned hints) | |
301 | { | |
302 | int flags = 0; | |
303 | const char *mode_str = NULL; | |
304 | libretro_vfs_implementation_file *stream = | |
305 | (libretro_vfs_implementation_file*) | |
306 | malloc(sizeof(*stream)); | |
307 | ||
308 | if (!stream) | |
309 | return NULL; | |
310 | ||
311 | stream->fd = 0; | |
312 | stream->hints = hints; | |
313 | stream->size = 0; | |
314 | stream->buf = NULL; | |
315 | stream->fp = NULL; | |
316 | #ifdef _WIN32 | |
317 | stream->fh = 0; | |
318 | #endif | |
319 | stream->orig_path = NULL; | |
320 | stream->mappos = 0; | |
321 | stream->mapsize = 0; | |
322 | stream->mapped = NULL; | |
323 | stream->scheme = VFS_SCHEME_NONE; | |
324 | ||
325 | #ifdef VFS_FRONTEND | |
326 | if ( path | |
327 | && path[0] == 'v' | |
328 | && path[1] == 'f' | |
329 | && path[2] == 's' | |
330 | && path[3] == 'o' | |
331 | && path[4] == 'n' | |
332 | && path[5] == 'l' | |
333 | && path[6] == 'y' | |
334 | && path[7] == ':' | |
335 | && path[8] == '/' | |
336 | && path[9] == '/') | |
337 | path += sizeof("vfsonly://")-1; | |
338 | #endif | |
339 | ||
340 | #ifdef HAVE_CDROM | |
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; | |
353 | ||
354 | if ( path | |
355 | && path[0] == 'c' | |
356 | && path[1] == 'd' | |
357 | && path[2] == 'r' | |
358 | && path[3] == 'o' | |
359 | && path[4] == 'm' | |
360 | && path[5] == ':' | |
361 | && path[6] == '/' | |
362 | && path[7] == '/' | |
363 | && path[8] != '\0') | |
364 | { | |
365 | path += sizeof("cdrom://")-1; | |
366 | stream->scheme = VFS_SCHEME_CDROM; | |
367 | } | |
368 | #endif | |
369 | ||
370 | stream->orig_path = strdup(path); | |
371 | ||
372 | #ifdef HAVE_MMAP | |
373 | if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS && mode == RETRO_VFS_FILE_ACCESS_READ) | |
374 | stream->hints |= RFILE_HINT_UNBUFFERED; | |
375 | else | |
376 | #endif | |
377 | stream->hints &= ~RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS; | |
378 | ||
379 | switch (mode) | |
380 | { | |
381 | case RETRO_VFS_FILE_ACCESS_READ: | |
382 | mode_str = "rb"; | |
383 | ||
384 | flags = O_RDONLY; | |
385 | #ifdef _WIN32 | |
386 | flags |= O_BINARY; | |
387 | #endif | |
388 | break; | |
389 | ||
390 | case RETRO_VFS_FILE_ACCESS_WRITE: | |
391 | mode_str = "wb"; | |
392 | ||
393 | flags = O_WRONLY | O_CREAT | O_TRUNC; | |
394 | #if !defined(_WIN32) | |
395 | flags |= S_IRUSR | S_IWUSR; | |
396 | #else | |
397 | flags |= O_BINARY; | |
398 | #endif | |
399 | break; | |
400 | ||
401 | case RETRO_VFS_FILE_ACCESS_READ_WRITE: | |
402 | mode_str = "w+b"; | |
403 | flags = O_RDWR | O_CREAT | O_TRUNC; | |
404 | #if !defined(_WIN32) | |
405 | flags |= S_IRUSR | S_IWUSR; | |
406 | #else | |
407 | flags |= O_BINARY; | |
408 | #endif | |
409 | break; | |
410 | ||
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: | |
413 | mode_str = "r+b"; | |
414 | ||
415 | flags = O_RDWR; | |
416 | #if !defined(_WIN32) | |
417 | flags |= S_IRUSR | S_IWUSR; | |
418 | #else | |
419 | flags |= O_BINARY; | |
420 | #endif | |
421 | break; | |
422 | ||
423 | default: | |
424 | goto error; | |
425 | } | |
426 | ||
427 | if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) | |
428 | { | |
429 | FILE *fp; | |
430 | #ifdef HAVE_CDROM | |
431 | if (stream->scheme == VFS_SCHEME_CDROM) | |
432 | { | |
433 | retro_vfs_file_open_cdrom(stream, path, mode, hints); | |
434 | #if defined(_WIN32) && !defined(_XBOX) | |
435 | if (!stream->fh) | |
436 | goto error; | |
437 | #else | |
438 | if (!stream->fp) | |
439 | goto error; | |
440 | #endif | |
441 | } | |
442 | else | |
443 | #endif | |
444 | { | |
445 | if (!(fp = (FILE*)fopen_utf8(path, mode_str))) | |
446 | goto error; | |
447 | ||
448 | stream->fp = fp; | |
449 | } | |
450 | ||
451 | /* Regarding setvbuf: | |
452 | * | |
453 | * https://www.freebsd.org/cgi/man.cgi?query=setvbuf&apropos=0&sektion=0&manpath=FreeBSD+11.1-RELEASE&arch=default&format=html | |
454 | * | |
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. | |
458 | * | |
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. | |
461 | */ | |
462 | /* TODO: this is only useful for a few platforms, | |
463 | * find which and add ifdef */ | |
464 | #if defined(_3DS) | |
465 | if (stream->scheme != VFS_SCHEME_CDROM) | |
466 | { | |
467 | stream->buf = (char*)calloc(1, 0x10000); | |
468 | if (stream->fp) | |
469 | setvbuf(stream->fp, stream->buf, _IOFBF, 0x10000); | |
470 | } | |
471 | #elif defined(WIIU) | |
472 | if (stream->scheme != VFS_SCHEME_CDROM) | |
473 | { | |
474 | const int bufsize = 128 * 1024; | |
475 | stream->buf = (char*)memalign(0x40, bufsize); | |
476 | if (stream->fp) | |
477 | setvbuf(stream->fp, stream->buf, _IOFBF, bufsize); | |
478 | stream->buf = (char*)calloc(1, 0x4000); | |
479 | if (stream->fp) | |
480 | setvbuf(stream->fp, stream->buf, _IOFBF, 0x4000); | |
481 | } | |
482 | #endif | |
483 | } | |
484 | else | |
485 | { | |
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); | |
490 | if (path_local) | |
491 | free(path_local); | |
492 | #else | |
493 | wchar_t * path_wide = utf8_to_utf16_string_alloc(path); | |
494 | stream->fd = _wopen(path_wide, flags, 0); | |
495 | if (path_wide) | |
496 | free(path_wide); | |
497 | #endif | |
498 | #else | |
499 | stream->fd = open(path, flags, 0); | |
500 | #endif | |
501 | ||
502 | if (stream->fd == -1) | |
503 | goto error; | |
504 | ||
505 | #ifdef HAVE_MMAP | |
506 | if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS) | |
507 | { | |
508 | stream->mappos = 0; | |
509 | stream->mapped = NULL; | |
510 | stream->mapsize = retro_vfs_file_seek_internal(stream, 0, SEEK_END); | |
511 | ||
512 | if (stream->mapsize == (uint64_t)-1) | |
513 | goto error; | |
514 | ||
515 | retro_vfs_file_seek_internal(stream, 0, SEEK_SET); | |
516 | ||
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; | |
520 | } | |
521 | #endif | |
522 | } | |
523 | #ifdef HAVE_CDROM | |
524 | if (stream->scheme == VFS_SCHEME_CDROM) | |
525 | { | |
526 | retro_vfs_file_seek_cdrom(stream, 0, SEEK_SET); | |
527 | retro_vfs_file_seek_cdrom(stream, 0, SEEK_END); | |
528 | ||
529 | stream->size = retro_vfs_file_tell_impl(stream); | |
530 | ||
531 | retro_vfs_file_seek_cdrom(stream, 0, SEEK_SET); | |
532 | } | |
533 | else | |
534 | #endif | |
535 | { | |
536 | retro_vfs_file_seek_internal(stream, 0, SEEK_SET); | |
537 | retro_vfs_file_seek_internal(stream, 0, SEEK_END); | |
538 | ||
539 | stream->size = retro_vfs_file_tell_impl(stream); | |
540 | ||
541 | retro_vfs_file_seek_internal(stream, 0, SEEK_SET); | |
542 | } | |
543 | return stream; | |
544 | ||
545 | error: | |
546 | retro_vfs_file_close_impl(stream); | |
547 | return NULL; | |
548 | } | |
549 | ||
550 | int retro_vfs_file_close_impl(libretro_vfs_implementation_file *stream) | |
551 | { | |
552 | if (!stream) | |
553 | return -1; | |
554 | ||
555 | #ifdef HAVE_CDROM | |
556 | if (stream->scheme == VFS_SCHEME_CDROM) | |
557 | { | |
558 | retro_vfs_file_close_cdrom(stream); | |
559 | goto end; | |
560 | } | |
561 | #endif | |
562 | ||
563 | if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) | |
564 | { | |
565 | if (stream->fp) | |
566 | fclose(stream->fp); | |
567 | } | |
568 | else | |
569 | { | |
570 | #ifdef HAVE_MMAP | |
571 | if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS) | |
572 | munmap(stream->mapped, stream->mapsize); | |
573 | #endif | |
574 | } | |
575 | ||
576 | if (stream->fd > 0) | |
577 | close(stream->fd); | |
578 | #ifdef HAVE_CDROM | |
579 | end: | |
580 | if (stream->cdrom.cue_buf) | |
581 | free(stream->cdrom.cue_buf); | |
582 | #endif | |
583 | if (stream->buf) | |
584 | free(stream->buf); | |
585 | ||
586 | if (stream->orig_path) | |
587 | free(stream->orig_path); | |
588 | ||
589 | free(stream); | |
590 | ||
591 | return 0; | |
592 | } | |
593 | ||
594 | int retro_vfs_file_error_impl(libretro_vfs_implementation_file *stream) | |
595 | { | |
596 | #ifdef HAVE_CDROM | |
597 | if (stream->scheme == VFS_SCHEME_CDROM) | |
598 | return retro_vfs_file_error_cdrom(stream); | |
599 | #endif | |
600 | return ferror(stream->fp); | |
601 | } | |
602 | ||
603 | int64_t retro_vfs_file_size_impl(libretro_vfs_implementation_file *stream) | |
604 | { | |
605 | if (stream) | |
606 | return stream->size; | |
607 | return 0; | |
608 | } | |
609 | ||
610 | int64_t retro_vfs_file_truncate_impl(libretro_vfs_implementation_file *stream, int64_t length) | |
611 | { | |
612 | #ifdef _WIN32 | |
613 | if (stream && _chsize(_fileno(stream->fp), length) == 0) | |
614 | { | |
615 | stream->size = length; | |
616 | return 0; | |
617 | } | |
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) | |
620 | { | |
621 | stream->size = length; | |
622 | return 0; | |
623 | } | |
624 | #endif | |
625 | return -1; | |
626 | } | |
627 | ||
628 | int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream) | |
629 | { | |
630 | if (!stream) | |
631 | return -1; | |
632 | ||
633 | if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) | |
634 | { | |
635 | #ifdef HAVE_CDROM | |
636 | if (stream->scheme == VFS_SCHEME_CDROM) | |
637 | return retro_vfs_file_tell_cdrom(stream); | |
638 | #endif | |
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); | |
644 | #else | |
645 | return ftell(stream->fp); | |
646 | #endif | |
647 | } | |
648 | #ifdef HAVE_MMAP | |
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; | |
654 | #endif | |
655 | if (lseek(stream->fd, 0, SEEK_CUR) < 0) | |
656 | return -1; | |
657 | ||
658 | return 0; | |
659 | } | |
660 | ||
661 | int64_t retro_vfs_file_seek_impl(libretro_vfs_implementation_file *stream, | |
662 | int64_t offset, int seek_position) | |
663 | { | |
664 | return retro_vfs_file_seek_internal(stream, offset, seek_position); | |
665 | } | |
666 | ||
667 | int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream, | |
668 | void *s, uint64_t len) | |
669 | { | |
670 | if (!stream || !s) | |
671 | return -1; | |
672 | ||
673 | if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) | |
674 | { | |
675 | #ifdef HAVE_CDROM | |
676 | if (stream->scheme == VFS_SCHEME_CDROM) | |
677 | return retro_vfs_file_read_cdrom(stream, s, len); | |
678 | #endif | |
679 | return fread(s, 1, (size_t)len, stream->fp); | |
680 | } | |
681 | #ifdef HAVE_MMAP | |
682 | if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS) | |
683 | { | |
684 | if (stream->mappos > stream->mapsize) | |
685 | return -1; | |
686 | ||
687 | if (stream->mappos + len > stream->mapsize) | |
688 | len = stream->mapsize - stream->mappos; | |
689 | ||
690 | memcpy(s, &stream->mapped[stream->mappos], len); | |
691 | stream->mappos += len; | |
692 | ||
693 | return len; | |
694 | } | |
695 | #endif | |
696 | ||
697 | return read(stream->fd, s, (size_t)len); | |
698 | } | |
699 | ||
700 | int64_t retro_vfs_file_write_impl(libretro_vfs_implementation_file *stream, const void *s, uint64_t len) | |
701 | { | |
702 | int64_t pos = 0; | |
703 | ssize_t result = -1; | |
704 | ||
705 | if (!stream) | |
706 | return -1; | |
707 | ||
708 | if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) | |
709 | { | |
710 | pos = retro_vfs_file_tell_impl(stream); | |
711 | result = fwrite(s, 1, (size_t)len, stream->fp); | |
712 | ||
713 | if (result != -1 && pos + result > stream->size) | |
714 | stream->size = pos + result; | |
715 | ||
716 | return result; | |
717 | } | |
718 | #ifdef HAVE_MMAP | |
719 | if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS) | |
720 | return -1; | |
721 | #endif | |
722 | ||
723 | pos = retro_vfs_file_tell_impl(stream); | |
724 | result = write(stream->fd, s, (size_t)len); | |
725 | ||
726 | if (result != -1 && pos + result > stream->size) | |
727 | stream->size = pos + result; | |
728 | ||
729 | return result; | |
730 | } | |
731 | ||
732 | int retro_vfs_file_flush_impl(libretro_vfs_implementation_file *stream) | |
733 | { | |
734 | if (stream && fflush(stream->fp) == 0) | |
735 | return 0; | |
736 | return -1; | |
737 | } | |
738 | ||
739 | int retro_vfs_file_remove_impl(const char *path) | |
740 | { | |
741 | #if defined(_WIN32) && !defined(_XBOX) | |
742 | /* Win32 (no Xbox) */ | |
743 | ||
744 | #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 | |
745 | char *path_local = NULL; | |
746 | #else | |
747 | wchar_t *path_wide = NULL; | |
748 | #endif | |
749 | if (!path || !*path) | |
750 | return -1; | |
751 | #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 | |
752 | if ((path_local = utf8_to_local_string_alloc(path))) | |
753 | { | |
754 | int ret = remove(path_local); | |
755 | free(path_local); | |
756 | ||
757 | if (ret == 0) | |
758 | return 0; | |
759 | } | |
760 | #else | |
761 | if ((path_wide = utf8_to_utf16_string_alloc(path))) | |
762 | { | |
763 | int ret = _wremove(path_wide); | |
764 | free(path_wide); | |
765 | ||
766 | if (ret == 0) | |
767 | return 0; | |
768 | } | |
769 | #endif | |
770 | #else | |
771 | if (remove(path) == 0) | |
772 | return 0; | |
773 | #endif | |
774 | return -1; | |
775 | } | |
776 | ||
777 | int retro_vfs_file_rename_impl(const char *old_path, const char *new_path) | |
778 | { | |
779 | #if defined(_WIN32) && !defined(_XBOX) | |
780 | /* Win32 (no Xbox) */ | |
781 | int ret = -1; | |
782 | #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 | |
783 | char *old_path_local = NULL; | |
784 | #else | |
785 | wchar_t *old_path_wide = NULL; | |
786 | #endif | |
787 | ||
788 | if (!old_path || !*old_path || !new_path || !*new_path) | |
789 | return -1; | |
790 | ||
791 | #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 | |
792 | old_path_local = utf8_to_local_string_alloc(old_path); | |
793 | ||
794 | if (old_path_local) | |
795 | { | |
796 | char *new_path_local = utf8_to_local_string_alloc(new_path); | |
797 | ||
798 | if (new_path_local) | |
799 | { | |
800 | if (rename(old_path_local, new_path_local) == 0) | |
801 | ret = 0; | |
802 | free(new_path_local); | |
803 | } | |
804 | ||
805 | free(old_path_local); | |
806 | } | |
807 | #else | |
808 | old_path_wide = utf8_to_utf16_string_alloc(old_path); | |
809 | ||
810 | if (old_path_wide) | |
811 | { | |
812 | wchar_t *new_path_wide = utf8_to_utf16_string_alloc(new_path); | |
813 | ||
814 | if (new_path_wide) | |
815 | { | |
816 | if (_wrename(old_path_wide, new_path_wide) == 0) | |
817 | ret = 0; | |
818 | free(new_path_wide); | |
819 | } | |
820 | ||
821 | free(old_path_wide); | |
822 | } | |
823 | #endif | |
824 | return ret; | |
825 | ||
826 | #else | |
827 | /* Every other platform */ | |
828 | if (!old_path || !*old_path || !new_path || !*new_path) | |
829 | return -1; | |
830 | return rename(old_path, new_path) == 0 ? 0 : -1; | |
831 | #endif | |
832 | } | |
833 | ||
834 | const char *retro_vfs_file_get_path_impl( | |
835 | libretro_vfs_implementation_file *stream) | |
836 | { | |
837 | /* should never happen, do something noisy so caller can be fixed */ | |
838 | if (!stream) | |
839 | abort(); | |
840 | return stream->orig_path; | |
841 | } | |
842 | ||
843 | int retro_vfs_stat_impl(const char *path, int32_t *size) | |
844 | { | |
845 | bool is_dir = false; | |
846 | bool is_character_special = false; | |
847 | #if defined(VITA) | |
848 | /* Vita / PSP */ | |
849 | SceIoStat buf; | |
850 | int dir_ret; | |
851 | char *tmp = NULL; | |
852 | size_t len = 0; | |
853 | ||
854 | if (!path || !*path) | |
855 | return 0; | |
856 | ||
857 | tmp = strdup(path); | |
858 | len = strlen(tmp); | |
859 | if (tmp[len-1] == '/') | |
860 | tmp[len-1] = '\0'; | |
861 | ||
862 | dir_ret = sceIoGetstat(tmp, &buf); | |
863 | free(tmp); | |
864 | if (dir_ret < 0) | |
865 | return 0; | |
866 | ||
867 | if (size) | |
868 | *size = (int32_t)buf.st_size; | |
869 | ||
870 | is_dir = FIO_S_ISDIR(buf.st_mode); | |
871 | #elif defined(__PSL1GHT__) || defined(__PS3__) | |
872 | /* Lowlevel Lv2 */ | |
873 | sysFSStat buf; | |
874 | ||
875 | if (!path || !*path) | |
876 | return 0; | |
877 | if (sysFsStat(path, &buf) < 0) | |
878 | return 0; | |
879 | ||
880 | if (size) | |
881 | *size = (int32_t)buf.st_size; | |
882 | ||
883 | is_dir = ((buf.st_mode & S_IFMT) == S_IFDIR); | |
884 | #elif defined(_WIN32) | |
885 | /* Windows */ | |
886 | DWORD file_info; | |
887 | struct _stat buf; | |
888 | #if defined(LEGACY_WIN32) | |
889 | char *path_local = NULL; | |
890 | #else | |
891 | wchar_t *path_wide = NULL; | |
892 | #endif | |
893 | ||
894 | if (!path || !*path) | |
895 | return 0; | |
896 | ||
897 | #if defined(LEGACY_WIN32) | |
898 | path_local = utf8_to_local_string_alloc(path); | |
899 | file_info = GetFileAttributes(path_local); | |
900 | ||
901 | if (!string_is_empty(path_local)) | |
902 | _stat(path_local, &buf); | |
903 | ||
904 | if (path_local) | |
905 | free(path_local); | |
906 | #else | |
907 | path_wide = utf8_to_utf16_string_alloc(path); | |
908 | file_info = GetFileAttributesW(path_wide); | |
909 | ||
910 | _wstat(path_wide, &buf); | |
911 | ||
912 | if (path_wide) | |
913 | free(path_wide); | |
914 | #endif | |
915 | ||
916 | if (file_info == INVALID_FILE_ATTRIBUTES) | |
917 | return 0; | |
918 | ||
919 | if (size) | |
920 | *size = (int32_t)buf.st_size; | |
921 | ||
922 | is_dir = (file_info & FILE_ATTRIBUTE_DIRECTORY); | |
923 | ||
924 | #elif defined(GEKKO) | |
925 | /* On GEKKO platforms, paths cannot have | |
926 | * trailing slashes - we must therefore | |
927 | * remove them */ | |
928 | char *path_buf = NULL; | |
929 | int stat_ret = -1; | |
930 | struct stat stat_buf; | |
931 | size_t len; | |
932 | ||
933 | if (string_is_empty(path)) | |
934 | return 0; | |
935 | ||
936 | if (!(path_buf = strdup(path))) | |
937 | return 0; | |
938 | ||
939 | if ((len = strlen(path_buf)) > 0) | |
940 | if (path_buf[len - 1] == '/') | |
941 | path_buf[len - 1] = '\0'; | |
942 | ||
943 | stat_ret = stat(path_buf, &stat_buf); | |
944 | free(path_buf); | |
945 | ||
946 | if (stat_ret < 0) | |
947 | return 0; | |
948 | ||
949 | if (size) | |
950 | *size = (int32_t)stat_buf.st_size; | |
951 | ||
952 | is_dir = S_ISDIR(stat_buf.st_mode); | |
953 | is_character_special = S_ISCHR(stat_buf.st_mode); | |
954 | ||
955 | #else | |
956 | /* Every other platform */ | |
957 | struct stat buf; | |
958 | ||
959 | if (!path || !*path) | |
960 | return 0; | |
961 | if (stat(path, &buf) < 0) | |
962 | return 0; | |
963 | ||
964 | if (size) | |
965 | *size = (int32_t)buf.st_size; | |
966 | ||
967 | is_dir = S_ISDIR(buf.st_mode); | |
968 | is_character_special = S_ISCHR(buf.st_mode); | |
969 | #endif | |
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); | |
971 | } | |
972 | ||
973 | #if defined(VITA) | |
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) | |
977 | #else | |
978 | #define path_mkdir_error(ret) ((ret) < 0 && errno == EEXIST) | |
979 | #endif | |
980 | ||
981 | int retro_vfs_mkdir_impl(const char *dir) | |
982 | { | |
983 | #if defined(_WIN32) | |
984 | #ifdef LEGACY_WIN32 | |
985 | int ret = _mkdir(dir); | |
986 | #else | |
987 | wchar_t *dir_w = utf8_to_utf16_string_alloc(dir); | |
988 | int ret = -1; | |
989 | ||
990 | if (dir_w) | |
991 | { | |
992 | ret = _wmkdir(dir_w); | |
993 | free(dir_w); | |
994 | } | |
995 | #endif | |
996 | #elif defined(IOS) | |
997 | int ret = mkdir(dir, 0755); | |
998 | #elif defined(VITA) | |
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. */ | |
1006 | int ret = -1; | |
1007 | if (!string_is_empty(dir)) | |
1008 | { | |
1009 | char *dir_buf = strdup(dir); | |
1010 | ||
1011 | if (dir_buf) | |
1012 | { | |
1013 | size_t len = strlen(dir_buf); | |
1014 | ||
1015 | if (len > 0) | |
1016 | if (dir_buf[len - 1] == '/') | |
1017 | dir_buf[len - 1] = '\0'; | |
1018 | ||
1019 | ret = mkdir(dir_buf, 0750); | |
1020 | ||
1021 | free(dir_buf); | |
1022 | } | |
1023 | } | |
1024 | #else | |
1025 | int ret = mkdir(dir, 0750); | |
1026 | #endif | |
1027 | ||
1028 | if (path_mkdir_error(ret)) | |
1029 | return -2; | |
1030 | return ret < 0 ? -1 : 0; | |
1031 | } | |
1032 | ||
1033 | #ifdef VFS_FRONTEND | |
1034 | struct retro_vfs_dir_handle | |
1035 | #else | |
1036 | struct libretro_vfs_implementation_dir | |
1037 | #endif | |
1038 | { | |
1039 | char* orig_path; | |
1040 | #if defined(_WIN32) | |
1041 | #if defined(LEGACY_WIN32) | |
1042 | WIN32_FIND_DATA entry; | |
1043 | #else | |
1044 | WIN32_FIND_DATAW entry; | |
1045 | #endif | |
1046 | HANDLE directory; | |
1047 | bool next; | |
1048 | char path[PATH_MAX_LENGTH]; | |
1049 | #elif defined(VITA) | |
1050 | SceUID directory; | |
1051 | SceIoDirent entry; | |
1052 | #elif defined(__PSL1GHT__) || defined(__PS3__) | |
1053 | int error; | |
1054 | int directory; | |
1055 | sysFSDirent entry; | |
1056 | #else | |
1057 | DIR *directory; | |
1058 | const struct dirent *entry; | |
1059 | #endif | |
1060 | }; | |
1061 | ||
1062 | static bool dirent_check_error(libretro_vfs_implementation_dir *rdir) | |
1063 | { | |
1064 | #if defined(_WIN32) | |
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); | |
1070 | #else | |
1071 | return !(rdir->directory); | |
1072 | #endif | |
1073 | } | |
1074 | ||
1075 | libretro_vfs_implementation_dir *retro_vfs_opendir_impl( | |
1076 | const char *name, bool include_hidden) | |
1077 | { | |
1078 | #if defined(_WIN32) | |
1079 | char path_buf[1024]; | |
1080 | size_t copied = 0; | |
1081 | #if defined(LEGACY_WIN32) | |
1082 | char *path_local = NULL; | |
1083 | #else | |
1084 | wchar_t *path_wide = NULL; | |
1085 | #endif | |
1086 | #endif | |
1087 | libretro_vfs_implementation_dir *rdir; | |
1088 | ||
1089 | /* Reject NULL or empty string paths*/ | |
1090 | if (!name || (*name == 0)) | |
1091 | return NULL; | |
1092 | ||
1093 | /*Allocate RDIR struct. Tidied later with retro_closedir*/ | |
1094 | if (!(rdir = (libretro_vfs_implementation_dir*) | |
1095 | calloc(1, sizeof(*rdir)))) | |
1096 | return NULL; | |
1097 | ||
1098 | rdir->orig_path = strdup(name); | |
1099 | ||
1100 | #if defined(_WIN32) | |
1101 | copied = strlcpy(path_buf, name, sizeof(path_buf)); | |
1102 | ||
1103 | /* Non-NT platforms don't like extra slashes in the path */ | |
1104 | if (path_buf[copied - 1] != '\\') | |
1105 | path_buf [copied++] = '\\'; | |
1106 | ||
1107 | path_buf[copied ] = '*'; | |
1108 | path_buf[copied+1] = '\0'; | |
1109 | ||
1110 | #if defined(LEGACY_WIN32) | |
1111 | path_local = utf8_to_local_string_alloc(path_buf); | |
1112 | rdir->directory = FindFirstFile(path_local, &rdir->entry); | |
1113 | ||
1114 | if (path_local) | |
1115 | free(path_local); | |
1116 | #else | |
1117 | path_wide = utf8_to_utf16_string_alloc(path_buf); | |
1118 | rdir->directory = FindFirstFileW(path_wide, &rdir->entry); | |
1119 | ||
1120 | if (path_wide) | |
1121 | free(path_wide); | |
1122 | #endif | |
1123 | ||
1124 | #elif defined(VITA) | |
1125 | rdir->directory = sceIoDopen(name); | |
1126 | #elif defined(_3DS) | |
1127 | rdir->directory = !string_is_empty(name) ? opendir(name) : NULL; | |
1128 | rdir->entry = NULL; | |
1129 | #elif defined(__PSL1GHT__) || defined(__PS3__) | |
1130 | rdir->error = sysFsOpendir(name, &rdir->directory); | |
1131 | #else | |
1132 | rdir->directory = opendir(name); | |
1133 | rdir->entry = NULL; | |
1134 | #endif | |
1135 | ||
1136 | #ifdef _WIN32 | |
1137 | if (include_hidden) | |
1138 | rdir->entry.dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN; | |
1139 | else | |
1140 | rdir->entry.dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN; | |
1141 | #endif | |
1142 | ||
1143 | if (rdir->directory && !dirent_check_error(rdir)) | |
1144 | return rdir; | |
1145 | ||
1146 | retro_vfs_closedir_impl(rdir); | |
1147 | return NULL; | |
1148 | } | |
1149 | ||
1150 | bool retro_vfs_readdir_impl(libretro_vfs_implementation_dir *rdir) | |
1151 | { | |
1152 | #if defined(_WIN32) | |
1153 | if (rdir->next) | |
1154 | #if defined(LEGACY_WIN32) | |
1155 | return (FindNextFile(rdir->directory, &rdir->entry) != 0); | |
1156 | #else | |
1157 | return (FindNextFileW(rdir->directory, &rdir->entry) != 0); | |
1158 | #endif | |
1159 | ||
1160 | rdir->next = true; | |
1161 | return (rdir->directory != INVALID_HANDLE_VALUE); | |
1162 | #elif defined(VITA) | |
1163 | return (sceIoDread(rdir->directory, &rdir->entry) > 0); | |
1164 | #elif defined(__PSL1GHT__) || defined(__PS3__) | |
1165 | uint64_t nread; | |
1166 | rdir->error = sysFsReaddir(rdir->directory, &rdir->entry, &nread); | |
1167 | return (nread != 0); | |
1168 | #else | |
1169 | return ((rdir->entry = readdir(rdir->directory)) != NULL); | |
1170 | #endif | |
1171 | } | |
1172 | ||
1173 | const char *retro_vfs_dirent_get_name_impl(libretro_vfs_implementation_dir *rdir) | |
1174 | { | |
1175 | #if defined(_WIN32) | |
1176 | #if defined(LEGACY_WIN32) | |
1177 | char *name = local_to_utf8_string_alloc(rdir->entry.cFileName); | |
1178 | #else | |
1179 | char *name = utf16_to_utf8_string_alloc(rdir->entry.cFileName); | |
1180 | #endif | |
1181 | memset(rdir->entry.cFileName, 0, sizeof(rdir->entry.cFileName)); | |
1182 | strlcpy((char*)rdir->entry.cFileName, name, sizeof(rdir->entry.cFileName)); | |
1183 | if (name) | |
1184 | free(name); | |
1185 | return (char*)rdir->entry.cFileName; | |
1186 | #elif defined(VITA) || defined(__PSL1GHT__) || defined(__PS3__) | |
1187 | return rdir->entry.d_name; | |
1188 | #else | |
1189 | if (!rdir || !rdir->entry) | |
1190 | return NULL; | |
1191 | return rdir->entry->d_name; | |
1192 | #endif | |
1193 | } | |
1194 | ||
1195 | bool retro_vfs_dirent_is_dir_impl(libretro_vfs_implementation_dir *rdir) | |
1196 | { | |
1197 | #if defined(_WIN32) | |
1198 | const WIN32_FIND_DATA *entry = (const WIN32_FIND_DATA*)&rdir->entry; | |
1199 | return entry->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; | |
1200 | #elif defined(VITA) | |
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); | |
1206 | #else | |
1207 | struct stat buf; | |
1208 | char path[PATH_MAX_LENGTH]; | |
1209 | #if defined(DT_DIR) | |
1210 | const struct dirent *entry = (const struct dirent*)rdir->entry; | |
1211 | if (entry->d_type == DT_DIR) | |
1212 | return true; | |
1213 | /* This can happen on certain file systems. */ | |
1214 | if (!(entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK)) | |
1215 | return false; | |
1216 | #endif | |
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) | |
1220 | return false; | |
1221 | return S_ISDIR(buf.st_mode); | |
1222 | #endif | |
1223 | } | |
1224 | ||
1225 | int retro_vfs_closedir_impl(libretro_vfs_implementation_dir *rdir) | |
1226 | { | |
1227 | if (!rdir) | |
1228 | return -1; | |
1229 | ||
1230 | #if defined(_WIN32) | |
1231 | if (rdir->directory != INVALID_HANDLE_VALUE) | |
1232 | FindClose(rdir->directory); | |
1233 | #elif defined(VITA) | |
1234 | sceIoDclose(rdir->directory); | |
1235 | #elif defined(__PSL1GHT__) || defined(__PS3__) | |
1236 | rdir->error = sysFsClosedir(rdir->directory); | |
1237 | #else | |
1238 | if (rdir->directory) | |
1239 | closedir(rdir->directory); | |
1240 | #endif | |
1241 | ||
1242 | if (rdir->orig_path) | |
1243 | free(rdir->orig_path); | |
1244 | free(rdir); | |
1245 | return 0; | |
1246 | } |