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