Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/sdl/io/SDL_iostream.c
9903 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
if (result < 0) {
459
return SDL_SetError("Error flushing datastream: %s", strerror(errno));
460
}
461
return true;
462
}
463
464
static 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
477
SDL_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
520
typedef 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
563
static 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
605
static 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
620
static 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
635
static 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
659
static 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
672
SDL_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
714
typedef struct IOStreamMemData
715
{
716
Uint8 *base;
717
Uint8 *here;
718
Uint8 *stop;
719
} IOStreamMemData;
720
721
static Sint64 SDLCALL mem_size(void *userdata)
722
{
723
const IOStreamMemData *iodata = (IOStreamMemData *) userdata;
724
return (iodata->stop - iodata->base);
725
}
726
727
static 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
756
static 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
768
static 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
774
static 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
780
static 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)
789
static 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
802
SDL_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
920
SDL_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
960
SDL_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
1000
typedef struct IOStreamDynamicMemData
1001
{
1002
SDL_IOStream *stream;
1003
IOStreamMemData data;
1004
Uint8 *end;
1005
} IOStreamDynamicMemData;
1006
1007
static Sint64 SDLCALL dynamic_mem_size(void *userdata)
1008
{
1009
IOStreamDynamicMemData *iodata = (IOStreamDynamicMemData *) userdata;
1010
return mem_size(&iodata->data);
1011
}
1012
1013
static 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
1019
static 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
1025
static 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
1049
static 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
1063
static 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
1074
SDL_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
1098
SDL_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
1107
SDL_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
1127
bool 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
1141
void *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
1202
done:
1203
if (datasize) {
1204
*datasize = (size_t)size_total;
1205
}
1206
if (closeio && src) {
1207
SDL_CloseIO(src);
1208
}
1209
return data;
1210
}
1211
1212
void *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
1224
bool 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
1259
done:
1260
if (closeio && src) {
1261
SDL_CloseIO(src);
1262
}
1263
1264
return success;
1265
}
1266
1267
bool 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
1276
SDL_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
1289
Sint64 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
1309
Sint64 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
1321
Sint64 SDL_TellIO(SDL_IOStream *context)
1322
{
1323
return SDL_SeekIO(context, 0, SDL_IO_SEEK_CUR);
1324
}
1325
1326
size_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
1357
size_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
1384
size_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
1403
size_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
1419
bool 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
1441
bool 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
1455
bool 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
1469
bool 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
1483
bool SDL_ReadS16LE(SDL_IOStream *src, Sint16 *value)
1484
{
1485
return SDL_ReadU16LE(src, (Uint16 *)value);
1486
}
1487
1488
bool 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
1502
bool SDL_ReadS16BE(SDL_IOStream *src, Sint16 *value)
1503
{
1504
return SDL_ReadU16BE(src, (Uint16 *)value);
1505
}
1506
1507
bool 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
1521
bool SDL_ReadS32LE(SDL_IOStream *src, Sint32 *value)
1522
{
1523
return SDL_ReadU32LE(src, (Uint32 *)value);
1524
}
1525
1526
bool 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
1540
bool SDL_ReadS32BE(SDL_IOStream *src, Sint32 *value)
1541
{
1542
return SDL_ReadU32BE(src, (Uint32 *)value);
1543
}
1544
1545
bool 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
1559
bool SDL_ReadS64LE(SDL_IOStream *src, Sint64 *value)
1560
{
1561
return SDL_ReadU64LE(src, (Uint64 *)value);
1562
}
1563
1564
bool 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
1578
bool SDL_ReadS64BE(SDL_IOStream *src, Sint64 *value)
1579
{
1580
return SDL_ReadU64BE(src, (Uint64 *)value);
1581
}
1582
1583
bool SDL_WriteU8(SDL_IOStream *dst, Uint8 value)
1584
{
1585
return (SDL_WriteIO(dst, &value, sizeof(value)) == sizeof(value));
1586
}
1587
1588
bool SDL_WriteS8(SDL_IOStream *dst, Sint8 value)
1589
{
1590
return (SDL_WriteIO(dst, &value, sizeof(value)) == sizeof(value));
1591
}
1592
1593
bool 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
1599
bool SDL_WriteS16LE(SDL_IOStream *dst, Sint16 value)
1600
{
1601
return SDL_WriteU16LE(dst, (Uint16)value);
1602
}
1603
1604
bool 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
1610
bool SDL_WriteS16BE(SDL_IOStream *dst, Sint16 value)
1611
{
1612
return SDL_WriteU16BE(dst, (Uint16)value);
1613
}
1614
1615
bool 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
1621
bool SDL_WriteS32LE(SDL_IOStream *dst, Sint32 value)
1622
{
1623
return SDL_WriteU32LE(dst, (Uint32)value);
1624
}
1625
1626
bool 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
1632
bool SDL_WriteS32BE(SDL_IOStream *dst, Sint32 value)
1633
{
1634
return SDL_WriteU32BE(dst, (Uint32)value);
1635
}
1636
1637
bool 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
1643
bool SDL_WriteS64LE(SDL_IOStream *dst, Sint64 value)
1644
{
1645
return SDL_WriteU64LE(dst, (Uint64)value);
1646
}
1647
1648
bool 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
1654
bool SDL_WriteS64BE(SDL_IOStream *dst, Sint64 value)
1655
{
1656
return SDL_WriteU64BE(dst, (Uint64)value);
1657
}
1658
1659