Atlas - SDL_iostream.c

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