physfs_platform_libretro.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. /*
  2. * libretro support routines for PhysicsFS.
  3. *
  4. * Please see the file LICENSE.txt in the source's root directory.
  5. *
  6. * This file written by Vladimir Serbinenko (@phcoder), and Rob Loach (@RobLoach).
  7. */
  8. #define __PHYSICSFS_INTERNAL__
  9. #include "physfs_platforms.h"
  10. #ifdef PHYSFS_PLATFORM_LIBRETRO
  11. #include <string.h>
  12. #include <errno.h>
  13. /* libretro dependencies */
  14. #include <libretro.h>
  15. /**
  16. * Disable threading support by defining PHYSFS_PLATFORM_LIBRETRO_NO_THREADS.
  17. */
  18. #ifndef PHYSFS_PLATFORM_LIBRETRO_NO_THREADS
  19. #include <rthreads/rthreads.h>
  20. #endif /* PHYSFS_PLATFORM_LIBRETRO_NO_THREADS */
  21. #include "physfs_internal.h"
  22. static retro_environment_t physfs_platform_libretro_environ_cb = NULL;
  23. static struct retro_vfs_interface *physfs_platform_libretro_vfs = NULL;
  24. int __PHYSFS_platformInit(const char *argv0)
  25. {
  26. /* as a cheat, we expect argv0 to be a retro_environment_t callback. */
  27. retro_environment_t environ_cb = (retro_environment_t) argv0;
  28. struct retro_vfs_interface_info vfs_interface_info;
  29. BAIL_IF(environ_cb == NULL, PHYSFS_ERR_INVALID_ARGUMENT, 0);
  30. /* get the virtual file system interface, at least version 3 */
  31. vfs_interface_info.required_interface_version = 3;
  32. vfs_interface_info.iface = NULL;
  33. BAIL_IF(!environ_cb(RETRO_ENVIRONMENT_GET_VFS_INTERFACE, &vfs_interface_info), PHYSFS_ERR_UNSUPPORTED, 0);
  34. physfs_platform_libretro_vfs = vfs_interface_info.iface;
  35. physfs_platform_libretro_environ_cb = environ_cb;
  36. return 1;
  37. } /* __PHYSFS_platformInit */
  38. void __PHYSFS_platformDeinit(void)
  39. {
  40. physfs_platform_libretro_environ_cb = NULL;
  41. physfs_platform_libretro_vfs = NULL;
  42. } /* __PHYSFS_platformDeinit */
  43. void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
  44. {
  45. /* no-op. */
  46. } /* __PHYSFS_platformDetectAvailableCDs */
  47. char *physfs_platform_libretro_get_directory(int libretro_dir, int add_slash)
  48. {
  49. const char *dir = NULL;
  50. char *retval;
  51. size_t dir_length;
  52. BAIL_IF(physfs_platform_libretro_environ_cb == NULL, PHYSFS_ERR_NOT_INITIALIZED, NULL);
  53. BAIL_IF(!physfs_platform_libretro_environ_cb(libretro_dir, &dir), PHYSFS_ERR_UNSUPPORTED, NULL);
  54. dir_length = strlen(dir);
  55. retval = allocator.Malloc(dir_length + 2);
  56. if (!retval) {
  57. BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  58. }
  59. strcpy(retval, dir);
  60. if (add_slash && (dir_length == 0 || retval[dir_length - 1] != __PHYSFS_platformDirSeparator)) {
  61. retval[dir_length] = __PHYSFS_platformDirSeparator;
  62. retval[dir_length + 1] = '\0';
  63. }
  64. return retval;
  65. }
  66. char *__PHYSFS_platformCalcBaseDir(const char *argv0)
  67. {
  68. int append_slash = 1;
  69. char* dir = NULL;
  70. char* retval;
  71. size_t dir_length;
  72. const struct retro_game_info_ext *game_info_ext = NULL;
  73. BAIL_IF(physfs_platform_libretro_environ_cb == NULL, PHYSFS_ERR_NOT_INITIALIZED, NULL);
  74. /* try to get the base path to the actively loaded content */
  75. if (physfs_platform_libretro_environ_cb(RETRO_ENVIRONMENT_GET_GAME_INFO_EXT, &game_info_ext) && game_info_ext != NULL) {
  76. /* use the archive file if the content is within an archive */
  77. if (game_info_ext->file_in_archive && game_info_ext->archive_path != NULL) {
  78. dir = (char *) game_info_ext->archive_path;
  79. append_slash = 0;
  80. } else {
  81. dir = (char *) game_info_ext->dir;
  82. }
  83. }
  84. /* fallback to the system directory */
  85. if (dir == NULL)
  86. physfs_platform_libretro_environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir);
  87. /* as a last case, provide an empty string, assuming the platform resolves a working directory */
  88. if (dir == NULL)
  89. dir = "";
  90. dir_length = strlen(dir);
  91. retval = allocator.Malloc(dir_length + 2);
  92. BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  93. strcpy(retval, dir);
  94. /* append a slash if needed */
  95. if (append_slash && (dir_length == 0 || retval[dir_length - 1] != __PHYSFS_platformDirSeparator)) {
  96. retval[dir_length] = __PHYSFS_platformDirSeparator;
  97. retval[dir_length + 1] = '\0';
  98. }
  99. return retval;
  100. } /* __PHYSFS_platformCalcBaseDir */
  101. char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
  102. {
  103. const char *userdir = __PHYSFS_getUserDir();
  104. const size_t len = strlen(userdir) + strlen(org) + strlen(app) + 3;
  105. char *retval = (char *) allocator.Malloc(len);
  106. BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  107. snprintf(retval, len, "%s%s/%s/", userdir, org, app);
  108. return retval;
  109. } /* __PHYSFS_platformCalcPrefDir */
  110. char *__PHYSFS_platformCalcUserDir(void)
  111. {
  112. return physfs_platform_libretro_get_directory(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, 1);
  113. } /* __PHYSFS_platformCalcUserDir */
  114. #ifndef PHYSFS_PLATFORM_LIBRETRO_NO_THREADS
  115. /**
  116. * Thread mutex data structure.
  117. */
  118. typedef struct
  119. {
  120. slock_t *mutex;
  121. uintptr_t owner;
  122. PHYSFS_uint32 count;
  123. } PthreadMutex;
  124. #endif /* PHYSFS_PLATFORM_LIBRETRO_NO_THREADS */
  125. void *__PHYSFS_platformGetThreadID(void)
  126. {
  127. #ifdef PHYSFS_PLATFORM_LIBRETRO_NO_THREADS
  128. return (void *) (size_t) 0x1;
  129. #else
  130. return (void *) sthread_get_current_thread_id();
  131. #endif /* PHYSFS_PLATFORM_LIBRETRO_NO_THREADS */
  132. } /* __PHYSFS_platformGetThreadID */
  133. void *__PHYSFS_platformCreateMutex(void)
  134. {
  135. #ifdef PHYSFS_PLATFORM_LIBRETRO_NO_THREADS
  136. return (void *) (size_t) 0x1;
  137. #else
  138. PthreadMutex *m = (PthreadMutex *) allocator.Malloc(sizeof (PthreadMutex));
  139. BAIL_IF(!m, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  140. m->mutex = slock_new();
  141. if (!m->mutex) {
  142. allocator.Free(m);
  143. BAIL(PHYSFS_ERR_OS_ERROR, NULL);
  144. } /* if */
  145. m->count = 0;
  146. m->owner = (uintptr_t) 0xDEADBEEF;
  147. return (void *) m;
  148. #endif /* PHYSFS_PLATFORM_LIBRETRO_NO_THREADS */
  149. } /* __PHYSFS_platformCreateMutex */
  150. void __PHYSFS_platformDestroyMutex(void *mutex)
  151. {
  152. #ifndef PHYSFS_PLATFORM_LIBRETRO_NO_THREADS
  153. PthreadMutex *m = (PthreadMutex *) mutex;
  154. /* Destroying a locked mutex is a bug, but we'll try to be helpful. */
  155. if ((m->owner == sthread_get_current_thread_id()) && (m->count > 0))
  156. slock_unlock(m->mutex);
  157. slock_free(m->mutex);
  158. allocator.Free(m);
  159. #endif /* PHYSFS_PLATFORM_LIBRETRO_NO_THREADS */
  160. } /* __PHYSFS_platformDestroyMutex */
  161. int __PHYSFS_platformGrabMutex(void *mutex)
  162. {
  163. #ifdef PHYSFS_PLATFORM_LIBRETRO_NO_THREADS
  164. return 1; /* Single-threaded, so we ignore this by returning success. */
  165. #else
  166. PthreadMutex *m = (PthreadMutex *) mutex;
  167. uintptr_t tid = sthread_get_current_thread_id();
  168. if (m->owner != tid)
  169. {
  170. slock_lock(m->mutex);
  171. m->owner = tid;
  172. } /* if */
  173. m->count++;
  174. return 1;
  175. #endif /* PHYSFS_PLATFORM_LIBRETRO_NO_THREADS */
  176. } /* __PHYSFS_platformGrabMutex */
  177. void __PHYSFS_platformReleaseMutex(void *mutex)
  178. {
  179. #ifndef PHYSFS_PLATFORM_LIBRETRO_NO_THREADS
  180. PthreadMutex *m = (PthreadMutex *) mutex;
  181. assert(m->owner == sthread_get_current_thread_id()); /* catch programming errors. */
  182. assert(m->count > 0); /* catch programming errors. */
  183. if (m->owner == sthread_get_current_thread_id()) {
  184. if (--m->count == 0) {
  185. m->owner = (uintptr_t) 0xDEADBEEF;
  186. slock_unlock(m->mutex);
  187. } /* if */
  188. } /* if */
  189. #endif /* PHYSFS_PLATFORM_LIBRETRO_NO_THREADS */
  190. } /* __PHYSFS_platformReleaseMutex */
  191. PHYSFS_EnumerateCallbackResult __PHYSFS_platformEnumerate(const char *dirname,
  192. PHYSFS_EnumerateCallback callback,
  193. const char *origdir, void *callbackdata)
  194. {
  195. struct retro_vfs_dir_handle* dir;
  196. PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK;
  197. BAIL_IF(physfs_platform_libretro_vfs == NULL || physfs_platform_libretro_vfs->opendir == NULL || physfs_platform_libretro_vfs->readdir == NULL, PHYSFS_ERR_NOT_INITIALIZED, PHYSFS_ENUM_ERROR);
  198. dir = physfs_platform_libretro_vfs->opendir(dirname, true);
  199. BAIL_IF(dir == NULL, PHYSFS_ERR_NOT_FOUND, PHYSFS_ENUM_ERROR);
  200. while (physfs_platform_libretro_vfs->readdir(dir)) {
  201. const char *name = physfs_platform_libretro_vfs->dirent_get_name(dir);
  202. if (name[0] == '.') { /* ignore "." and ".." */
  203. if ((name[1] == '\0') || ((name[1] == '.') && (name[2] == '\0'))) {
  204. continue;
  205. } /* if */
  206. } /* if */
  207. retval = callback(callbackdata, origdir, name);
  208. if (retval == PHYSFS_ENUM_ERROR) {
  209. PHYSFS_setErrorCode(PHYSFS_ERR_APP_CALLBACK);
  210. break;
  211. }
  212. } /* while */
  213. if (physfs_platform_libretro_vfs->closedir != NULL) {
  214. physfs_platform_libretro_vfs->closedir(dir);
  215. }
  216. return retval;
  217. } /* __PHYSFS_platformEnumerate */
  218. static void *__PHYSFS_platformOpen(const char *filename, unsigned lr_mode, int appending)
  219. {
  220. void* retval;
  221. BAIL_IF(physfs_platform_libretro_vfs == NULL || physfs_platform_libretro_vfs->open == NULL, PHYSFS_ERR_NOT_INITIALIZED, NULL);
  222. BAIL_IF(filename == NULL || strlen(filename) == 0, PHYSFS_ERR_BAD_FILENAME, NULL);
  223. retval = (void *) physfs_platform_libretro_vfs->open(filename, lr_mode, RETRO_VFS_FILE_ACCESS_HINT_NONE);
  224. BAIL_IF(retval == NULL, PHYSFS_ERR_IO, NULL);
  225. if (appending) {
  226. if (physfs_platform_libretro_vfs->seek(retval, 0, RETRO_VFS_SEEK_POSITION_END) < 0) {
  227. physfs_platform_libretro_vfs->close(retval);
  228. BAIL(PHYSFS_ERR_IO, NULL);
  229. } /* if */
  230. } /* if */
  231. return retval;
  232. } /* doOpen */
  233. void *__PHYSFS_platformOpenRead(const char *filename)
  234. {
  235. return __PHYSFS_platformOpen(filename, RETRO_VFS_FILE_ACCESS_READ, 0);
  236. } /* __PHYSFS_platformOpenRead */
  237. void *__PHYSFS_platformOpenWrite(const char *filename)
  238. {
  239. return __PHYSFS_platformOpen(filename, RETRO_VFS_FILE_ACCESS_WRITE, 0);
  240. } /* __PHYSFS_platformOpenWrite */
  241. void *__PHYSFS_platformOpenAppend(const char *filename)
  242. {
  243. return __PHYSFS_platformOpen(filename, RETRO_VFS_FILE_ACCESS_WRITE | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING, 1);
  244. } /* __PHYSFS_platformOpenAppend */
  245. PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer,
  246. PHYSFS_uint64 len)
  247. {
  248. PHYSFS_sint64 retval;
  249. BAIL_IF(physfs_platform_libretro_vfs == NULL || physfs_platform_libretro_vfs->read == NULL, PHYSFS_ERR_NOT_INITIALIZED, -1);
  250. BAIL_IF(opaque == NULL || buffer == NULL, PHYSFS_ERR_INVALID_ARGUMENT, -1);
  251. retval = (PHYSFS_sint64) physfs_platform_libretro_vfs->read((struct retro_vfs_file_handle *) opaque, buffer, (uint64_t) len);
  252. BAIL_IF(retval == -1, PHYSFS_ERR_IO, -1);
  253. return retval;
  254. } /* __PHYSFS_platformRead */
  255. PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
  256. PHYSFS_uint64 len)
  257. {
  258. PHYSFS_sint64 retval;
  259. BAIL_IF(physfs_platform_libretro_vfs == NULL || physfs_platform_libretro_vfs->write == NULL, PHYSFS_ERR_NOT_INITIALIZED, -1);
  260. BAIL_IF(opaque == NULL || buffer == NULL, PHYSFS_ERR_INVALID_ARGUMENT, -1);
  261. retval = (PHYSFS_sint64) physfs_platform_libretro_vfs->write((struct retro_vfs_file_handle *) opaque, buffer, (uint64_t)len);
  262. BAIL_IF(retval == -1, PHYSFS_ERR_IO, -1);
  263. return retval;
  264. } /* __PHYSFS_platformWrite */
  265. int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
  266. {
  267. BAIL_IF(physfs_platform_libretro_vfs == NULL || physfs_platform_libretro_vfs->seek == NULL, PHYSFS_ERR_NOT_INITIALIZED, 0);
  268. BAIL_IF(opaque == NULL, PHYSFS_ERR_INVALID_ARGUMENT, 0);
  269. BAIL_IF(physfs_platform_libretro_vfs->seek((struct retro_vfs_file_handle *) opaque, pos, RETRO_VFS_SEEK_POSITION_START) == -1, PHYSFS_ERR_IO, 0);
  270. return 1;
  271. } /* __PHYSFS_platformSeek */
  272. PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
  273. {
  274. BAIL_IF(physfs_platform_libretro_vfs == NULL || physfs_platform_libretro_vfs->tell == NULL, PHYSFS_ERR_NOT_INITIALIZED, -1);
  275. BAIL_IF(opaque == NULL, PHYSFS_ERR_INVALID_ARGUMENT, -1);
  276. return (PHYSFS_sint64) physfs_platform_libretro_vfs->tell((struct retro_vfs_file_handle *) opaque);
  277. } /* __PHYSFS_platformTell */
  278. PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
  279. {
  280. BAIL_IF(physfs_platform_libretro_vfs == NULL || physfs_platform_libretro_vfs->size == NULL, PHYSFS_ERR_NOT_INITIALIZED, -1);
  281. BAIL_IF(opaque == NULL, PHYSFS_ERR_INVALID_ARGUMENT, -1);
  282. return (PHYSFS_sint64) physfs_platform_libretro_vfs->size((struct retro_vfs_file_handle *) opaque);
  283. } /* __PHYSFS_platformFileLength */
  284. int __PHYSFS_platformFlush(void *opaque)
  285. {
  286. BAIL_IF(physfs_platform_libretro_vfs == NULL || physfs_platform_libretro_vfs->flush == NULL, PHYSFS_ERR_NOT_INITIALIZED, 0);
  287. BAIL_IF(opaque == NULL, PHYSFS_ERR_INVALID_ARGUMENT, 0);
  288. return physfs_platform_libretro_vfs->flush((struct retro_vfs_file_handle *) opaque) == 0 ? 1 : 0;
  289. } /* __PHYSFS_platformFlush */
  290. void __PHYSFS_platformClose(void *opaque)
  291. {
  292. BAIL_IF(physfs_platform_libretro_vfs == NULL || physfs_platform_libretro_vfs->close == NULL, PHYSFS_ERR_NOT_INITIALIZED, /* void */);
  293. BAIL_IF(opaque == NULL, PHYSFS_ERR_INVALID_ARGUMENT, /* void */);
  294. physfs_platform_libretro_vfs->close((struct retro_vfs_file_handle *) opaque);
  295. } /* __PHYSFS_platformClose */
  296. int __PHYSFS_platformDelete(const char *path)
  297. {
  298. BAIL_IF(physfs_platform_libretro_vfs == NULL || physfs_platform_libretro_vfs->remove == NULL, PHYSFS_ERR_NOT_INITIALIZED, 0);
  299. return physfs_platform_libretro_vfs->remove(path) == 0 ? 1 : 0;
  300. } /* __PHYSFS_platformDelete */
  301. static PHYSFS_ErrorCode errcodeFromErrnoError(const int err)
  302. {
  303. switch (err)
  304. {
  305. case 0: return PHYSFS_ERR_OK;
  306. case EACCES: return PHYSFS_ERR_PERMISSION;
  307. case EPERM: return PHYSFS_ERR_PERMISSION;
  308. #if defined(EDQUOT)
  309. case EDQUOT: return PHYSFS_ERR_NO_SPACE;
  310. #elif defined(WSAEDQUOT)
  311. case WSAEDQUOT: return PHYSFS_ERR_NO_SPACE;
  312. #endif
  313. case EIO: return PHYSFS_ERR_IO;
  314. #if defined(ELOOP)
  315. case ELOOP: return PHYSFS_ERR_SYMLINK_LOOP;
  316. #endif
  317. case EMLINK: return PHYSFS_ERR_NO_SPACE;
  318. case ENAMETOOLONG: return PHYSFS_ERR_BAD_FILENAME;
  319. case ENOENT: return PHYSFS_ERR_NOT_FOUND;
  320. case ENOSPC: return PHYSFS_ERR_NO_SPACE;
  321. case ENOTDIR: return PHYSFS_ERR_NOT_FOUND;
  322. case EISDIR: return PHYSFS_ERR_NOT_A_FILE;
  323. case EROFS: return PHYSFS_ERR_READ_ONLY;
  324. case ETXTBSY: return PHYSFS_ERR_BUSY;
  325. case EBUSY: return PHYSFS_ERR_BUSY;
  326. case ENOMEM: return PHYSFS_ERR_OUT_OF_MEMORY;
  327. case ENOTEMPTY: return PHYSFS_ERR_DIR_NOT_EMPTY;
  328. default: return PHYSFS_ERR_OS_ERROR;
  329. } /* switch */
  330. } /* errcodeFromErrnoError */
  331. static inline PHYSFS_ErrorCode errcodeFromErrno(void)
  332. {
  333. return errcodeFromErrnoError(errno);
  334. } /* errcodeFromErrno */
  335. int __PHYSFS_platformMkDir(const char *path)
  336. {
  337. int rc;
  338. BAIL_IF(physfs_platform_libretro_vfs == NULL || physfs_platform_libretro_vfs->mkdir == NULL, PHYSFS_ERR_NOT_INITIALIZED, -1);
  339. rc = physfs_platform_libretro_vfs->mkdir(path);
  340. BAIL_IF(rc == -1, PHYSFS_ERR_IO, 0); /* Unknown failure */
  341. BAIL_IF(rc == -2, PHYSFS_ERR_DUPLICATE, 0); /* Already exists */
  342. return 1;
  343. } /* __PHYSFS_platformMkDir */
  344. int __PHYSFS_platformStat(const char *fname, PHYSFS_Stat *st, const int follow)
  345. {
  346. int32_t size;
  347. int flags;
  348. BAIL_IF(physfs_platform_libretro_vfs == NULL || physfs_platform_libretro_vfs->stat == NULL, PHYSFS_ERR_NOT_INITIALIZED, 0);
  349. flags = physfs_platform_libretro_vfs->stat(fname, &size);
  350. if (!(flags & RETRO_VFS_STAT_IS_VALID)) {
  351. BAIL(PHYSFS_ERR_NOT_FOUND, 0);
  352. }
  353. if (flags & RETRO_VFS_STAT_IS_DIRECTORY) {
  354. st->filetype = PHYSFS_FILETYPE_DIRECTORY;
  355. st->filesize = 0;
  356. } else if (flags & RETRO_VFS_STAT_IS_CHARACTER_SPECIAL) {
  357. st->filetype = PHYSFS_FILETYPE_OTHER;
  358. st->filesize = 0;
  359. } else {
  360. st->filetype = PHYSFS_FILETYPE_REGULAR;
  361. st->filesize = size;
  362. }
  363. /* libretro's virtual file system doesn't support retrieving the following values */
  364. st->modtime = -1;
  365. st->createtime = -1;
  366. st->accesstime = -1;
  367. st->readonly = 0;
  368. return 1;
  369. } /* __PHYSFS_platformStat */
  370. #endif /* PHYSFS_PLATFORM_LIBRETRO */
  371. /* end of physfs_platform_libretro.c ... */