vfs_implementation.c 33 KB

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