Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/sdl/io/SDL_iostream.c
21827 views
1
/*
2
Simple DirectMedia Layer
3
Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
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
48
struct 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
67
typedef 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
85
static 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
147
static 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
159
static 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
193
static 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
259
static 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
292
static 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
301
static 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
315
SDL_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
364
typedef struct IOStreamFDData
365
{
366
int fd;
367
bool autoclose;
368
bool regular_file;
369
} IOStreamFDData;
370
371
static 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
385
static 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
412
static 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
431
static 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
450
static 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
// We get EINVAL when flushing a pipe, just make that a no-op
459
if (result < 0 && errno != EINVAL) {
460
return SDL_SetError("Error flushing datastream: %s", strerror(errno));
461
}
462
return true;
463
}
464
465
static bool SDLCALL fd_close(void *userdata)
466
{
467
IOStreamFDData *iodata = (IOStreamFDData *) userdata;
468
bool status = true;
469
if (iodata->autoclose) {
470
if (close(iodata->fd) < 0) {
471
status = SDL_SetError("Error closing datastream: %s", strerror(errno));
472
}
473
}
474
SDL_free(iodata);
475
return status;
476
}
477
478
SDL_IOStream *SDL_IOFromFD(int fd, bool autoclose)
479
{
480
IOStreamFDData *iodata = (IOStreamFDData *) SDL_calloc(1, sizeof (*iodata));
481
if (!iodata) {
482
if (autoclose) {
483
close(fd);
484
}
485
return NULL;
486
}
487
488
SDL_IOStreamInterface iface;
489
SDL_INIT_INTERFACE(&iface);
490
// There's no fd_size because SDL_GetIOSize emulates it the same way we'd do it for fd anyhow.
491
iface.seek = fd_seek;
492
iface.read = fd_read;
493
iface.write = fd_write;
494
iface.flush = fd_flush;
495
iface.close = fd_close;
496
497
iodata->fd = fd;
498
iodata->autoclose = autoclose;
499
500
struct stat st;
501
iodata->regular_file = ((fstat(fd, &st) == 0) && S_ISREG(st.st_mode));
502
503
SDL_IOStream *iostr = SDL_OpenIO(&iface, iodata);
504
if (!iostr) {
505
iface.close(iodata);
506
} else {
507
const SDL_PropertiesID props = SDL_GetIOProperties(iostr);
508
if (props) {
509
SDL_SetNumberProperty(props, SDL_PROP_IOSTREAM_FILE_DESCRIPTOR_NUMBER, fd);
510
}
511
}
512
513
return iostr;
514
}
515
#endif // !defined(SDL_PLATFORM_WINDOWS)
516
517
#if defined(HAVE_STDIO_H) && !defined(SDL_PLATFORM_WINDOWS)
518
519
// Functions to read/write stdio file pointers. Not used for windows.
520
521
typedef struct IOStreamStdioData
522
{
523
FILE *fp;
524
bool autoclose;
525
bool regular_file;
526
} IOStreamStdioData;
527
528
#ifdef HAVE_FOPEN64
529
#define fopen fopen64
530
#endif
531
#ifdef HAVE_FSEEKO64
532
#define fseek_off_t off64_t
533
#define fseek fseeko64
534
#define ftell ftello64
535
#elif defined(HAVE_FSEEKO)
536
#if defined(OFF_MIN) && defined(OFF_MAX)
537
#define FSEEK_OFF_MIN OFF_MIN
538
#define FSEEK_OFF_MAX OFF_MAX
539
#elif defined(HAVE_LIMITS_H)
540
/* POSIX doesn't specify the minimum and maximum macros for off_t so
541
* we have to improvise and dance around implementation-defined
542
* behavior. This may fail if the off_t type has padding bits or
543
* is not a two's-complement representation. The compilers will detect
544
* and eliminate the dead code if off_t has 64 bits.
545
*/
546
#define FSEEK_OFF_MAX (((((off_t)1 << (sizeof(off_t) * CHAR_BIT - 2)) - 1) << 1) + 1)
547
#define FSEEK_OFF_MIN (-(FSEEK_OFF_MAX)-1)
548
#endif
549
#define fseek_off_t off_t
550
#define fseek fseeko
551
#define ftell ftello
552
#elif defined(HAVE__FSEEKI64)
553
#define fseek_off_t __int64
554
#define fseek _fseeki64
555
#define ftell _ftelli64
556
#else
557
#ifdef HAVE_LIMITS_H
558
#define FSEEK_OFF_MIN LONG_MIN
559
#define FSEEK_OFF_MAX LONG_MAX
560
#endif
561
#define fseek_off_t long
562
#endif
563
564
static Sint64 SDLCALL stdio_seek(void *userdata, Sint64 offset, SDL_IOWhence whence)
565
{
566
IOStreamStdioData *iodata = (IOStreamStdioData *) userdata;
567
int stdiowhence;
568
569
switch (whence) {
570
case SDL_IO_SEEK_SET:
571
stdiowhence = SEEK_SET;
572
break;
573
case SDL_IO_SEEK_CUR:
574
stdiowhence = SEEK_CUR;
575
break;
576
case SDL_IO_SEEK_END:
577
stdiowhence = SEEK_END;
578
break;
579
default:
580
SDL_SetError("Unknown value for 'whence'");
581
return -1;
582
}
583
584
#if defined(FSEEK_OFF_MIN) && defined(FSEEK_OFF_MAX)
585
if (offset < (Sint64)(FSEEK_OFF_MIN) || offset > (Sint64)(FSEEK_OFF_MAX)) {
586
SDL_SetError("Seek offset out of range");
587
return -1;
588
}
589
#endif
590
591
// don't make a possibly-costly API call for the noop seek from SDL_TellIO
592
const bool is_noop = (whence == SDL_IO_SEEK_CUR) && (offset == 0);
593
594
if (is_noop || fseek(iodata->fp, (fseek_off_t)offset, stdiowhence) == 0) {
595
const Sint64 pos = ftell(iodata->fp);
596
if (pos < 0) {
597
SDL_SetError("Couldn't get stream offset: %s", strerror(errno));
598
return -1;
599
}
600
return pos;
601
}
602
SDL_SetError("Error seeking in datastream: %s", strerror(errno));
603
return -1;
604
}
605
606
static size_t SDLCALL stdio_read(void *userdata, void *ptr, size_t size, SDL_IOStatus *status)
607
{
608
IOStreamStdioData *iodata = (IOStreamStdioData *) userdata;
609
const size_t bytes = fread(ptr, 1, size, iodata->fp);
610
if (bytes == 0 && ferror(iodata->fp)) {
611
if (errno == EAGAIN) {
612
*status = SDL_IO_STATUS_NOT_READY;
613
clearerr(iodata->fp);
614
} else {
615
SDL_SetError("Error reading from datastream: %s", strerror(errno));
616
}
617
}
618
return bytes;
619
}
620
621
static size_t SDLCALL stdio_write(void *userdata, const void *ptr, size_t size, SDL_IOStatus *status)
622
{
623
IOStreamStdioData *iodata = (IOStreamStdioData *) userdata;
624
const size_t bytes = fwrite(ptr, 1, size, iodata->fp);
625
if (bytes == 0 && ferror(iodata->fp)) {
626
if (errno == EAGAIN) {
627
*status = SDL_IO_STATUS_NOT_READY;
628
clearerr(iodata->fp);
629
} else {
630
SDL_SetError("Error writing to datastream: %s", strerror(errno));
631
}
632
}
633
return bytes;
634
}
635
636
static bool SDLCALL stdio_flush(void *userdata, SDL_IOStatus *status)
637
{
638
IOStreamStdioData *iodata = (IOStreamStdioData *) userdata;
639
if (fflush(iodata->fp) != 0) {
640
if (errno == EAGAIN) {
641
*status = SDL_IO_STATUS_NOT_READY;
642
return false;
643
} else {
644
return SDL_SetError("Error flushing datastream: %s", strerror(errno));
645
}
646
}
647
648
int result;
649
int fd = fileno(iodata->fp);
650
do {
651
result = SDL_fdatasync(fd);
652
} while (result < 0 && errno == EINTR);
653
654
if (result < 0) {
655
return SDL_SetError("Error flushing datastream: %s", strerror(errno));
656
}
657
return true;
658
}
659
660
static bool SDLCALL stdio_close(void *userdata)
661
{
662
IOStreamStdioData *iodata = (IOStreamStdioData *) userdata;
663
bool status = true;
664
if (iodata->autoclose) {
665
if (fclose(iodata->fp) != 0) {
666
status = SDL_SetError("Error closing datastream: %s", strerror(errno));
667
}
668
}
669
SDL_free(iodata);
670
return status;
671
}
672
673
SDL_IOStream *SDL_IOFromFP(FILE *fp, bool autoclose)
674
{
675
IOStreamStdioData *iodata = (IOStreamStdioData *) SDL_calloc(1, sizeof (*iodata));
676
if (!iodata) {
677
if (autoclose) {
678
fclose(fp);
679
}
680
return NULL;
681
}
682
683
SDL_IOStreamInterface iface;
684
SDL_INIT_INTERFACE(&iface);
685
// There's no stdio_size because SDL_GetIOSize emulates it the same way we'd do it for stdio anyhow.
686
iface.seek = stdio_seek;
687
iface.read = stdio_read;
688
iface.write = stdio_write;
689
iface.flush = stdio_flush;
690
iface.close = stdio_close;
691
692
iodata->fp = fp;
693
iodata->autoclose = autoclose;
694
695
struct stat st;
696
iodata->regular_file = ((fstat(fileno(fp), &st) == 0) && S_ISREG(st.st_mode));
697
698
SDL_IOStream *iostr = SDL_OpenIO(&iface, iodata);
699
if (!iostr) {
700
iface.close(iodata);
701
} else {
702
const SDL_PropertiesID props = SDL_GetIOProperties(iostr);
703
if (props) {
704
SDL_SetPointerProperty(props, SDL_PROP_IOSTREAM_STDIO_FILE_POINTER, fp);
705
SDL_SetNumberProperty(props, SDL_PROP_IOSTREAM_FILE_DESCRIPTOR_NUMBER, fileno(fp));
706
}
707
}
708
709
return iostr;
710
}
711
#endif // !HAVE_STDIO_H && !defined(SDL_PLATFORM_WINDOWS)
712
713
// Functions to read/write memory pointers
714
715
typedef struct IOStreamMemData
716
{
717
Uint8 *base;
718
Uint8 *here;
719
Uint8 *stop;
720
} IOStreamMemData;
721
722
static Sint64 SDLCALL mem_size(void *userdata)
723
{
724
const IOStreamMemData *iodata = (IOStreamMemData *) userdata;
725
return (iodata->stop - iodata->base);
726
}
727
728
static Sint64 SDLCALL mem_seek(void *userdata, Sint64 offset, SDL_IOWhence whence)
729
{
730
IOStreamMemData *iodata = (IOStreamMemData *) userdata;
731
Uint8 *newpos;
732
733
switch (whence) {
734
case SDL_IO_SEEK_SET:
735
newpos = iodata->base + offset;
736
break;
737
case SDL_IO_SEEK_CUR:
738
newpos = iodata->here + offset;
739
break;
740
case SDL_IO_SEEK_END:
741
newpos = iodata->stop + offset;
742
break;
743
default:
744
SDL_SetError("Unknown value for 'whence'");
745
return -1;
746
}
747
if (newpos < iodata->base) {
748
newpos = iodata->base;
749
}
750
if (newpos > iodata->stop) {
751
newpos = iodata->stop;
752
}
753
iodata->here = newpos;
754
return (Sint64)(iodata->here - iodata->base);
755
}
756
757
static size_t mem_io(void *userdata, void *dst, const void *src, size_t size)
758
{
759
IOStreamMemData *iodata = (IOStreamMemData *) userdata;
760
const size_t mem_available = (iodata->stop - iodata->here);
761
if (size > mem_available) {
762
size = mem_available;
763
}
764
SDL_memcpy(dst, src, size);
765
iodata->here += size;
766
return size;
767
}
768
769
static size_t SDLCALL mem_read(void *userdata, void *ptr, size_t size, SDL_IOStatus *status)
770
{
771
IOStreamMemData *iodata = (IOStreamMemData *) userdata;
772
return mem_io(userdata, ptr, iodata->here, size);
773
}
774
775
static size_t SDLCALL mem_write(void *userdata, const void *ptr, size_t size, SDL_IOStatus *status)
776
{
777
IOStreamMemData *iodata = (IOStreamMemData *) userdata;
778
return mem_io(userdata, iodata->here, ptr, size);
779
}
780
781
static bool SDLCALL mem_close(void *userdata)
782
{
783
SDL_free(userdata);
784
return true;
785
}
786
787
// Functions to create SDL_IOStream structures from various data sources
788
789
#if defined(HAVE_STDIO_H) && !defined(SDL_PLATFORM_WINDOWS)
790
static bool IsRegularFileOrPipe(FILE *f)
791
{
792
#ifndef SDL_PLATFORM_EMSCRIPTEN
793
struct stat st;
794
if (fstat(fileno(f), &st) < 0 || !(S_ISREG(st.st_mode) || S_ISFIFO(st.st_mode))) {
795
return false;
796
}
797
#endif // !SDL_PLATFORM_EMSCRIPTEN
798
799
return true;
800
}
801
#endif
802
803
SDL_IOStream *SDL_IOFromFile(const char *file, const char *mode)
804
{
805
SDL_IOStream *iostr = NULL;
806
807
if (!file || !*file) {
808
SDL_InvalidParamError("file");
809
return NULL;
810
}
811
if (!mode || !*mode) {
812
SDL_InvalidParamError("mode");
813
return NULL;
814
}
815
816
#ifdef SDL_PLATFORM_ANDROID
817
#ifdef HAVE_STDIO_H
818
// Try to open the file on the filesystem first
819
if (*file == '/') {
820
FILE *fp = fopen(file, mode);
821
if (fp) {
822
if (!IsRegularFileOrPipe(fp)) {
823
fclose(fp);
824
SDL_SetError("%s is not a regular file or pipe", file);
825
return NULL;
826
}
827
return SDL_IOFromFP(fp, true);
828
}
829
} else if (SDL_strncmp(file, "content://", 10) == 0) {
830
// Try opening content:// URI
831
int fd = Android_JNI_OpenFileDescriptor(file, mode);
832
if (fd == -1) {
833
// SDL error is already set.
834
return NULL;
835
}
836
837
FILE *fp = fdopen(fd, mode);
838
if (!fp) {
839
close(fd);
840
SDL_SetError("Unable to open file descriptor (%d) from URI %s: %s", fd, file, strerror(errno));
841
return NULL;
842
}
843
844
return SDL_IOFromFP(fp, true);
845
} else {
846
// Try opening it from internal storage if it's a relative path
847
char *path = NULL;
848
SDL_asprintf(&path, "%s/%s", SDL_GetAndroidInternalStoragePath(), file);
849
if (path) {
850
FILE *fp = fopen(path, mode);
851
SDL_free(path);
852
if (fp) {
853
if (!IsRegularFileOrPipe(fp)) {
854
fclose(fp);
855
SDL_SetError("%s is not a regular file or pipe", path);
856
return NULL;
857
}
858
return SDL_IOFromFP(fp, true);
859
}
860
}
861
}
862
#endif // HAVE_STDIO_H
863
864
// Try to open the file from the asset system
865
866
void *iodata = NULL;
867
if (!Android_JNI_FileOpen(&iodata, file, mode)) {
868
return NULL;
869
}
870
871
SDL_IOStreamInterface iface;
872
SDL_INIT_INTERFACE(&iface);
873
iface.size = Android_JNI_FileSize;
874
iface.seek = Android_JNI_FileSeek;
875
iface.read = Android_JNI_FileRead;
876
iface.write = Android_JNI_FileWrite;
877
iface.close = Android_JNI_FileClose;
878
879
iostr = SDL_OpenIO(&iface, iodata);
880
if (!iostr) {
881
iface.close(iodata);
882
} else {
883
const SDL_PropertiesID props = SDL_GetIOProperties(iostr);
884
if (props) {
885
SDL_SetPointerProperty(props, SDL_PROP_IOSTREAM_ANDROID_AASSET_POINTER, iodata);
886
}
887
}
888
889
#elif defined(SDL_PLATFORM_WINDOWS)
890
HANDLE handle = windows_file_open(file, mode);
891
if (handle != INVALID_HANDLE_VALUE) {
892
iostr = SDL_IOFromHandle(handle, mode, true);
893
}
894
895
#elif defined(HAVE_STDIO_H)
896
{
897
#if defined(SDL_PLATFORM_3DS)
898
FILE *fp = N3DS_FileOpen(file, mode);
899
#else
900
FILE *fp = fopen(file, mode);
901
#endif
902
903
if (!fp) {
904
SDL_SetError("Couldn't open %s: %s", file, strerror(errno));
905
} else if (!IsRegularFileOrPipe(fp)) {
906
fclose(fp);
907
fp = NULL;
908
SDL_SetError("%s is not a regular file or pipe", file);
909
} else {
910
iostr = SDL_IOFromFP(fp, true);
911
}
912
}
913
914
#else
915
SDL_SetError("SDL not compiled with stdio support");
916
#endif // !HAVE_STDIO_H
917
918
return iostr;
919
}
920
921
SDL_IOStream *SDL_IOFromMem(void *mem, size_t size)
922
{
923
if (!mem) {
924
SDL_InvalidParamError("mem");
925
return NULL;
926
} else if (!size) {
927
SDL_InvalidParamError("size");
928
return NULL;
929
}
930
931
IOStreamMemData *iodata = (IOStreamMemData *) SDL_calloc(1, sizeof (*iodata));
932
if (!iodata) {
933
return NULL;
934
}
935
936
SDL_IOStreamInterface iface;
937
SDL_INIT_INTERFACE(&iface);
938
iface.size = mem_size;
939
iface.seek = mem_seek;
940
iface.read = mem_read;
941
iface.write = mem_write;
942
iface.close = mem_close;
943
944
iodata->base = (Uint8 *)mem;
945
iodata->here = iodata->base;
946
iodata->stop = iodata->base + size;
947
948
SDL_IOStream *iostr = SDL_OpenIO(&iface, iodata);
949
if (!iostr) {
950
SDL_free(iodata);
951
} else {
952
const SDL_PropertiesID props = SDL_GetIOProperties(iostr);
953
if (props) {
954
SDL_SetPointerProperty(props, SDL_PROP_IOSTREAM_MEMORY_POINTER, mem);
955
SDL_SetNumberProperty(props, SDL_PROP_IOSTREAM_MEMORY_SIZE_NUMBER, size);
956
}
957
}
958
return iostr;
959
}
960
961
SDL_IOStream *SDL_IOFromConstMem(const void *mem, size_t size)
962
{
963
if (!mem) {
964
SDL_InvalidParamError("mem");
965
return NULL;
966
} else if (!size) {
967
SDL_InvalidParamError("size");
968
return NULL;
969
}
970
971
IOStreamMemData *iodata = (IOStreamMemData *) SDL_calloc(1, sizeof (*iodata));
972
if (!iodata) {
973
return NULL;
974
}
975
976
SDL_IOStreamInterface iface;
977
SDL_INIT_INTERFACE(&iface);
978
iface.size = mem_size;
979
iface.seek = mem_seek;
980
iface.read = mem_read;
981
// leave iface.write as NULL.
982
iface.close = mem_close;
983
984
iodata->base = (Uint8 *)mem;
985
iodata->here = iodata->base;
986
iodata->stop = iodata->base + size;
987
988
SDL_IOStream *iostr = SDL_OpenIO(&iface, iodata);
989
if (!iostr) {
990
SDL_free(iodata);
991
} else {
992
const SDL_PropertiesID props = SDL_GetIOProperties(iostr);
993
if (props) {
994
SDL_SetPointerProperty(props, SDL_PROP_IOSTREAM_MEMORY_POINTER, (void *)mem);
995
SDL_SetNumberProperty(props, SDL_PROP_IOSTREAM_MEMORY_SIZE_NUMBER, size);
996
}
997
}
998
return iostr;
999
}
1000
1001
typedef struct IOStreamDynamicMemData
1002
{
1003
SDL_IOStream *stream;
1004
IOStreamMemData data;
1005
Uint8 *end;
1006
} IOStreamDynamicMemData;
1007
1008
static Sint64 SDLCALL dynamic_mem_size(void *userdata)
1009
{
1010
IOStreamDynamicMemData *iodata = (IOStreamDynamicMemData *) userdata;
1011
return mem_size(&iodata->data);
1012
}
1013
1014
static Sint64 SDLCALL dynamic_mem_seek(void *userdata, Sint64 offset, SDL_IOWhence whence)
1015
{
1016
IOStreamDynamicMemData *iodata = (IOStreamDynamicMemData *) userdata;
1017
return mem_seek(&iodata->data, offset, whence);
1018
}
1019
1020
static size_t SDLCALL dynamic_mem_read(void *userdata, void *ptr, size_t size, SDL_IOStatus *status)
1021
{
1022
IOStreamDynamicMemData *iodata = (IOStreamDynamicMemData *) userdata;
1023
return mem_io(&iodata->data, ptr, iodata->data.here, size);
1024
}
1025
1026
static bool dynamic_mem_realloc(IOStreamDynamicMemData *iodata, size_t size)
1027
{
1028
size_t chunksize = (size_t)SDL_GetNumberProperty(SDL_GetIOProperties(iodata->stream), SDL_PROP_IOSTREAM_DYNAMIC_CHUNKSIZE_NUMBER, 0);
1029
if (!chunksize) {
1030
chunksize = 1024;
1031
}
1032
1033
// We're intentionally allocating more memory than needed so it can be null terminated
1034
size_t chunks = (((iodata->end - iodata->data.base) + size) / chunksize) + 1;
1035
size_t length = (chunks * chunksize);
1036
Uint8 *base = (Uint8 *)SDL_realloc(iodata->data.base, length);
1037
if (!base) {
1038
return false;
1039
}
1040
1041
size_t here_offset = (iodata->data.here - iodata->data.base);
1042
size_t stop_offset = (iodata->data.stop - iodata->data.base);
1043
iodata->data.base = base;
1044
iodata->data.here = base + here_offset;
1045
iodata->data.stop = base + stop_offset;
1046
iodata->end = base + length;
1047
return SDL_SetPointerProperty(SDL_GetIOProperties(iodata->stream), SDL_PROP_IOSTREAM_DYNAMIC_MEMORY_POINTER, base);
1048
}
1049
1050
static size_t SDLCALL dynamic_mem_write(void *userdata, const void *ptr, size_t size, SDL_IOStatus *status)
1051
{
1052
IOStreamDynamicMemData *iodata = (IOStreamDynamicMemData *) userdata;
1053
if (size > (size_t)(iodata->data.stop - iodata->data.here)) {
1054
if (size > (size_t)(iodata->end - iodata->data.here)) {
1055
if (!dynamic_mem_realloc(iodata, size)) {
1056
return 0;
1057
}
1058
}
1059
iodata->data.stop = iodata->data.here + size;
1060
}
1061
return mem_io(&iodata->data, iodata->data.here, ptr, size);
1062
}
1063
1064
static bool SDLCALL dynamic_mem_close(void *userdata)
1065
{
1066
const IOStreamDynamicMemData *iodata = (IOStreamDynamicMemData *) userdata;
1067
void *mem = SDL_GetPointerProperty(SDL_GetIOProperties(iodata->stream), SDL_PROP_IOSTREAM_DYNAMIC_MEMORY_POINTER, NULL);
1068
if (mem) {
1069
SDL_free(mem);
1070
}
1071
SDL_free(userdata);
1072
return true;
1073
}
1074
1075
SDL_IOStream *SDL_IOFromDynamicMem(void)
1076
{
1077
IOStreamDynamicMemData *iodata = (IOStreamDynamicMemData *) SDL_calloc(1, sizeof (*iodata));
1078
if (!iodata) {
1079
return NULL;
1080
}
1081
1082
SDL_IOStreamInterface iface;
1083
SDL_INIT_INTERFACE(&iface);
1084
iface.size = dynamic_mem_size;
1085
iface.seek = dynamic_mem_seek;
1086
iface.read = dynamic_mem_read;
1087
iface.write = dynamic_mem_write;
1088
iface.close = dynamic_mem_close;
1089
1090
SDL_IOStream *iostr = SDL_OpenIO(&iface, iodata);
1091
if (iostr) {
1092
iodata->stream = iostr;
1093
} else {
1094
SDL_free(iodata);
1095
}
1096
return iostr;
1097
}
1098
1099
SDL_IOStatus SDL_GetIOStatus(SDL_IOStream *context)
1100
{
1101
if (!context) {
1102
SDL_InvalidParamError("context");
1103
return SDL_IO_STATUS_ERROR;
1104
}
1105
return context->status;
1106
}
1107
1108
SDL_IOStream *SDL_OpenIO(const SDL_IOStreamInterface *iface, void *userdata)
1109
{
1110
if (!iface) {
1111
SDL_InvalidParamError("iface");
1112
return NULL;
1113
}
1114
if (iface->version < sizeof(*iface)) {
1115
// Update this to handle older versions of this interface
1116
SDL_SetError("Invalid interface, should be initialized with SDL_INIT_INTERFACE()");
1117
return NULL;
1118
}
1119
1120
SDL_IOStream *iostr = (SDL_IOStream *)SDL_calloc(1, sizeof(*iostr));
1121
if (iostr) {
1122
SDL_copyp(&iostr->iface, iface);
1123
iostr->userdata = userdata;
1124
}
1125
return iostr;
1126
}
1127
1128
bool SDL_CloseIO(SDL_IOStream *iostr)
1129
{
1130
bool result = true;
1131
if (iostr) {
1132
if (iostr->iface.close) {
1133
result = iostr->iface.close(iostr->userdata);
1134
}
1135
SDL_DestroyProperties(iostr->props);
1136
SDL_free(iostr);
1137
}
1138
return result;
1139
}
1140
1141
// Load all the data from an SDL data stream
1142
void *SDL_LoadFile_IO(SDL_IOStream *src, size_t *datasize, bool closeio)
1143
{
1144
const int FILE_CHUNK_SIZE = 1024;
1145
Sint64 size, size_total = 0;
1146
size_t size_read;
1147
char *data = NULL, *newdata;
1148
bool loading_chunks = false;
1149
1150
if (!src) {
1151
SDL_InvalidParamError("src");
1152
goto done;
1153
}
1154
1155
size = SDL_GetIOSize(src);
1156
if (size < 0) {
1157
size = FILE_CHUNK_SIZE;
1158
loading_chunks = true;
1159
}
1160
if (size >= SDL_SIZE_MAX - 1) {
1161
goto done;
1162
}
1163
data = (char *)SDL_malloc((size_t)(size + 1));
1164
if (!data) {
1165
goto done;
1166
}
1167
1168
size_total = 0;
1169
for (;;) {
1170
if (loading_chunks) {
1171
if ((size_total + FILE_CHUNK_SIZE) > size) {
1172
size = (size_total + FILE_CHUNK_SIZE);
1173
if (size >= SDL_SIZE_MAX - 1) {
1174
newdata = NULL;
1175
} else {
1176
newdata = (char *)SDL_realloc(data, (size_t)(size + 1));
1177
}
1178
if (!newdata) {
1179
SDL_free(data);
1180
data = NULL;
1181
goto done;
1182
}
1183
data = newdata;
1184
}
1185
}
1186
1187
size_read = SDL_ReadIO(src, data + size_total, (size_t)(size - size_total));
1188
if (size_read > 0) {
1189
size_total += size_read;
1190
continue;
1191
} else if (SDL_GetIOStatus(src) == SDL_IO_STATUS_NOT_READY) {
1192
// Wait for the stream to be ready
1193
SDL_Delay(1);
1194
continue;
1195
}
1196
1197
// The stream status will remain set for the caller to check
1198
break;
1199
}
1200
1201
data[size_total] = '\0';
1202
1203
done:
1204
if (datasize) {
1205
*datasize = (size_t)size_total;
1206
}
1207
if (closeio && src) {
1208
SDL_CloseIO(src);
1209
}
1210
return data;
1211
}
1212
1213
void *SDL_LoadFile(const char *file, size_t *datasize)
1214
{
1215
SDL_IOStream *stream = SDL_IOFromFile(file, "rb");
1216
if (!stream) {
1217
if (datasize) {
1218
*datasize = 0;
1219
}
1220
return NULL;
1221
}
1222
return SDL_LoadFile_IO(stream, datasize, true);
1223
}
1224
1225
bool SDL_SaveFile_IO(SDL_IOStream *src, const void *data, size_t datasize, bool closeio)
1226
{
1227
size_t size_written = 0;
1228
size_t size_total = 0;
1229
bool success = true;
1230
1231
if (!src) {
1232
SDL_InvalidParamError("src");
1233
goto done;
1234
}
1235
1236
if (!data && datasize > 0) {
1237
SDL_InvalidParamError("data");
1238
goto done;
1239
}
1240
1241
if (datasize > 0) {
1242
while (size_total < datasize) {
1243
size_written = SDL_WriteIO(src, ((const char *) data) + size_written, datasize - size_written);
1244
1245
if (size_written <= 0) {
1246
if (SDL_GetIOStatus(src) == SDL_IO_STATUS_NOT_READY) {
1247
// Wait for the stream to be ready
1248
SDL_Delay(1);
1249
continue;
1250
} else {
1251
success = false;
1252
goto done;
1253
}
1254
}
1255
1256
size_total += size_written;
1257
}
1258
}
1259
1260
done:
1261
if (closeio && src) {
1262
SDL_CloseIO(src);
1263
}
1264
1265
return success;
1266
}
1267
1268
bool SDL_SaveFile(const char *file, const void *data, size_t datasize)
1269
{
1270
SDL_IOStream *stream = SDL_IOFromFile(file, "wb");
1271
if (!stream) {
1272
return false;
1273
}
1274
return SDL_SaveFile_IO(stream, data, datasize, true);
1275
}
1276
1277
SDL_PropertiesID SDL_GetIOProperties(SDL_IOStream *context)
1278
{
1279
if (!context) {
1280
SDL_InvalidParamError("context");
1281
return 0;
1282
}
1283
1284
if (context->props == 0) {
1285
context->props = SDL_CreateProperties();
1286
}
1287
return context->props;
1288
}
1289
1290
Sint64 SDL_GetIOSize(SDL_IOStream *context)
1291
{
1292
if (!context) {
1293
return SDL_InvalidParamError("context");
1294
}
1295
if (!context->iface.size) {
1296
Sint64 pos, size;
1297
1298
pos = SDL_SeekIO(context, 0, SDL_IO_SEEK_CUR);
1299
if (pos < 0) {
1300
return -1;
1301
}
1302
size = SDL_SeekIO(context, 0, SDL_IO_SEEK_END);
1303
1304
SDL_SeekIO(context, pos, SDL_IO_SEEK_SET);
1305
return size;
1306
}
1307
return context->iface.size(context->userdata);
1308
}
1309
1310
Sint64 SDL_SeekIO(SDL_IOStream *context, Sint64 offset, SDL_IOWhence whence)
1311
{
1312
if (!context) {
1313
SDL_InvalidParamError("context");
1314
return -1;
1315
} else if (!context->iface.seek) {
1316
SDL_Unsupported();
1317
return -1;
1318
}
1319
return context->iface.seek(context->userdata, offset, whence);
1320
}
1321
1322
Sint64 SDL_TellIO(SDL_IOStream *context)
1323
{
1324
return SDL_SeekIO(context, 0, SDL_IO_SEEK_CUR);
1325
}
1326
1327
size_t SDL_ReadIO(SDL_IOStream *context, void *ptr, size_t size)
1328
{
1329
size_t bytes;
1330
1331
if (!context) {
1332
SDL_InvalidParamError("context");
1333
return 0;
1334
} else if (!context->iface.read) {
1335
context->status = SDL_IO_STATUS_WRITEONLY;
1336
SDL_Unsupported();
1337
return 0;
1338
}
1339
1340
context->status = SDL_IO_STATUS_READY;
1341
SDL_ClearError();
1342
1343
if (size == 0) {
1344
return 0;
1345
}
1346
1347
bytes = context->iface.read(context->userdata, ptr, size, &context->status);
1348
if (bytes == 0 && context->status == SDL_IO_STATUS_READY) {
1349
if (*SDL_GetError()) {
1350
context->status = SDL_IO_STATUS_ERROR;
1351
} else {
1352
context->status = SDL_IO_STATUS_EOF;
1353
}
1354
}
1355
return bytes;
1356
}
1357
1358
size_t SDL_WriteIO(SDL_IOStream *context, const void *ptr, size_t size)
1359
{
1360
size_t bytes;
1361
1362
if (!context) {
1363
SDL_InvalidParamError("context");
1364
return 0;
1365
} else if (!context->iface.write) {
1366
context->status = SDL_IO_STATUS_READONLY;
1367
SDL_Unsupported();
1368
return 0;
1369
}
1370
1371
context->status = SDL_IO_STATUS_READY;
1372
SDL_ClearError();
1373
1374
if (size == 0) {
1375
return 0;
1376
}
1377
1378
bytes = context->iface.write(context->userdata, ptr, size, &context->status);
1379
if ((bytes == 0) && (context->status == SDL_IO_STATUS_READY)) {
1380
context->status = SDL_IO_STATUS_ERROR;
1381
}
1382
return bytes;
1383
}
1384
1385
size_t SDL_IOprintf(SDL_IOStream *context, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
1386
{
1387
va_list ap;
1388
int size;
1389
char *string;
1390
size_t bytes;
1391
1392
va_start(ap, fmt);
1393
size = SDL_vasprintf(&string, fmt, ap);
1394
va_end(ap);
1395
if (size < 0) {
1396
return 0;
1397
}
1398
1399
bytes = SDL_WriteIO(context, string, (size_t)size);
1400
SDL_free(string);
1401
return bytes;
1402
}
1403
1404
size_t SDL_IOvprintf(SDL_IOStream *context, SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap)
1405
{
1406
int size;
1407
char *string;
1408
size_t bytes;
1409
1410
size = SDL_vasprintf(&string, fmt, ap);
1411
if (size < 0) {
1412
return 0;
1413
}
1414
1415
bytes = SDL_WriteIO(context, string, (size_t)size);
1416
SDL_free(string);
1417
return bytes;
1418
}
1419
1420
bool SDL_FlushIO(SDL_IOStream *context)
1421
{
1422
bool result = true;
1423
1424
if (!context) {
1425
return SDL_InvalidParamError("context");
1426
}
1427
1428
context->status = SDL_IO_STATUS_READY;
1429
SDL_ClearError();
1430
1431
if (context->iface.flush) {
1432
result = context->iface.flush(context->userdata, &context->status);
1433
}
1434
if (!result && (context->status == SDL_IO_STATUS_READY)) {
1435
context->status = SDL_IO_STATUS_ERROR;
1436
}
1437
return result;
1438
}
1439
1440
// Functions for dynamically reading and writing endian-specific values
1441
1442
bool SDL_ReadU8(SDL_IOStream *src, Uint8 *value)
1443
{
1444
Uint8 data = 0;
1445
bool result = false;
1446
1447
if (SDL_ReadIO(src, &data, sizeof(data)) == sizeof(data)) {
1448
result = true;
1449
}
1450
if (value) {
1451
*value = data;
1452
}
1453
return result;
1454
}
1455
1456
bool SDL_ReadS8(SDL_IOStream *src, Sint8 *value)
1457
{
1458
Sint8 data = 0;
1459
bool result = false;
1460
1461
if (SDL_ReadIO(src, &data, sizeof(data)) == sizeof(data)) {
1462
result = true;
1463
}
1464
if (value) {
1465
*value = data;
1466
}
1467
return result;
1468
}
1469
1470
bool SDL_ReadU16LE(SDL_IOStream *src, Uint16 *value)
1471
{
1472
Uint16 data = 0;
1473
bool result = false;
1474
1475
if (SDL_ReadIO(src, &data, sizeof(data)) == sizeof(data)) {
1476
result = true;
1477
}
1478
if (value) {
1479
*value = SDL_Swap16LE(data);
1480
}
1481
return result;
1482
}
1483
1484
bool SDL_ReadS16LE(SDL_IOStream *src, Sint16 *value)
1485
{
1486
return SDL_ReadU16LE(src, (Uint16 *)value);
1487
}
1488
1489
bool SDL_ReadU16BE(SDL_IOStream *src, Uint16 *value)
1490
{
1491
Uint16 data = 0;
1492
bool result = false;
1493
1494
if (SDL_ReadIO(src, &data, sizeof(data)) == sizeof(data)) {
1495
result = true;
1496
}
1497
if (value) {
1498
*value = SDL_Swap16BE(data);
1499
}
1500
return result;
1501
}
1502
1503
bool SDL_ReadS16BE(SDL_IOStream *src, Sint16 *value)
1504
{
1505
return SDL_ReadU16BE(src, (Uint16 *)value);
1506
}
1507
1508
bool SDL_ReadU32LE(SDL_IOStream *src, Uint32 *value)
1509
{
1510
Uint32 data = 0;
1511
bool result = false;
1512
1513
if (SDL_ReadIO(src, &data, sizeof(data)) == sizeof(data)) {
1514
result = true;
1515
}
1516
if (value) {
1517
*value = SDL_Swap32LE(data);
1518
}
1519
return result;
1520
}
1521
1522
bool SDL_ReadS32LE(SDL_IOStream *src, Sint32 *value)
1523
{
1524
return SDL_ReadU32LE(src, (Uint32 *)value);
1525
}
1526
1527
bool SDL_ReadU32BE(SDL_IOStream *src, Uint32 *value)
1528
{
1529
Uint32 data = 0;
1530
bool result = false;
1531
1532
if (SDL_ReadIO(src, &data, sizeof(data)) == sizeof(data)) {
1533
result = true;
1534
}
1535
if (value) {
1536
*value = SDL_Swap32BE(data);
1537
}
1538
return result;
1539
}
1540
1541
bool SDL_ReadS32BE(SDL_IOStream *src, Sint32 *value)
1542
{
1543
return SDL_ReadU32BE(src, (Uint32 *)value);
1544
}
1545
1546
bool SDL_ReadU64LE(SDL_IOStream *src, Uint64 *value)
1547
{
1548
Uint64 data = 0;
1549
bool result = false;
1550
1551
if (SDL_ReadIO(src, &data, sizeof(data)) == sizeof(data)) {
1552
result = true;
1553
}
1554
if (value) {
1555
*value = SDL_Swap64LE(data);
1556
}
1557
return result;
1558
}
1559
1560
bool SDL_ReadS64LE(SDL_IOStream *src, Sint64 *value)
1561
{
1562
return SDL_ReadU64LE(src, (Uint64 *)value);
1563
}
1564
1565
bool SDL_ReadU64BE(SDL_IOStream *src, Uint64 *value)
1566
{
1567
Uint64 data = 0;
1568
bool result = false;
1569
1570
if (SDL_ReadIO(src, &data, sizeof(data)) == sizeof(data)) {
1571
result = true;
1572
}
1573
if (value) {
1574
*value = SDL_Swap64BE(data);
1575
}
1576
return result;
1577
}
1578
1579
bool SDL_ReadS64BE(SDL_IOStream *src, Sint64 *value)
1580
{
1581
return SDL_ReadU64BE(src, (Uint64 *)value);
1582
}
1583
1584
bool SDL_WriteU8(SDL_IOStream *dst, Uint8 value)
1585
{
1586
return (SDL_WriteIO(dst, &value, sizeof(value)) == sizeof(value));
1587
}
1588
1589
bool SDL_WriteS8(SDL_IOStream *dst, Sint8 value)
1590
{
1591
return (SDL_WriteIO(dst, &value, sizeof(value)) == sizeof(value));
1592
}
1593
1594
bool SDL_WriteU16LE(SDL_IOStream *dst, Uint16 value)
1595
{
1596
const Uint16 swapped = SDL_Swap16LE(value);
1597
return (SDL_WriteIO(dst, &swapped, sizeof(swapped)) == sizeof(swapped));
1598
}
1599
1600
bool SDL_WriteS16LE(SDL_IOStream *dst, Sint16 value)
1601
{
1602
return SDL_WriteU16LE(dst, (Uint16)value);
1603
}
1604
1605
bool SDL_WriteU16BE(SDL_IOStream *dst, Uint16 value)
1606
{
1607
const Uint16 swapped = SDL_Swap16BE(value);
1608
return (SDL_WriteIO(dst, &swapped, sizeof(swapped)) == sizeof(swapped));
1609
}
1610
1611
bool SDL_WriteS16BE(SDL_IOStream *dst, Sint16 value)
1612
{
1613
return SDL_WriteU16BE(dst, (Uint16)value);
1614
}
1615
1616
bool SDL_WriteU32LE(SDL_IOStream *dst, Uint32 value)
1617
{
1618
const Uint32 swapped = SDL_Swap32LE(value);
1619
return (SDL_WriteIO(dst, &swapped, sizeof(swapped)) == sizeof(swapped));
1620
}
1621
1622
bool SDL_WriteS32LE(SDL_IOStream *dst, Sint32 value)
1623
{
1624
return SDL_WriteU32LE(dst, (Uint32)value);
1625
}
1626
1627
bool SDL_WriteU32BE(SDL_IOStream *dst, Uint32 value)
1628
{
1629
const Uint32 swapped = SDL_Swap32BE(value);
1630
return (SDL_WriteIO(dst, &swapped, sizeof(swapped)) == sizeof(swapped));
1631
}
1632
1633
bool SDL_WriteS32BE(SDL_IOStream *dst, Sint32 value)
1634
{
1635
return SDL_WriteU32BE(dst, (Uint32)value);
1636
}
1637
1638
bool SDL_WriteU64LE(SDL_IOStream *dst, Uint64 value)
1639
{
1640
const Uint64 swapped = SDL_Swap64LE(value);
1641
return (SDL_WriteIO(dst, &swapped, sizeof(swapped)) == sizeof(swapped));
1642
}
1643
1644
bool SDL_WriteS64LE(SDL_IOStream *dst, Sint64 value)
1645
{
1646
return SDL_WriteU64LE(dst, (Uint64)value);
1647
}
1648
1649
bool SDL_WriteU64BE(SDL_IOStream *dst, Uint64 value)
1650
{
1651
const Uint64 swapped = SDL_Swap64BE(value);
1652
return (SDL_WriteIO(dst, &swapped, sizeof(swapped)) == sizeof(swapped));
1653
}
1654
1655
bool SDL_WriteS64BE(SDL_IOStream *dst, Sint64 value)
1656
{
1657
return SDL_WriteU64BE(dst, (Uint64)value);
1658
}
1659
1660