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