SDL_iostream.c 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #include "SDL_internal.h"
  19. #if defined(SDL_PLATFORM_WINDOWS)
  20. #include "../core/windows/SDL_windows.h"
  21. #else
  22. #include <unistd.h>
  23. #endif
  24. #ifdef HAVE_STDIO_H
  25. #include <stdio.h>
  26. #include <errno.h>
  27. #include <sys/stat.h>
  28. #endif
  29. #ifdef HAVE_LIMITS_H
  30. #include <limits.h>
  31. #endif
  32. #ifdef SDL_PLATFORM_APPLE
  33. #include <fcntl.h>
  34. #endif
  35. #include "SDL_iostream_c.h"
  36. /* This file provides a general interface for SDL to read and write
  37. data sources. It can easily be extended to files, memory, etc.
  38. */
  39. struct SDL_IOStream
  40. {
  41. SDL_IOStreamInterface iface;
  42. void *userdata;
  43. SDL_IOStatus status;
  44. SDL_PropertiesID props;
  45. };
  46. #ifdef SDL_PLATFORM_3DS
  47. #include "n3ds/SDL_iostreamromfs.h"
  48. #endif // SDL_PLATFORM_3DS
  49. #ifdef SDL_PLATFORM_ANDROID
  50. #include <unistd.h>
  51. #include "../core/android/SDL_android.h"
  52. #endif
  53. #if defined(SDL_PLATFORM_WINDOWS)
  54. typedef struct IOStreamWindowsData
  55. {
  56. HANDLE h;
  57. void *data;
  58. size_t size;
  59. size_t left;
  60. bool append;
  61. bool autoclose;
  62. } IOStreamWindowsData;
  63. // Functions to read/write Win32 API file pointers
  64. #ifndef INVALID_SET_FILE_POINTER
  65. #define INVALID_SET_FILE_POINTER 0xFFFFFFFF
  66. #endif
  67. #define READAHEAD_BUFFER_SIZE 1024
  68. static HANDLE SDLCALL windows_file_open(const char *filename, const char *mode)
  69. {
  70. #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
  71. UINT old_error_mode;
  72. #endif
  73. HANDLE h;
  74. DWORD r_right, w_right;
  75. DWORD must_exist, truncate;
  76. int a_mode;
  77. // "r" = reading, file must exist
  78. // "w" = writing, truncate existing, file may not exist
  79. // "r+"= reading or writing, file must exist
  80. // "a" = writing, append file may not exist
  81. // "a+"= append + read, file may not exist
  82. // "w+" = read, write, truncate. file may not exist
  83. must_exist = (SDL_strchr(mode, 'r') != NULL) ? OPEN_EXISTING : 0;
  84. truncate = (SDL_strchr(mode, 'w') != NULL) ? CREATE_ALWAYS : 0;
  85. r_right = (SDL_strchr(mode, '+') != NULL || must_exist) ? GENERIC_READ : 0;
  86. a_mode = (SDL_strchr(mode, 'a') != NULL) ? OPEN_ALWAYS : 0;
  87. w_right = (a_mode || SDL_strchr(mode, '+') || truncate) ? GENERIC_WRITE : 0;
  88. if (!r_right && !w_right) {
  89. return INVALID_HANDLE_VALUE; // inconsistent mode
  90. }
  91. // failed (invalid call)
  92. #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
  93. // Do not open a dialog box if failure
  94. old_error_mode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
  95. #endif
  96. {
  97. LPWSTR str = WIN_UTF8ToStringW(filename);
  98. h = CreateFileW(str,
  99. (w_right | r_right),
  100. (w_right) ? 0 : FILE_SHARE_READ,
  101. NULL,
  102. (must_exist | truncate | a_mode),
  103. FILE_ATTRIBUTE_NORMAL,
  104. NULL);
  105. SDL_free(str);
  106. }
  107. #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
  108. // restore old behavior
  109. SetErrorMode(old_error_mode);
  110. #endif
  111. if (h == INVALID_HANDLE_VALUE) {
  112. char *error;
  113. if (SDL_asprintf(&error, "Couldn't open %s", filename) > 0) {
  114. WIN_SetError(error);
  115. SDL_free(error);
  116. } else {
  117. SDL_SetError("Couldn't open %s", filename);
  118. }
  119. }
  120. return h;
  121. }
  122. static Sint64 SDLCALL windows_file_size(void *userdata)
  123. {
  124. IOStreamWindowsData *iodata = (IOStreamWindowsData *) userdata;
  125. LARGE_INTEGER size;
  126. if (!GetFileSizeEx(iodata->h, &size)) {
  127. return WIN_SetError("windows_file_size");
  128. }
  129. return size.QuadPart;
  130. }
  131. static Sint64 SDLCALL windows_file_seek(void *userdata, Sint64 offset, SDL_IOWhence whence)
  132. {
  133. IOStreamWindowsData *iodata = (IOStreamWindowsData *) userdata;
  134. DWORD windowswhence;
  135. LARGE_INTEGER windowsoffset;
  136. // FIXME: We may be able to satisfy the seek within buffered data
  137. if ((whence == SDL_IO_SEEK_CUR) && (iodata->left)) {
  138. offset -= iodata->left;
  139. }
  140. iodata->left = 0;
  141. switch (whence) {
  142. case SDL_IO_SEEK_SET:
  143. windowswhence = FILE_BEGIN;
  144. break;
  145. case SDL_IO_SEEK_CUR:
  146. windowswhence = FILE_CURRENT;
  147. break;
  148. case SDL_IO_SEEK_END:
  149. windowswhence = FILE_END;
  150. break;
  151. default:
  152. SDL_SetError("windows_file_seek: Unknown value for 'whence'");
  153. return -1;
  154. }
  155. windowsoffset.QuadPart = offset;
  156. if (!SetFilePointerEx(iodata->h, windowsoffset, &windowsoffset, windowswhence)) {
  157. return WIN_SetError("Error seeking in datastream");
  158. }
  159. return windowsoffset.QuadPart;
  160. }
  161. static size_t SDLCALL windows_file_read(void *userdata, void *ptr, size_t size, SDL_IOStatus *status)
  162. {
  163. IOStreamWindowsData *iodata = (IOStreamWindowsData *) userdata;
  164. size_t total_need = size;
  165. size_t total_read = 0;
  166. size_t read_ahead;
  167. DWORD bytes;
  168. if (iodata->left > 0) {
  169. void *data = (char *)iodata->data +
  170. iodata->size -
  171. iodata->left;
  172. read_ahead = SDL_min(total_need, iodata->left);
  173. SDL_memcpy(ptr, data, read_ahead);
  174. iodata->left -= read_ahead;
  175. if (read_ahead == total_need) {
  176. return size;
  177. }
  178. ptr = (char *)ptr + read_ahead;
  179. total_need -= read_ahead;
  180. total_read += read_ahead;
  181. }
  182. if (total_need < READAHEAD_BUFFER_SIZE) {
  183. if (!ReadFile(iodata->h, iodata->data, READAHEAD_BUFFER_SIZE, &bytes, NULL)) {
  184. DWORD error = GetLastError();
  185. switch (error) {
  186. case ERROR_BROKEN_PIPE:
  187. case ERROR_HANDLE_EOF:
  188. break;
  189. case ERROR_NO_DATA:
  190. *status = SDL_IO_STATUS_NOT_READY;
  191. break;
  192. default:
  193. WIN_SetError("Error reading from datastream");
  194. break;
  195. }
  196. return 0;
  197. }
  198. read_ahead = SDL_min(total_need, bytes);
  199. SDL_memcpy(ptr, iodata->data, read_ahead);
  200. iodata->size = bytes;
  201. iodata->left = bytes - read_ahead;
  202. total_read += read_ahead;
  203. } else {
  204. if (!ReadFile(iodata->h, ptr, (DWORD)total_need, &bytes, NULL)) {
  205. DWORD error = GetLastError();
  206. switch (error) {
  207. case ERROR_BROKEN_PIPE:
  208. case ERROR_HANDLE_EOF:
  209. break;
  210. case ERROR_NO_DATA:
  211. *status = SDL_IO_STATUS_NOT_READY;
  212. break;
  213. default:
  214. WIN_SetError("Error reading from datastream");
  215. break;
  216. }
  217. return 0;
  218. }
  219. total_read += bytes;
  220. }
  221. return total_read;
  222. }
  223. static size_t SDLCALL windows_file_write(void *userdata, const void *ptr, size_t size, SDL_IOStatus *status)
  224. {
  225. IOStreamWindowsData *iodata = (IOStreamWindowsData *) userdata;
  226. DWORD bytes;
  227. if (iodata->left) {
  228. if (!SetFilePointer(iodata->h, -(LONG)iodata->left, NULL, FILE_CURRENT)) {
  229. WIN_SetError("Error seeking in datastream");
  230. return 0;
  231. }
  232. iodata->left = 0;
  233. }
  234. // if in append mode, we must go to the EOF before write
  235. if (iodata->append) {
  236. LARGE_INTEGER windowsoffset;
  237. windowsoffset.QuadPart = 0;
  238. if (!SetFilePointerEx(iodata->h, windowsoffset, &windowsoffset, FILE_END)) {
  239. WIN_SetError("Error seeking in datastream");
  240. return 0;
  241. }
  242. }
  243. if (!WriteFile(iodata->h, ptr, (DWORD)size, &bytes, NULL)) {
  244. WIN_SetError("Error writing to datastream");
  245. return 0;
  246. }
  247. if (bytes == 0 && size > 0) {
  248. *status = SDL_IO_STATUS_NOT_READY;
  249. }
  250. return bytes;
  251. }
  252. static bool SDLCALL windows_file_flush(void *userdata, SDL_IOStatus *status)
  253. {
  254. IOStreamWindowsData *iodata = (IOStreamWindowsData *) userdata;
  255. if (!FlushFileBuffers(iodata->h)) {
  256. return WIN_SetError("Error flushing datastream");
  257. }
  258. return true;
  259. }
  260. static bool SDLCALL windows_file_close(void *userdata)
  261. {
  262. IOStreamWindowsData *iodata = (IOStreamWindowsData *) userdata;
  263. if (iodata->h != INVALID_HANDLE_VALUE) {
  264. if (iodata->autoclose) {
  265. CloseHandle(iodata->h);
  266. }
  267. iodata->h = INVALID_HANDLE_VALUE; // to be sure
  268. }
  269. SDL_free(iodata->data);
  270. SDL_free(iodata);
  271. return true;
  272. }
  273. SDL_IOStream *SDL_IOFromHandle(HANDLE handle, const char *mode, bool autoclose)
  274. {
  275. IOStreamWindowsData *iodata = (IOStreamWindowsData *) SDL_calloc(1, sizeof (*iodata));
  276. if (!iodata) {
  277. if (autoclose) {
  278. CloseHandle(handle);
  279. }
  280. return NULL;
  281. }
  282. SDL_IOStreamInterface iface;
  283. SDL_INIT_INTERFACE(&iface);
  284. if (GetFileType(handle) == FILE_TYPE_DISK) {
  285. iface.size = windows_file_size;
  286. iface.seek = windows_file_seek;
  287. }
  288. iface.read = windows_file_read;
  289. iface.write = windows_file_write;
  290. iface.flush = windows_file_flush;
  291. iface.close = windows_file_close;
  292. iodata->h = handle;
  293. iodata->append = (SDL_strchr(mode, 'a') != NULL);
  294. iodata->autoclose = autoclose;
  295. iodata->data = (char *)SDL_malloc(READAHEAD_BUFFER_SIZE);
  296. if (!iodata->data) {
  297. iface.close(iodata);
  298. return NULL;
  299. }
  300. SDL_IOStream *iostr = SDL_OpenIO(&iface, iodata);
  301. if (!iostr) {
  302. iface.close(iodata);
  303. } else {
  304. const SDL_PropertiesID props = SDL_GetIOProperties(iostr);
  305. if (props) {
  306. SDL_SetPointerProperty(props, SDL_PROP_IOSTREAM_WINDOWS_HANDLE_POINTER, iodata->h);
  307. }
  308. }
  309. return iostr;
  310. }
  311. #endif // defined(SDL_PLATFORM_WINDOWS)
  312. #if !defined(SDL_PLATFORM_WINDOWS)
  313. // Functions to read/write file descriptors. Not used for windows.
  314. typedef struct IOStreamFDData
  315. {
  316. int fd;
  317. bool autoclose;
  318. bool regular_file;
  319. } IOStreamFDData;
  320. static int SDL_fdatasync(int fd)
  321. {
  322. int result = 0;
  323. #if defined(SDL_PLATFORM_APPLE) // Apple doesn't have fdatasync (rather, the symbol exists as an incompatible system call).
  324. result = fcntl(fd, F_FULLFSYNC);
  325. #elif defined(SDL_PLATFORM_HAIKU)
  326. result = fsync(fd);
  327. #elif defined(HAVE_FDATASYNC)
  328. result = fdatasync(fd);
  329. #endif
  330. return result;
  331. }
  332. static Sint64 SDLCALL fd_seek(void *userdata, Sint64 offset, SDL_IOWhence whence)
  333. {
  334. IOStreamFDData *iodata = (IOStreamFDData *) userdata;
  335. int fdwhence;
  336. switch (whence) {
  337. case SDL_IO_SEEK_SET:
  338. fdwhence = SEEK_SET;
  339. break;
  340. case SDL_IO_SEEK_CUR:
  341. fdwhence = SEEK_CUR;
  342. break;
  343. case SDL_IO_SEEK_END:
  344. fdwhence = SEEK_END;
  345. break;
  346. default:
  347. SDL_SetError("Unknown value for 'whence'");
  348. return -1;
  349. }
  350. off_t result = lseek(iodata->fd, (off_t)offset, fdwhence);
  351. if (result < 0) {
  352. SDL_SetError("Couldn't get stream offset: %s", strerror(errno));
  353. }
  354. return result;
  355. }
  356. static size_t SDLCALL fd_read(void *userdata, void *ptr, size_t size, SDL_IOStatus *status)
  357. {
  358. IOStreamFDData *iodata = (IOStreamFDData *) userdata;
  359. ssize_t bytes;
  360. do {
  361. bytes = read(iodata->fd, ptr, size);
  362. } while (bytes < 0 && errno == EINTR);
  363. if (bytes < 0) {
  364. if (errno == EAGAIN) {
  365. *status = SDL_IO_STATUS_NOT_READY;
  366. } else {
  367. SDL_SetError("Error reading from datastream: %s", strerror(errno));
  368. }
  369. bytes = 0;
  370. }
  371. return (size_t)bytes;
  372. }
  373. static size_t SDLCALL fd_write(void *userdata, const void *ptr, size_t size, SDL_IOStatus *status)
  374. {
  375. IOStreamFDData *iodata = (IOStreamFDData *) userdata;
  376. ssize_t bytes;
  377. do {
  378. bytes = write(iodata->fd, ptr, size);
  379. } while (bytes < 0 && errno == EINTR);
  380. if (bytes < 0) {
  381. if (errno == EAGAIN) {
  382. *status = SDL_IO_STATUS_NOT_READY;
  383. } else {
  384. SDL_SetError("Error writing to datastream: %s", strerror(errno));
  385. }
  386. bytes = 0;
  387. }
  388. return (size_t)bytes;
  389. }
  390. static bool SDLCALL fd_flush(void *userdata, SDL_IOStatus *status)
  391. {
  392. IOStreamFDData *iodata = (IOStreamFDData *) userdata;
  393. int result;
  394. do {
  395. result = SDL_fdatasync(iodata->fd);
  396. } while (result < 0 && errno == EINTR);
  397. if (result < 0 && errno != EINVAL) {
  398. return SDL_SetError("Error flushing datastream: %s", strerror(errno));
  399. }
  400. return true;
  401. }
  402. static bool SDLCALL fd_close(void *userdata)
  403. {
  404. IOStreamFDData *iodata = (IOStreamFDData *) userdata;
  405. bool status = true;
  406. if (iodata->autoclose) {
  407. if (close(iodata->fd) < 0) {
  408. status = SDL_SetError("Error closing datastream: %s", strerror(errno));
  409. }
  410. }
  411. SDL_free(iodata);
  412. return status;
  413. }
  414. SDL_IOStream *SDL_IOFromFD(int fd, bool autoclose)
  415. {
  416. IOStreamFDData *iodata = (IOStreamFDData *) SDL_calloc(1, sizeof (*iodata));
  417. if (!iodata) {
  418. if (autoclose) {
  419. close(fd);
  420. }
  421. return NULL;
  422. }
  423. SDL_IOStreamInterface iface;
  424. SDL_INIT_INTERFACE(&iface);
  425. // There's no fd_size because SDL_GetIOSize emulates it the same way we'd do it for fd anyhow.
  426. iface.seek = fd_seek;
  427. iface.read = fd_read;
  428. iface.write = fd_write;
  429. iface.flush = fd_flush;
  430. iface.close = fd_close;
  431. iodata->fd = fd;
  432. iodata->autoclose = autoclose;
  433. struct stat st;
  434. iodata->regular_file = ((fstat(fd, &st) == 0) && S_ISREG(st.st_mode));
  435. SDL_IOStream *iostr = SDL_OpenIO(&iface, iodata);
  436. if (!iostr) {
  437. iface.close(iodata);
  438. } else {
  439. const SDL_PropertiesID props = SDL_GetIOProperties(iostr);
  440. if (props) {
  441. SDL_SetNumberProperty(props, SDL_PROP_IOSTREAM_FILE_DESCRIPTOR_NUMBER, fd);
  442. }
  443. }
  444. return iostr;
  445. }
  446. #endif // !defined(SDL_PLATFORM_WINDOWS)
  447. #if defined(HAVE_STDIO_H) && !defined(SDL_PLATFORM_WINDOWS)
  448. // Functions to read/write stdio file pointers. Not used for windows.
  449. typedef struct IOStreamStdioData
  450. {
  451. FILE *fp;
  452. bool autoclose;
  453. bool regular_file;
  454. } IOStreamStdioData;
  455. #ifdef HAVE_FOPEN64
  456. #define fopen fopen64
  457. #endif
  458. #ifdef HAVE_FSEEKO64
  459. #define fseek_off_t off64_t
  460. #define fseek fseeko64
  461. #define ftell ftello64
  462. #elif defined(HAVE_FSEEKO)
  463. #if defined(OFF_MIN) && defined(OFF_MAX)
  464. #define FSEEK_OFF_MIN OFF_MIN
  465. #define FSEEK_OFF_MAX OFF_MAX
  466. #elif defined(HAVE_LIMITS_H)
  467. /* POSIX doesn't specify the minimum and maximum macros for off_t so
  468. * we have to improvise and dance around implementation-defined
  469. * behavior. This may fail if the off_t type has padding bits or
  470. * is not a two's-complement representation. The compilers will detect
  471. * and eliminate the dead code if off_t has 64 bits.
  472. */
  473. #define FSEEK_OFF_MAX (((((off_t)1 << (sizeof(off_t) * CHAR_BIT - 2)) - 1) << 1) + 1)
  474. #define FSEEK_OFF_MIN (-(FSEEK_OFF_MAX)-1)
  475. #endif
  476. #define fseek_off_t off_t
  477. #define fseek fseeko
  478. #define ftell ftello
  479. #elif defined(HAVE__FSEEKI64)
  480. #define fseek_off_t __int64
  481. #define fseek _fseeki64
  482. #define ftell _ftelli64
  483. #else
  484. #ifdef HAVE_LIMITS_H
  485. #define FSEEK_OFF_MIN LONG_MIN
  486. #define FSEEK_OFF_MAX LONG_MAX
  487. #endif
  488. #define fseek_off_t long
  489. #endif
  490. static Sint64 SDLCALL stdio_seek(void *userdata, Sint64 offset, SDL_IOWhence whence)
  491. {
  492. IOStreamStdioData *iodata = (IOStreamStdioData *) userdata;
  493. int stdiowhence;
  494. switch (whence) {
  495. case SDL_IO_SEEK_SET:
  496. stdiowhence = SEEK_SET;
  497. break;
  498. case SDL_IO_SEEK_CUR:
  499. stdiowhence = SEEK_CUR;
  500. break;
  501. case SDL_IO_SEEK_END:
  502. stdiowhence = SEEK_END;
  503. break;
  504. default:
  505. SDL_SetError("Unknown value for 'whence'");
  506. return -1;
  507. }
  508. #if defined(FSEEK_OFF_MIN) && defined(FSEEK_OFF_MAX)
  509. if (offset < (Sint64)(FSEEK_OFF_MIN) || offset > (Sint64)(FSEEK_OFF_MAX)) {
  510. SDL_SetError("Seek offset out of range");
  511. return -1;
  512. }
  513. #endif
  514. // don't make a possibly-costly API call for the noop seek from SDL_TellIO
  515. const bool is_noop = (whence == SDL_IO_SEEK_CUR) && (offset == 0);
  516. if (is_noop || fseek(iodata->fp, (fseek_off_t)offset, stdiowhence) == 0) {
  517. const Sint64 pos = ftell(iodata->fp);
  518. if (pos < 0) {
  519. SDL_SetError("Couldn't get stream offset: %s", strerror(errno));
  520. return -1;
  521. }
  522. return pos;
  523. }
  524. SDL_SetError("Error seeking in datastream: %s", strerror(errno));
  525. return -1;
  526. }
  527. static size_t SDLCALL stdio_read(void *userdata, void *ptr, size_t size, SDL_IOStatus *status)
  528. {
  529. IOStreamStdioData *iodata = (IOStreamStdioData *) userdata;
  530. const size_t bytes = fread(ptr, 1, size, iodata->fp);
  531. if (bytes == 0 && ferror(iodata->fp)) {
  532. if (errno == EAGAIN) {
  533. *status = SDL_IO_STATUS_NOT_READY;
  534. clearerr(iodata->fp);
  535. } else {
  536. SDL_SetError("Error reading from datastream: %s", strerror(errno));
  537. }
  538. }
  539. return bytes;
  540. }
  541. static size_t SDLCALL stdio_write(void *userdata, const void *ptr, size_t size, SDL_IOStatus *status)
  542. {
  543. IOStreamStdioData *iodata = (IOStreamStdioData *) userdata;
  544. const size_t bytes = fwrite(ptr, 1, size, iodata->fp);
  545. if (bytes == 0 && ferror(iodata->fp)) {
  546. if (errno == EAGAIN) {
  547. *status = SDL_IO_STATUS_NOT_READY;
  548. clearerr(iodata->fp);
  549. } else {
  550. SDL_SetError("Error writing to datastream: %s", strerror(errno));
  551. }
  552. }
  553. return bytes;
  554. }
  555. static bool SDLCALL stdio_flush(void *userdata, SDL_IOStatus *status)
  556. {
  557. IOStreamStdioData *iodata = (IOStreamStdioData *) userdata;
  558. if (fflush(iodata->fp) != 0) {
  559. if (errno == EAGAIN) {
  560. *status = SDL_IO_STATUS_NOT_READY;
  561. return false;
  562. } else {
  563. return SDL_SetError("Error flushing datastream: %s", strerror(errno));
  564. }
  565. }
  566. int result;
  567. int fd = fileno(iodata->fp);
  568. do {
  569. result = SDL_fdatasync(fd);
  570. } while (result < 0 && errno == EINTR);
  571. if (result < 0) {
  572. return SDL_SetError("Error flushing datastream: %s", strerror(errno));
  573. }
  574. return true;
  575. }
  576. static bool SDLCALL stdio_close(void *userdata)
  577. {
  578. IOStreamStdioData *iodata = (IOStreamStdioData *) userdata;
  579. bool status = true;
  580. if (iodata->autoclose) {
  581. if (fclose(iodata->fp) != 0) {
  582. status = SDL_SetError("Error closing datastream: %s", strerror(errno));
  583. }
  584. }
  585. SDL_free(iodata);
  586. return status;
  587. }
  588. SDL_IOStream *SDL_IOFromFP(FILE *fp, bool autoclose)
  589. {
  590. IOStreamStdioData *iodata = (IOStreamStdioData *) SDL_calloc(1, sizeof (*iodata));
  591. if (!iodata) {
  592. if (autoclose) {
  593. fclose(fp);
  594. }
  595. return NULL;
  596. }
  597. SDL_IOStreamInterface iface;
  598. SDL_INIT_INTERFACE(&iface);
  599. // There's no stdio_size because SDL_GetIOSize emulates it the same way we'd do it for stdio anyhow.
  600. iface.seek = stdio_seek;
  601. iface.read = stdio_read;
  602. iface.write = stdio_write;
  603. iface.flush = stdio_flush;
  604. iface.close = stdio_close;
  605. iodata->fp = fp;
  606. iodata->autoclose = autoclose;
  607. struct stat st;
  608. iodata->regular_file = ((fstat(fileno(fp), &st) == 0) && S_ISREG(st.st_mode));
  609. SDL_IOStream *iostr = SDL_OpenIO(&iface, iodata);
  610. if (!iostr) {
  611. iface.close(iodata);
  612. } else {
  613. const SDL_PropertiesID props = SDL_GetIOProperties(iostr);
  614. if (props) {
  615. SDL_SetPointerProperty(props, SDL_PROP_IOSTREAM_STDIO_FILE_POINTER, fp);
  616. SDL_SetNumberProperty(props, SDL_PROP_IOSTREAM_FILE_DESCRIPTOR_NUMBER, fileno(fp));
  617. }
  618. }
  619. return iostr;
  620. }
  621. #endif // !HAVE_STDIO_H && !defined(SDL_PLATFORM_WINDOWS)
  622. // Functions to read/write memory pointers
  623. typedef struct IOStreamMemData
  624. {
  625. Uint8 *base;
  626. Uint8 *here;
  627. Uint8 *stop;
  628. } IOStreamMemData;
  629. static Sint64 SDLCALL mem_size(void *userdata)
  630. {
  631. const IOStreamMemData *iodata = (IOStreamMemData *) userdata;
  632. return (iodata->stop - iodata->base);
  633. }
  634. static Sint64 SDLCALL mem_seek(void *userdata, Sint64 offset, SDL_IOWhence whence)
  635. {
  636. IOStreamMemData *iodata = (IOStreamMemData *) userdata;
  637. Uint8 *newpos;
  638. switch (whence) {
  639. case SDL_IO_SEEK_SET:
  640. newpos = iodata->base + offset;
  641. break;
  642. case SDL_IO_SEEK_CUR:
  643. newpos = iodata->here + offset;
  644. break;
  645. case SDL_IO_SEEK_END:
  646. newpos = iodata->stop + offset;
  647. break;
  648. default:
  649. SDL_SetError("Unknown value for 'whence'");
  650. return -1;
  651. }
  652. if (newpos < iodata->base) {
  653. newpos = iodata->base;
  654. }
  655. if (newpos > iodata->stop) {
  656. newpos = iodata->stop;
  657. }
  658. iodata->here = newpos;
  659. return (Sint64)(iodata->here - iodata->base);
  660. }
  661. static size_t mem_io(void *userdata, void *dst, const void *src, size_t size)
  662. {
  663. IOStreamMemData *iodata = (IOStreamMemData *) userdata;
  664. const size_t mem_available = (iodata->stop - iodata->here);
  665. if (size > mem_available) {
  666. size = mem_available;
  667. }
  668. SDL_memcpy(dst, src, size);
  669. iodata->here += size;
  670. return size;
  671. }
  672. static size_t SDLCALL mem_read(void *userdata, void *ptr, size_t size, SDL_IOStatus *status)
  673. {
  674. IOStreamMemData *iodata = (IOStreamMemData *) userdata;
  675. return mem_io(userdata, ptr, iodata->here, size);
  676. }
  677. static size_t SDLCALL mem_write(void *userdata, const void *ptr, size_t size, SDL_IOStatus *status)
  678. {
  679. IOStreamMemData *iodata = (IOStreamMemData *) userdata;
  680. return mem_io(userdata, iodata->here, ptr, size);
  681. }
  682. static bool SDLCALL mem_close(void *userdata)
  683. {
  684. SDL_free(userdata);
  685. return true;
  686. }
  687. // Functions to create SDL_IOStream structures from various data sources
  688. #if defined(HAVE_STDIO_H) && !defined(SDL_PLATFORM_WINDOWS)
  689. static bool IsRegularFileOrPipe(FILE *f)
  690. {
  691. #ifndef SDL_PLATFORM_EMSCRIPTEN
  692. struct stat st;
  693. if (fstat(fileno(f), &st) < 0 || !(S_ISREG(st.st_mode) || S_ISFIFO(st.st_mode))) {
  694. return false;
  695. }
  696. #endif // !SDL_PLATFORM_EMSCRIPTEN
  697. return true;
  698. }
  699. #endif
  700. SDL_IOStream *SDL_IOFromFile(const char *file, const char *mode)
  701. {
  702. SDL_IOStream *iostr = NULL;
  703. if (!file || !*file) {
  704. SDL_InvalidParamError("file");
  705. return NULL;
  706. }
  707. if (!mode || !*mode) {
  708. SDL_InvalidParamError("mode");
  709. return NULL;
  710. }
  711. #ifdef SDL_PLATFORM_ANDROID
  712. #ifdef HAVE_STDIO_H
  713. // Try to open the file on the filesystem first
  714. if (*file == '/') {
  715. FILE *fp = fopen(file, mode);
  716. if (fp) {
  717. if (!IsRegularFileOrPipe(fp)) {
  718. fclose(fp);
  719. SDL_SetError("%s is not a regular file or pipe", file);
  720. return NULL;
  721. }
  722. return SDL_IOFromFP(fp, true);
  723. }
  724. } else if (SDL_strncmp(file, "content://", 10) == 0) {
  725. // Try opening content:// URI
  726. int fd = Android_JNI_OpenFileDescriptor(file, mode);
  727. if (fd == -1) {
  728. // SDL error is already set.
  729. return NULL;
  730. }
  731. FILE *fp = fdopen(fd, mode);
  732. if (!fp) {
  733. close(fd);
  734. SDL_SetError("Unable to open file descriptor (%d) from URI %s: %s", fd, file, strerror(errno));
  735. return NULL;
  736. }
  737. return SDL_IOFromFP(fp, true);
  738. } else {
  739. // Try opening it from internal storage if it's a relative path
  740. char *path = NULL;
  741. SDL_asprintf(&path, "%s/%s", SDL_GetAndroidInternalStoragePath(), file);
  742. if (path) {
  743. FILE *fp = fopen(path, mode);
  744. SDL_free(path);
  745. if (fp) {
  746. if (!IsRegularFileOrPipe(fp)) {
  747. fclose(fp);
  748. SDL_SetError("%s is not a regular file or pipe", path);
  749. return NULL;
  750. }
  751. return SDL_IOFromFP(fp, true);
  752. }
  753. }
  754. }
  755. #endif // HAVE_STDIO_H
  756. // Try to open the file from the asset system
  757. void *iodata = NULL;
  758. if (!Android_JNI_FileOpen(&iodata, file, mode)) {
  759. return NULL;
  760. }
  761. SDL_IOStreamInterface iface;
  762. SDL_INIT_INTERFACE(&iface);
  763. iface.size = Android_JNI_FileSize;
  764. iface.seek = Android_JNI_FileSeek;
  765. iface.read = Android_JNI_FileRead;
  766. iface.write = Android_JNI_FileWrite;
  767. iface.close = Android_JNI_FileClose;
  768. iostr = SDL_OpenIO(&iface, iodata);
  769. if (!iostr) {
  770. iface.close(iodata);
  771. } else {
  772. const SDL_PropertiesID props = SDL_GetIOProperties(iostr);
  773. if (props) {
  774. SDL_SetPointerProperty(props, SDL_PROP_IOSTREAM_ANDROID_AASSET_POINTER, iodata);
  775. }
  776. }
  777. #elif defined(SDL_PLATFORM_WINDOWS)
  778. HANDLE handle = windows_file_open(file, mode);
  779. if (handle != INVALID_HANDLE_VALUE) {
  780. iostr = SDL_IOFromHandle(handle, mode, true);
  781. }
  782. #elif defined(HAVE_STDIO_H)
  783. {
  784. #if defined(SDL_PLATFORM_3DS)
  785. FILE *fp = N3DS_FileOpen(file, mode);
  786. #else
  787. FILE *fp = fopen(file, mode);
  788. #endif
  789. if (!fp) {
  790. SDL_SetError("Couldn't open %s: %s", file, strerror(errno));
  791. } else if (!IsRegularFileOrPipe(fp)) {
  792. fclose(fp);
  793. fp = NULL;
  794. SDL_SetError("%s is not a regular file or pipe", file);
  795. } else {
  796. iostr = SDL_IOFromFP(fp, true);
  797. }
  798. }
  799. #else
  800. SDL_SetError("SDL not compiled with stdio support");
  801. #endif // !HAVE_STDIO_H
  802. return iostr;
  803. }
  804. SDL_IOStream *SDL_IOFromMem(void *mem, size_t size)
  805. {
  806. if (!mem) {
  807. SDL_InvalidParamError("mem");
  808. return NULL;
  809. } else if (!size) {
  810. SDL_InvalidParamError("size");
  811. return NULL;
  812. }
  813. IOStreamMemData *iodata = (IOStreamMemData *) SDL_calloc(1, sizeof (*iodata));
  814. if (!iodata) {
  815. return NULL;
  816. }
  817. SDL_IOStreamInterface iface;
  818. SDL_INIT_INTERFACE(&iface);
  819. iface.size = mem_size;
  820. iface.seek = mem_seek;
  821. iface.read = mem_read;
  822. iface.write = mem_write;
  823. iface.close = mem_close;
  824. iodata->base = (Uint8 *)mem;
  825. iodata->here = iodata->base;
  826. iodata->stop = iodata->base + size;
  827. SDL_IOStream *iostr = SDL_OpenIO(&iface, iodata);
  828. if (!iostr) {
  829. SDL_free(iodata);
  830. } else {
  831. const SDL_PropertiesID props = SDL_GetIOProperties(iostr);
  832. if (props) {
  833. SDL_SetPointerProperty(props, SDL_PROP_IOSTREAM_MEMORY_POINTER, mem);
  834. SDL_SetNumberProperty(props, SDL_PROP_IOSTREAM_MEMORY_SIZE_NUMBER, size);
  835. }
  836. }
  837. return iostr;
  838. }
  839. SDL_IOStream *SDL_IOFromConstMem(const void *mem, size_t size)
  840. {
  841. if (!mem) {
  842. SDL_InvalidParamError("mem");
  843. return NULL;
  844. } else if (!size) {
  845. SDL_InvalidParamError("size");
  846. return NULL;
  847. }
  848. IOStreamMemData *iodata = (IOStreamMemData *) SDL_calloc(1, sizeof (*iodata));
  849. if (!iodata) {
  850. return NULL;
  851. }
  852. SDL_IOStreamInterface iface;
  853. SDL_INIT_INTERFACE(&iface);
  854. iface.size = mem_size;
  855. iface.seek = mem_seek;
  856. iface.read = mem_read;
  857. // leave iface.write as NULL.
  858. iface.close = mem_close;
  859. iodata->base = (Uint8 *)mem;
  860. iodata->here = iodata->base;
  861. iodata->stop = iodata->base + size;
  862. SDL_IOStream *iostr = SDL_OpenIO(&iface, iodata);
  863. if (!iostr) {
  864. SDL_free(iodata);
  865. } else {
  866. const SDL_PropertiesID props = SDL_GetIOProperties(iostr);
  867. if (props) {
  868. SDL_SetPointerProperty(props, SDL_PROP_IOSTREAM_MEMORY_POINTER, (void *)mem);
  869. SDL_SetNumberProperty(props, SDL_PROP_IOSTREAM_MEMORY_SIZE_NUMBER, size);
  870. }
  871. }
  872. return iostr;
  873. }
  874. typedef struct IOStreamDynamicMemData
  875. {
  876. SDL_IOStream *stream;
  877. IOStreamMemData data;
  878. Uint8 *end;
  879. } IOStreamDynamicMemData;
  880. static Sint64 SDLCALL dynamic_mem_size(void *userdata)
  881. {
  882. IOStreamDynamicMemData *iodata = (IOStreamDynamicMemData *) userdata;
  883. return mem_size(&iodata->data);
  884. }
  885. static Sint64 SDLCALL dynamic_mem_seek(void *userdata, Sint64 offset, SDL_IOWhence whence)
  886. {
  887. IOStreamDynamicMemData *iodata = (IOStreamDynamicMemData *) userdata;
  888. return mem_seek(&iodata->data, offset, whence);
  889. }
  890. static size_t SDLCALL dynamic_mem_read(void *userdata, void *ptr, size_t size, SDL_IOStatus *status)
  891. {
  892. IOStreamDynamicMemData *iodata = (IOStreamDynamicMemData *) userdata;
  893. return mem_io(&iodata->data, ptr, iodata->data.here, size);
  894. }
  895. static bool dynamic_mem_realloc(IOStreamDynamicMemData *iodata, size_t size)
  896. {
  897. size_t chunksize = (size_t)SDL_GetNumberProperty(SDL_GetIOProperties(iodata->stream), SDL_PROP_IOSTREAM_DYNAMIC_CHUNKSIZE_NUMBER, 0);
  898. if (!chunksize) {
  899. chunksize = 1024;
  900. }
  901. // We're intentionally allocating more memory than needed so it can be null terminated
  902. size_t chunks = (((iodata->end - iodata->data.base) + size) / chunksize) + 1;
  903. size_t length = (chunks * chunksize);
  904. Uint8 *base = (Uint8 *)SDL_realloc(iodata->data.base, length);
  905. if (!base) {
  906. return false;
  907. }
  908. size_t here_offset = (iodata->data.here - iodata->data.base);
  909. size_t stop_offset = (iodata->data.stop - iodata->data.base);
  910. iodata->data.base = base;
  911. iodata->data.here = base + here_offset;
  912. iodata->data.stop = base + stop_offset;
  913. iodata->end = base + length;
  914. return SDL_SetPointerProperty(SDL_GetIOProperties(iodata->stream), SDL_PROP_IOSTREAM_DYNAMIC_MEMORY_POINTER, base);
  915. }
  916. static size_t SDLCALL dynamic_mem_write(void *userdata, const void *ptr, size_t size, SDL_IOStatus *status)
  917. {
  918. IOStreamDynamicMemData *iodata = (IOStreamDynamicMemData *) userdata;
  919. if (size > (size_t)(iodata->data.stop - iodata->data.here)) {
  920. if (size > (size_t)(iodata->end - iodata->data.here)) {
  921. if (!dynamic_mem_realloc(iodata, size)) {
  922. return 0;
  923. }
  924. }
  925. iodata->data.stop = iodata->data.here + size;
  926. }
  927. return mem_io(&iodata->data, iodata->data.here, ptr, size);
  928. }
  929. static bool SDLCALL dynamic_mem_close(void *userdata)
  930. {
  931. const IOStreamDynamicMemData *iodata = (IOStreamDynamicMemData *) userdata;
  932. void *mem = SDL_GetPointerProperty(SDL_GetIOProperties(iodata->stream), SDL_PROP_IOSTREAM_DYNAMIC_MEMORY_POINTER, NULL);
  933. if (mem) {
  934. SDL_free(mem);
  935. }
  936. SDL_free(userdata);
  937. return true;
  938. }
  939. SDL_IOStream *SDL_IOFromDynamicMem(void)
  940. {
  941. IOStreamDynamicMemData *iodata = (IOStreamDynamicMemData *) SDL_calloc(1, sizeof (*iodata));
  942. if (!iodata) {
  943. return NULL;
  944. }
  945. SDL_IOStreamInterface iface;
  946. SDL_INIT_INTERFACE(&iface);
  947. iface.size = dynamic_mem_size;
  948. iface.seek = dynamic_mem_seek;
  949. iface.read = dynamic_mem_read;
  950. iface.write = dynamic_mem_write;
  951. iface.close = dynamic_mem_close;
  952. SDL_IOStream *iostr = SDL_OpenIO(&iface, iodata);
  953. if (iostr) {
  954. iodata->stream = iostr;
  955. } else {
  956. SDL_free(iodata);
  957. }
  958. return iostr;
  959. }
  960. SDL_IOStatus SDL_GetIOStatus(SDL_IOStream *context)
  961. {
  962. if (!context) {
  963. SDL_InvalidParamError("context");
  964. return SDL_IO_STATUS_ERROR;
  965. }
  966. return context->status;
  967. }
  968. SDL_IOStream *SDL_OpenIO(const SDL_IOStreamInterface *iface, void *userdata)
  969. {
  970. if (!iface) {
  971. SDL_InvalidParamError("iface");
  972. return NULL;
  973. }
  974. if (iface->version < sizeof(*iface)) {
  975. // Update this to handle older versions of this interface
  976. SDL_SetError("Invalid interface, should be initialized with SDL_INIT_INTERFACE()");
  977. return NULL;
  978. }
  979. SDL_IOStream *iostr = (SDL_IOStream *)SDL_calloc(1, sizeof(*iostr));
  980. if (iostr) {
  981. SDL_copyp(&iostr->iface, iface);
  982. iostr->userdata = userdata;
  983. }
  984. return iostr;
  985. }
  986. bool SDL_CloseIO(SDL_IOStream *iostr)
  987. {
  988. bool result = true;
  989. if (iostr) {
  990. if (iostr->iface.close) {
  991. result = iostr->iface.close(iostr->userdata);
  992. }
  993. SDL_DestroyProperties(iostr->props);
  994. SDL_free(iostr);
  995. }
  996. return result;
  997. }
  998. // Load all the data from an SDL data stream
  999. void *SDL_LoadFile_IO(SDL_IOStream *src, size_t *datasize, bool closeio)
  1000. {
  1001. const int FILE_CHUNK_SIZE = 1024;
  1002. Sint64 size, size_total = 0;
  1003. size_t size_read;
  1004. char *data = NULL, *newdata;
  1005. bool loading_chunks = false;
  1006. if (!src) {
  1007. SDL_InvalidParamError("src");
  1008. goto done;
  1009. }
  1010. size = SDL_GetIOSize(src);
  1011. if (size < 0) {
  1012. size = FILE_CHUNK_SIZE;
  1013. loading_chunks = true;
  1014. }
  1015. if (size >= SDL_SIZE_MAX - 1) {
  1016. goto done;
  1017. }
  1018. data = (char *)SDL_malloc((size_t)(size + 1));
  1019. if (!data) {
  1020. goto done;
  1021. }
  1022. size_total = 0;
  1023. for (;;) {
  1024. if (loading_chunks) {
  1025. if ((size_total + FILE_CHUNK_SIZE) > size) {
  1026. size = (size_total + FILE_CHUNK_SIZE);
  1027. if (size >= SDL_SIZE_MAX - 1) {
  1028. newdata = NULL;
  1029. } else {
  1030. newdata = (char *)SDL_realloc(data, (size_t)(size + 1));
  1031. }
  1032. if (!newdata) {
  1033. SDL_free(data);
  1034. data = NULL;
  1035. goto done;
  1036. }
  1037. data = newdata;
  1038. }
  1039. }
  1040. size_read = SDL_ReadIO(src, data + size_total, (size_t)(size - size_total));
  1041. if (size_read > 0) {
  1042. size_total += size_read;
  1043. continue;
  1044. } else if (SDL_GetIOStatus(src) == SDL_IO_STATUS_NOT_READY) {
  1045. // Wait for the stream to be ready
  1046. SDL_Delay(1);
  1047. continue;
  1048. }
  1049. // The stream status will remain set for the caller to check
  1050. break;
  1051. }
  1052. data[size_total] = '\0';
  1053. done:
  1054. if (datasize) {
  1055. *datasize = (size_t)size_total;
  1056. }
  1057. if (closeio && src) {
  1058. SDL_CloseIO(src);
  1059. }
  1060. return data;
  1061. }
  1062. void *SDL_LoadFile(const char *file, size_t *datasize)
  1063. {
  1064. SDL_IOStream *stream = SDL_IOFromFile(file, "rb");
  1065. if (!stream) {
  1066. if (datasize) {
  1067. *datasize = 0;
  1068. }
  1069. return NULL;
  1070. }
  1071. return SDL_LoadFile_IO(stream, datasize, true);
  1072. }
  1073. bool SDL_SaveFile_IO(SDL_IOStream *src, const void *data, size_t datasize, bool closeio)
  1074. {
  1075. size_t size_written = 0;
  1076. size_t size_total = 0;
  1077. bool success = true;
  1078. if (!src) {
  1079. SDL_InvalidParamError("src");
  1080. goto done;
  1081. }
  1082. if (!data && datasize > 0) {
  1083. SDL_InvalidParamError("data");
  1084. goto done;
  1085. }
  1086. if (datasize > 0) {
  1087. while (size_total < datasize) {
  1088. size_written = SDL_WriteIO(src, ((const char *) data) + size_written, datasize - size_written);
  1089. if (size_written <= 0) {
  1090. if (SDL_GetIOStatus(src) == SDL_IO_STATUS_NOT_READY) {
  1091. // Wait for the stream to be ready
  1092. SDL_Delay(1);
  1093. continue;
  1094. } else {
  1095. success = false;
  1096. goto done;
  1097. }
  1098. }
  1099. size_total += size_written;
  1100. }
  1101. }
  1102. done:
  1103. if (closeio && src) {
  1104. SDL_CloseIO(src);
  1105. }
  1106. return success;
  1107. }
  1108. bool SDL_SaveFile(const char *file, const void *data, size_t datasize)
  1109. {
  1110. SDL_IOStream *stream = SDL_IOFromFile(file, "wb");
  1111. if (!stream) {
  1112. return false;
  1113. }
  1114. return SDL_SaveFile_IO(stream, data, datasize, true);
  1115. }
  1116. SDL_PropertiesID SDL_GetIOProperties(SDL_IOStream *context)
  1117. {
  1118. if (!context) {
  1119. SDL_InvalidParamError("context");
  1120. return 0;
  1121. }
  1122. if (context->props == 0) {
  1123. context->props = SDL_CreateProperties();
  1124. }
  1125. return context->props;
  1126. }
  1127. Sint64 SDL_GetIOSize(SDL_IOStream *context)
  1128. {
  1129. if (!context) {
  1130. return SDL_InvalidParamError("context");
  1131. }
  1132. if (!context->iface.size) {
  1133. Sint64 pos, size;
  1134. pos = SDL_SeekIO(context, 0, SDL_IO_SEEK_CUR);
  1135. if (pos < 0) {
  1136. return -1;
  1137. }
  1138. size = SDL_SeekIO(context, 0, SDL_IO_SEEK_END);
  1139. SDL_SeekIO(context, pos, SDL_IO_SEEK_SET);
  1140. return size;
  1141. }
  1142. return context->iface.size(context->userdata);
  1143. }
  1144. Sint64 SDL_SeekIO(SDL_IOStream *context, Sint64 offset, SDL_IOWhence whence)
  1145. {
  1146. if (!context) {
  1147. SDL_InvalidParamError("context");
  1148. return -1;
  1149. } else if (!context->iface.seek) {
  1150. SDL_Unsupported();
  1151. return -1;
  1152. }
  1153. return context->iface.seek(context->userdata, offset, whence);
  1154. }
  1155. Sint64 SDL_TellIO(SDL_IOStream *context)
  1156. {
  1157. return SDL_SeekIO(context, 0, SDL_IO_SEEK_CUR);
  1158. }
  1159. size_t SDL_ReadIO(SDL_IOStream *context, void *ptr, size_t size)
  1160. {
  1161. size_t bytes;
  1162. if (!context) {
  1163. SDL_InvalidParamError("context");
  1164. return 0;
  1165. } else if (!context->iface.read) {
  1166. context->status = SDL_IO_STATUS_WRITEONLY;
  1167. SDL_Unsupported();
  1168. return 0;
  1169. }
  1170. context->status = SDL_IO_STATUS_READY;
  1171. SDL_ClearError();
  1172. if (size == 0) {
  1173. return 0;
  1174. }
  1175. bytes = context->iface.read(context->userdata, ptr, size, &context->status);
  1176. if (bytes == 0 && context->status == SDL_IO_STATUS_READY) {
  1177. if (*SDL_GetError()) {
  1178. context->status = SDL_IO_STATUS_ERROR;
  1179. } else {
  1180. context->status = SDL_IO_STATUS_EOF;
  1181. }
  1182. }
  1183. return bytes;
  1184. }
  1185. size_t SDL_WriteIO(SDL_IOStream *context, const void *ptr, size_t size)
  1186. {
  1187. size_t bytes;
  1188. if (!context) {
  1189. SDL_InvalidParamError("context");
  1190. return 0;
  1191. } else if (!context->iface.write) {
  1192. context->status = SDL_IO_STATUS_READONLY;
  1193. SDL_Unsupported();
  1194. return 0;
  1195. }
  1196. context->status = SDL_IO_STATUS_READY;
  1197. SDL_ClearError();
  1198. if (size == 0) {
  1199. return 0;
  1200. }
  1201. bytes = context->iface.write(context->userdata, ptr, size, &context->status);
  1202. if ((bytes == 0) && (context->status == SDL_IO_STATUS_READY)) {
  1203. context->status = SDL_IO_STATUS_ERROR;
  1204. }
  1205. return bytes;
  1206. }
  1207. size_t SDL_IOprintf(SDL_IOStream *context, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
  1208. {
  1209. va_list ap;
  1210. int size;
  1211. char *string;
  1212. size_t bytes;
  1213. va_start(ap, fmt);
  1214. size = SDL_vasprintf(&string, fmt, ap);
  1215. va_end(ap);
  1216. if (size < 0) {
  1217. return 0;
  1218. }
  1219. bytes = SDL_WriteIO(context, string, (size_t)size);
  1220. SDL_free(string);
  1221. return bytes;
  1222. }
  1223. size_t SDL_IOvprintf(SDL_IOStream *context, SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap)
  1224. {
  1225. int size;
  1226. char *string;
  1227. size_t bytes;
  1228. size = SDL_vasprintf(&string, fmt, ap);
  1229. if (size < 0) {
  1230. return 0;
  1231. }
  1232. bytes = SDL_WriteIO(context, string, (size_t)size);
  1233. SDL_free(string);
  1234. return bytes;
  1235. }
  1236. bool SDL_FlushIO(SDL_IOStream *context)
  1237. {
  1238. bool result = true;
  1239. if (!context) {
  1240. return SDL_InvalidParamError("context");
  1241. }
  1242. context->status = SDL_IO_STATUS_READY;
  1243. SDL_ClearError();
  1244. if (context->iface.flush) {
  1245. result = context->iface.flush(context->userdata, &context->status);
  1246. }
  1247. if (!result && (context->status == SDL_IO_STATUS_READY)) {
  1248. context->status = SDL_IO_STATUS_ERROR;
  1249. }
  1250. return result;
  1251. }
  1252. // Functions for dynamically reading and writing endian-specific values
  1253. bool SDL_ReadU8(SDL_IOStream *src, Uint8 *value)
  1254. {
  1255. Uint8 data = 0;
  1256. bool result = false;
  1257. if (SDL_ReadIO(src, &data, sizeof(data)) == sizeof(data)) {
  1258. result = true;
  1259. }
  1260. if (value) {
  1261. *value = data;
  1262. }
  1263. return result;
  1264. }
  1265. bool SDL_ReadS8(SDL_IOStream *src, Sint8 *value)
  1266. {
  1267. Sint8 data = 0;
  1268. bool result = false;
  1269. if (SDL_ReadIO(src, &data, sizeof(data)) == sizeof(data)) {
  1270. result = true;
  1271. }
  1272. if (value) {
  1273. *value = data;
  1274. }
  1275. return result;
  1276. }
  1277. bool SDL_ReadU16LE(SDL_IOStream *src, Uint16 *value)
  1278. {
  1279. Uint16 data = 0;
  1280. bool result = false;
  1281. if (SDL_ReadIO(src, &data, sizeof(data)) == sizeof(data)) {
  1282. result = true;
  1283. }
  1284. if (value) {
  1285. *value = SDL_Swap16LE(data);
  1286. }
  1287. return result;
  1288. }
  1289. bool SDL_ReadS16LE(SDL_IOStream *src, Sint16 *value)
  1290. {
  1291. return SDL_ReadU16LE(src, (Uint16 *)value);
  1292. }
  1293. bool SDL_ReadU16BE(SDL_IOStream *src, Uint16 *value)
  1294. {
  1295. Uint16 data = 0;
  1296. bool result = false;
  1297. if (SDL_ReadIO(src, &data, sizeof(data)) == sizeof(data)) {
  1298. result = true;
  1299. }
  1300. if (value) {
  1301. *value = SDL_Swap16BE(data);
  1302. }
  1303. return result;
  1304. }
  1305. bool SDL_ReadS16BE(SDL_IOStream *src, Sint16 *value)
  1306. {
  1307. return SDL_ReadU16BE(src, (Uint16 *)value);
  1308. }
  1309. bool SDL_ReadU32LE(SDL_IOStream *src, Uint32 *value)
  1310. {
  1311. Uint32 data = 0;
  1312. bool result = false;
  1313. if (SDL_ReadIO(src, &data, sizeof(data)) == sizeof(data)) {
  1314. result = true;
  1315. }
  1316. if (value) {
  1317. *value = SDL_Swap32LE(data);
  1318. }
  1319. return result;
  1320. }
  1321. bool SDL_ReadS32LE(SDL_IOStream *src, Sint32 *value)
  1322. {
  1323. return SDL_ReadU32LE(src, (Uint32 *)value);
  1324. }
  1325. bool SDL_ReadU32BE(SDL_IOStream *src, Uint32 *value)
  1326. {
  1327. Uint32 data = 0;
  1328. bool result = false;
  1329. if (SDL_ReadIO(src, &data, sizeof(data)) == sizeof(data)) {
  1330. result = true;
  1331. }
  1332. if (value) {
  1333. *value = SDL_Swap32BE(data);
  1334. }
  1335. return result;
  1336. }
  1337. bool SDL_ReadS32BE(SDL_IOStream *src, Sint32 *value)
  1338. {
  1339. return SDL_ReadU32BE(src, (Uint32 *)value);
  1340. }
  1341. bool SDL_ReadU64LE(SDL_IOStream *src, Uint64 *value)
  1342. {
  1343. Uint64 data = 0;
  1344. bool result = false;
  1345. if (SDL_ReadIO(src, &data, sizeof(data)) == sizeof(data)) {
  1346. result = true;
  1347. }
  1348. if (value) {
  1349. *value = SDL_Swap64LE(data);
  1350. }
  1351. return result;
  1352. }
  1353. bool SDL_ReadS64LE(SDL_IOStream *src, Sint64 *value)
  1354. {
  1355. return SDL_ReadU64LE(src, (Uint64 *)value);
  1356. }
  1357. bool SDL_ReadU64BE(SDL_IOStream *src, Uint64 *value)
  1358. {
  1359. Uint64 data = 0;
  1360. bool result = false;
  1361. if (SDL_ReadIO(src, &data, sizeof(data)) == sizeof(data)) {
  1362. result = true;
  1363. }
  1364. if (value) {
  1365. *value = SDL_Swap64BE(data);
  1366. }
  1367. return result;
  1368. }
  1369. bool SDL_ReadS64BE(SDL_IOStream *src, Sint64 *value)
  1370. {
  1371. return SDL_ReadU64BE(src, (Uint64 *)value);
  1372. }
  1373. bool SDL_WriteU8(SDL_IOStream *dst, Uint8 value)
  1374. {
  1375. return (SDL_WriteIO(dst, &value, sizeof(value)) == sizeof(value));
  1376. }
  1377. bool SDL_WriteS8(SDL_IOStream *dst, Sint8 value)
  1378. {
  1379. return (SDL_WriteIO(dst, &value, sizeof(value)) == sizeof(value));
  1380. }
  1381. bool SDL_WriteU16LE(SDL_IOStream *dst, Uint16 value)
  1382. {
  1383. const Uint16 swapped = SDL_Swap16LE(value);
  1384. return (SDL_WriteIO(dst, &swapped, sizeof(swapped)) == sizeof(swapped));
  1385. }
  1386. bool SDL_WriteS16LE(SDL_IOStream *dst, Sint16 value)
  1387. {
  1388. return SDL_WriteU16LE(dst, (Uint16)value);
  1389. }
  1390. bool SDL_WriteU16BE(SDL_IOStream *dst, Uint16 value)
  1391. {
  1392. const Uint16 swapped = SDL_Swap16BE(value);
  1393. return (SDL_WriteIO(dst, &swapped, sizeof(swapped)) == sizeof(swapped));
  1394. }
  1395. bool SDL_WriteS16BE(SDL_IOStream *dst, Sint16 value)
  1396. {
  1397. return SDL_WriteU16BE(dst, (Uint16)value);
  1398. }
  1399. bool SDL_WriteU32LE(SDL_IOStream *dst, Uint32 value)
  1400. {
  1401. const Uint32 swapped = SDL_Swap32LE(value);
  1402. return (SDL_WriteIO(dst, &swapped, sizeof(swapped)) == sizeof(swapped));
  1403. }
  1404. bool SDL_WriteS32LE(SDL_IOStream *dst, Sint32 value)
  1405. {
  1406. return SDL_WriteU32LE(dst, (Uint32)value);
  1407. }
  1408. bool SDL_WriteU32BE(SDL_IOStream *dst, Uint32 value)
  1409. {
  1410. const Uint32 swapped = SDL_Swap32BE(value);
  1411. return (SDL_WriteIO(dst, &swapped, sizeof(swapped)) == sizeof(swapped));
  1412. }
  1413. bool SDL_WriteS32BE(SDL_IOStream *dst, Sint32 value)
  1414. {
  1415. return SDL_WriteU32BE(dst, (Uint32)value);
  1416. }
  1417. bool SDL_WriteU64LE(SDL_IOStream *dst, Uint64 value)
  1418. {
  1419. const Uint64 swapped = SDL_Swap64LE(value);
  1420. return (SDL_WriteIO(dst, &swapped, sizeof(swapped)) == sizeof(swapped));
  1421. }
  1422. bool SDL_WriteS64LE(SDL_IOStream *dst, Sint64 value)
  1423. {
  1424. return SDL_WriteU64LE(dst, (Uint64)value);
  1425. }
  1426. bool SDL_WriteU64BE(SDL_IOStream *dst, Uint64 value)
  1427. {
  1428. const Uint64 swapped = SDL_Swap64BE(value);
  1429. return (SDL_WriteIO(dst, &swapped, sizeof(swapped)) == sizeof(swapped));
  1430. }
  1431. bool SDL_WriteS64BE(SDL_IOStream *dst, Sint64 value)
  1432. {
  1433. return SDL_WriteU64BE(dst, (Uint64)value);
  1434. }