2 Simple DirectMedia Layer
3 Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
22 /* We won't get fseeko64 on QNX if _LARGEFILE64_SOURCE is defined, but the
23 configure script knows the C runtime has it and enables it. */
25 /* Need this so Linux systems define fseek64o, ftell64o and off64_t */
26 #ifndef _LARGEFILE64_SOURCE
27 #define _LARGEFILE64_SOURCE
31 #include "../SDL_internal.h"
33 #if defined(__WIN32__)
34 #include "../core/windows/SDL_windows.h"
45 /* This file provides a general interface for SDL to read and write
46 data sources. It can easily be extended to files, memory, etc.
49 #include "SDL_endian.h"
50 #include "SDL_rwops.h"
53 #include "cocoa/SDL_rwopsbundlesupport.h"
54 #endif /* __APPLE__ */
57 #include "../core/android/SDL_android.h"
58 #include "SDL_system.h"
62 #include "nacl_io/nacl_io.h"
67 /* Functions to read/write Win32 API file pointers */
69 #ifndef INVALID_SET_FILE_POINTER
70 #define INVALID_SET_FILE_POINTER 0xFFFFFFFF
73 #define READAHEAD_BUFFER_SIZE 1024
76 windows_file_open(SDL_RWops * context, const char *filename, const char *mode)
80 DWORD r_right, w_right;
81 DWORD must_exist, truncate;
85 return -1; /* failed (invalid call) */
87 context->hidden.windowsio.h = INVALID_HANDLE_VALUE; /* mark this as unusable */
88 context->hidden.windowsio.buffer.data = NULL;
89 context->hidden.windowsio.buffer.size = 0;
90 context->hidden.windowsio.buffer.left = 0;
92 /* "r" = reading, file must exist */
93 /* "w" = writing, truncate existing, file may not exist */
94 /* "r+"= reading or writing, file must exist */
95 /* "a" = writing, append file may not exist */
96 /* "a+"= append + read, file may not exist */
97 /* "w+" = read, write, truncate. file may not exist */
99 must_exist = (SDL_strchr(mode, 'r') != NULL) ? OPEN_EXISTING : 0;
100 truncate = (SDL_strchr(mode, 'w') != NULL) ? CREATE_ALWAYS : 0;
101 r_right = (SDL_strchr(mode, '+') != NULL
102 || must_exist) ? GENERIC_READ : 0;
103 a_mode = (SDL_strchr(mode, 'a') != NULL) ? OPEN_ALWAYS : 0;
104 w_right = (a_mode || SDL_strchr(mode, '+')
105 || truncate) ? GENERIC_WRITE : 0;
107 if (!r_right && !w_right) /* inconsistent mode */
108 return -1; /* failed (invalid call) */
110 context->hidden.windowsio.buffer.data =
111 (char *) SDL_malloc(READAHEAD_BUFFER_SIZE);
112 if (!context->hidden.windowsio.buffer.data) {
113 return SDL_OutOfMemory();
115 /* Do not open a dialog box if failure */
117 SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
120 LPTSTR tstr = WIN_UTF8ToString(filename);
121 h = CreateFile(tstr, (w_right | r_right),
122 (w_right) ? 0 : FILE_SHARE_READ, NULL,
123 (must_exist | truncate | a_mode),
124 FILE_ATTRIBUTE_NORMAL, NULL);
128 /* restore old behavior */
129 SetErrorMode(old_error_mode);
131 if (h == INVALID_HANDLE_VALUE) {
132 SDL_free(context->hidden.windowsio.buffer.data);
133 context->hidden.windowsio.buffer.data = NULL;
134 SDL_SetError("Couldn't open %s", filename);
135 return -2; /* failed (CreateFile) */
137 context->hidden.windowsio.h = h;
138 context->hidden.windowsio.append = a_mode ? SDL_TRUE : SDL_FALSE;
143 static Sint64 SDLCALL
144 windows_file_size(SDL_RWops * context)
148 if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE) {
149 return SDL_SetError("windows_file_size: invalid context/file not opened");
152 if (!GetFileSizeEx(context->hidden.windowsio.h, &size)) {
153 return WIN_SetError("windows_file_size");
156 return size.QuadPart;
159 static Sint64 SDLCALL
160 windows_file_seek(SDL_RWops * context, Sint64 offset, int whence)
163 LARGE_INTEGER windowsoffset;
165 if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE) {
166 return SDL_SetError("windows_file_seek: invalid context/file not opened");
169 /* FIXME: We may be able to satisfy the seek within buffered data */
170 if (whence == RW_SEEK_CUR && context->hidden.windowsio.buffer.left) {
171 offset -= (long)context->hidden.windowsio.buffer.left;
173 context->hidden.windowsio.buffer.left = 0;
177 windowswhence = FILE_BEGIN;
180 windowswhence = FILE_CURRENT;
183 windowswhence = FILE_END;
186 return SDL_SetError("windows_file_seek: Unknown value for 'whence'");
189 windowsoffset.QuadPart = offset;
190 if (!SetFilePointerEx(context->hidden.windowsio.h, windowsoffset, &windowsoffset, windowswhence)) {
191 return WIN_SetError("windows_file_seek");
193 return windowsoffset.QuadPart;
196 static size_t SDLCALL
197 windows_file_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
200 size_t total_read = 0;
204 total_need = size * maxnum;
206 if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE
210 if (context->hidden.windowsio.buffer.left > 0) {
211 void *data = (char *) context->hidden.windowsio.buffer.data +
212 context->hidden.windowsio.buffer.size -
213 context->hidden.windowsio.buffer.left;
215 SDL_min(total_need, context->hidden.windowsio.buffer.left);
216 SDL_memcpy(ptr, data, read_ahead);
217 context->hidden.windowsio.buffer.left -= read_ahead;
219 if (read_ahead == total_need) {
222 ptr = (char *) ptr + read_ahead;
223 total_need -= read_ahead;
224 total_read += read_ahead;
227 if (total_need < READAHEAD_BUFFER_SIZE) {
229 (context->hidden.windowsio.h, context->hidden.windowsio.buffer.data,
230 READAHEAD_BUFFER_SIZE, &byte_read, NULL)) {
231 SDL_Error(SDL_EFREAD);
234 read_ahead = SDL_min(total_need, (int) byte_read);
235 SDL_memcpy(ptr, context->hidden.windowsio.buffer.data, read_ahead);
236 context->hidden.windowsio.buffer.size = byte_read;
237 context->hidden.windowsio.buffer.left = byte_read - read_ahead;
238 total_read += read_ahead;
241 (context->hidden.windowsio.h, ptr, (DWORD)total_need, &byte_read, NULL)) {
242 SDL_Error(SDL_EFREAD);
245 total_read += byte_read;
247 return (total_read / size);
250 static size_t SDLCALL
251 windows_file_write(SDL_RWops * context, const void *ptr, size_t size,
259 total_bytes = size * num;
261 if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE
262 || total_bytes <= 0 || !size)
265 if (context->hidden.windowsio.buffer.left) {
266 SetFilePointer(context->hidden.windowsio.h,
267 -(LONG)context->hidden.windowsio.buffer.left, NULL,
269 context->hidden.windowsio.buffer.left = 0;
272 /* if in append mode, we must go to the EOF before write */
273 if (context->hidden.windowsio.append) {
274 if (SetFilePointer(context->hidden.windowsio.h, 0L, NULL, FILE_END) ==
275 INVALID_SET_FILE_POINTER) {
276 SDL_Error(SDL_EFWRITE);
282 (context->hidden.windowsio.h, ptr, (DWORD)total_bytes, &byte_written, NULL)) {
283 SDL_Error(SDL_EFWRITE);
287 nwritten = byte_written / size;
292 windows_file_close(SDL_RWops * context)
296 if (context->hidden.windowsio.h != INVALID_HANDLE_VALUE) {
297 CloseHandle(context->hidden.windowsio.h);
298 context->hidden.windowsio.h = INVALID_HANDLE_VALUE; /* to be sure */
300 SDL_free(context->hidden.windowsio.buffer.data);
301 context->hidden.windowsio.buffer.data = NULL;
306 #endif /* __WIN32__ */
311 #define fopen fopen64
314 #define fseek_off_t off64_t
315 #define fseek fseeko64
316 #define ftell ftello64
317 #elif defined(HAVE_FSEEKO)
318 #if defined(OFF_MIN) && defined(OFF_MAX)
319 #define FSEEK_OFF_MIN OFF_MIN
320 #define FSEEK_OFF_MAX OFF_MAX
321 #elif defined(HAVE_LIMITS_H)
322 /* POSIX doesn't specify the minimum and maximum macros for off_t so
323 * we have to improvise and dance around implementation-defined
324 * behavior. This may fail if the off_t type has padding bits or
325 * is not a two's-complement representation. The compilers will detect
326 * and eliminate the dead code if off_t has 64 bits.
328 #define FSEEK_OFF_MAX (((((off_t)1 << (sizeof(off_t) * CHAR_BIT - 2)) - 1) << 1) + 1)
329 #define FSEEK_OFF_MIN (-(FSEEK_OFF_MAX) - 1)
331 #define fseek_off_t off_t
334 #elif defined(HAVE__FSEEKI64)
335 #define fseek_off_t __int64
336 #define fseek _fseeki64
337 #define ftell _ftelli64
340 #define FSEEK_OFF_MIN LONG_MIN
341 #define FSEEK_OFF_MAX LONG_MAX
343 #define fseek_off_t long
346 /* Functions to read/write stdio file pointers */
348 static Sint64 SDLCALL
349 stdio_size(SDL_RWops * context)
353 pos = SDL_RWseek(context, 0, RW_SEEK_CUR);
357 size = SDL_RWseek(context, 0, RW_SEEK_END);
359 SDL_RWseek(context, pos, RW_SEEK_SET);
363 static Sint64 SDLCALL
364 stdio_seek(SDL_RWops * context, Sint64 offset, int whence)
370 stdiowhence = SEEK_SET;
373 stdiowhence = SEEK_CUR;
376 stdiowhence = SEEK_END;
379 return SDL_SetError("Unknown value for 'whence'");
382 #if defined(FSEEK_OFF_MIN) && defined(FSEEK_OFF_MAX)
383 if (offset < (Sint64)(FSEEK_OFF_MIN) || offset > (Sint64)(FSEEK_OFF_MAX)) {
384 return SDL_SetError("Seek offset out of range");
388 if (fseek(context->hidden.stdio.fp, (fseek_off_t)offset, stdiowhence) == 0) {
389 Sint64 pos = ftell(context->hidden.stdio.fp);
391 return SDL_SetError("Couldn't get stream offset");
395 return SDL_Error(SDL_EFSEEK);
398 static size_t SDLCALL
399 stdio_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
403 nread = fread(ptr, size, maxnum, context->hidden.stdio.fp);
404 if (nread == 0 && ferror(context->hidden.stdio.fp)) {
405 SDL_Error(SDL_EFREAD);
410 static size_t SDLCALL
411 stdio_write(SDL_RWops * context, const void *ptr, size_t size, size_t num)
415 nwrote = fwrite(ptr, size, num, context->hidden.stdio.fp);
416 if (nwrote == 0 && ferror(context->hidden.stdio.fp)) {
417 SDL_Error(SDL_EFWRITE);
423 stdio_close(SDL_RWops * context)
427 if (context->hidden.stdio.autoclose) {
428 /* WARNING: Check the return value here! */
429 if (fclose(context->hidden.stdio.fp) != 0) {
430 status = SDL_Error(SDL_EFWRITE);
437 #endif /* !HAVE_STDIO_H */
439 /* Functions to read/write memory pointers */
441 static Sint64 SDLCALL
442 mem_size(SDL_RWops * context)
444 return (Sint64)(context->hidden.mem.stop - context->hidden.mem.base);
447 static Sint64 SDLCALL
448 mem_seek(SDL_RWops * context, Sint64 offset, int whence)
454 newpos = context->hidden.mem.base + offset;
457 newpos = context->hidden.mem.here + offset;
460 newpos = context->hidden.mem.stop + offset;
463 return SDL_SetError("Unknown value for 'whence'");
465 if (newpos < context->hidden.mem.base) {
466 newpos = context->hidden.mem.base;
468 if (newpos > context->hidden.mem.stop) {
469 newpos = context->hidden.mem.stop;
471 context->hidden.mem.here = newpos;
472 return (Sint64)(context->hidden.mem.here - context->hidden.mem.base);
475 static size_t SDLCALL
476 mem_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
479 size_t mem_available;
481 total_bytes = (maxnum * size);
482 if ((maxnum <= 0) || (size <= 0)
483 || ((total_bytes / maxnum) != size)) {
487 mem_available = (context->hidden.mem.stop - context->hidden.mem.here);
488 if (total_bytes > mem_available) {
489 total_bytes = mem_available;
492 SDL_memcpy(ptr, context->hidden.mem.here, total_bytes);
493 context->hidden.mem.here += total_bytes;
495 return (total_bytes / size);
498 static size_t SDLCALL
499 mem_write(SDL_RWops * context, const void *ptr, size_t size, size_t num)
501 if ((context->hidden.mem.here + (num * size)) > context->hidden.mem.stop) {
502 num = (context->hidden.mem.stop - context->hidden.mem.here) / size;
504 SDL_memcpy(context->hidden.mem.here, ptr, num * size);
505 context->hidden.mem.here += num * size;
509 static size_t SDLCALL
510 mem_writeconst(SDL_RWops * context, const void *ptr, size_t size, size_t num)
512 SDL_SetError("Can't write to read-only memory");
517 mem_close(SDL_RWops * context)
526 /* Functions to create SDL_RWops structures from various data sources */
529 SDL_RWFromFile(const char *file, const char *mode)
531 SDL_RWops *rwops = NULL;
532 if (!file || !*file || !mode || !*mode) {
533 SDL_SetError("SDL_RWFromFile(): No file or no mode specified");
536 #if defined(__ANDROID__)
538 /* Try to open the file on the filesystem first */
540 FILE *fp = fopen(file, mode);
542 return SDL_RWFromFP(fp, 1);
545 /* Try opening it from internal storage if it's a relative path */
549 /* !!! FIXME: why not just "char path[PATH_MAX];" ? */
550 path = SDL_stack_alloc(char, PATH_MAX);
552 SDL_snprintf(path, PATH_MAX, "%s/%s",
553 SDL_AndroidGetInternalStoragePath(), file);
554 fp = fopen(path, mode);
555 SDL_stack_free(path);
557 return SDL_RWFromFP(fp, 1);
561 #endif /* HAVE_STDIO_H */
563 /* Try to open the file from the asset system */
564 rwops = SDL_AllocRW();
566 return NULL; /* SDL_SetError already setup by SDL_AllocRW() */
567 if (Android_JNI_FileOpen(rwops, file, mode) < 0) {
571 rwops->size = Android_JNI_FileSize;
572 rwops->seek = Android_JNI_FileSeek;
573 rwops->read = Android_JNI_FileRead;
574 rwops->write = Android_JNI_FileWrite;
575 rwops->close = Android_JNI_FileClose;
576 rwops->type = SDL_RWOPS_JNIFILE;
578 #elif defined(__WIN32__)
579 rwops = SDL_AllocRW();
581 return NULL; /* SDL_SetError already setup by SDL_AllocRW() */
582 if (windows_file_open(rwops, file, mode) < 0) {
586 rwops->size = windows_file_size;
587 rwops->seek = windows_file_seek;
588 rwops->read = windows_file_read;
589 rwops->write = windows_file_write;
590 rwops->close = windows_file_close;
591 rwops->type = SDL_RWOPS_WINFILE;
596 FILE *fp = SDL_OpenFPFromBundleOrFallback(file, mode);
599 fopen_s(&fp, file, mode);
601 FILE *fp = fopen(file, mode);
604 SDL_SetError("Couldn't open %s", file);
606 rwops = SDL_RWFromFP(fp, SDL_TRUE);
610 SDL_SetError("SDL not compiled with stdio support");
611 #endif /* !HAVE_STDIO_H */
618 SDL_RWFromFP(FILE * fp, SDL_bool autoclose)
620 SDL_RWops *rwops = NULL;
622 rwops = SDL_AllocRW();
624 rwops->size = stdio_size;
625 rwops->seek = stdio_seek;
626 rwops->read = stdio_read;
627 rwops->write = stdio_write;
628 rwops->close = stdio_close;
629 rwops->hidden.stdio.fp = fp;
630 rwops->hidden.stdio.autoclose = autoclose;
631 rwops->type = SDL_RWOPS_STDFILE;
637 SDL_RWFromFP(void * fp, SDL_bool autoclose)
639 SDL_SetError("SDL not compiled with stdio support");
642 #endif /* HAVE_STDIO_H */
645 SDL_RWFromMem(void *mem, int size)
647 SDL_RWops *rwops = NULL;
649 SDL_InvalidParamError("mem");
653 SDL_InvalidParamError("size");
657 rwops = SDL_AllocRW();
659 rwops->size = mem_size;
660 rwops->seek = mem_seek;
661 rwops->read = mem_read;
662 rwops->write = mem_write;
663 rwops->close = mem_close;
664 rwops->hidden.mem.base = (Uint8 *) mem;
665 rwops->hidden.mem.here = rwops->hidden.mem.base;
666 rwops->hidden.mem.stop = rwops->hidden.mem.base + size;
667 rwops->type = SDL_RWOPS_MEMORY;
673 SDL_RWFromConstMem(const void *mem, int size)
675 SDL_RWops *rwops = NULL;
677 SDL_InvalidParamError("mem");
681 SDL_InvalidParamError("size");
685 rwops = SDL_AllocRW();
687 rwops->size = mem_size;
688 rwops->seek = mem_seek;
689 rwops->read = mem_read;
690 rwops->write = mem_writeconst;
691 rwops->close = mem_close;
692 rwops->hidden.mem.base = (Uint8 *) mem;
693 rwops->hidden.mem.here = rwops->hidden.mem.base;
694 rwops->hidden.mem.stop = rwops->hidden.mem.base + size;
695 rwops->type = SDL_RWOPS_MEMORY_RO;
705 area = (SDL_RWops *) SDL_malloc(sizeof *area);
709 area->type = SDL_RWOPS_UNKNOWN;
715 SDL_FreeRW(SDL_RWops * area)
720 /* Load all the data from an SDL data stream */
722 SDL_LoadFile_RW(SDL_RWops * src, size_t *datasize, int freesrc)
724 const int FILE_CHUNK_SIZE = 1024;
726 size_t size_read, size_total;
727 void *data = NULL, *newdata;
730 SDL_InvalidParamError("src");
734 size = SDL_RWsize(src);
736 size = FILE_CHUNK_SIZE;
738 data = SDL_malloc((size_t)(size + 1));
742 if ((((Sint64)size_total) + FILE_CHUNK_SIZE) > size) {
743 size = (size_total + FILE_CHUNK_SIZE);
744 newdata = SDL_realloc(data, (size_t)(size + 1));
754 size_read = SDL_RWread(src, (char *)data+size_total, 1, (size_t)(size-size_total));
755 if (size_read == 0) {
758 size_total += size_read;
762 *datasize = size_total;
764 ((char *)data)[size_total] = '\0';
767 if (freesrc && src) {
774 SDL_LoadFile(const char *file, size_t *datasize)
776 return SDL_LoadFile_RW(SDL_RWFromFile(file, "rb"), datasize, 1);
780 SDL_RWsize(SDL_RWops *context)
782 return context->size(context);
786 SDL_RWseek(SDL_RWops *context, Sint64 offset, int whence)
788 return context->seek(context, offset, whence);
792 SDL_RWtell(SDL_RWops *context)
794 return context->seek(context, 0, RW_SEEK_CUR);
798 SDL_RWread(SDL_RWops *context, void *ptr, size_t size, size_t maxnum)
800 return context->read(context, ptr, size, maxnum);
804 SDL_RWwrite(SDL_RWops *context, const void *ptr, size_t size, size_t num)
806 return context->write(context, ptr, size, num);
810 SDL_RWclose(SDL_RWops *context)
812 return context->close(context);
815 /* Functions for dynamically reading and writing endian-specific values */
818 SDL_ReadU8(SDL_RWops * src)
822 SDL_RWread(src, &value, sizeof (value), 1);
827 SDL_ReadLE16(SDL_RWops * src)
831 SDL_RWread(src, &value, sizeof (value), 1);
832 return SDL_SwapLE16(value);
836 SDL_ReadBE16(SDL_RWops * src)
840 SDL_RWread(src, &value, sizeof (value), 1);
841 return SDL_SwapBE16(value);
845 SDL_ReadLE32(SDL_RWops * src)
849 SDL_RWread(src, &value, sizeof (value), 1);
850 return SDL_SwapLE32(value);
854 SDL_ReadBE32(SDL_RWops * src)
858 SDL_RWread(src, &value, sizeof (value), 1);
859 return SDL_SwapBE32(value);
863 SDL_ReadLE64(SDL_RWops * src)
867 SDL_RWread(src, &value, sizeof (value), 1);
868 return SDL_SwapLE64(value);
872 SDL_ReadBE64(SDL_RWops * src)
876 SDL_RWread(src, &value, sizeof (value), 1);
877 return SDL_SwapBE64(value);
881 SDL_WriteU8(SDL_RWops * dst, Uint8 value)
883 return SDL_RWwrite(dst, &value, sizeof (value), 1);
887 SDL_WriteLE16(SDL_RWops * dst, Uint16 value)
889 const Uint16 swapped = SDL_SwapLE16(value);
890 return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
894 SDL_WriteBE16(SDL_RWops * dst, Uint16 value)
896 const Uint16 swapped = SDL_SwapBE16(value);
897 return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
901 SDL_WriteLE32(SDL_RWops * dst, Uint32 value)
903 const Uint32 swapped = SDL_SwapLE32(value);
904 return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
908 SDL_WriteBE32(SDL_RWops * dst, Uint32 value)
910 const Uint32 swapped = SDL_SwapBE32(value);
911 return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
915 SDL_WriteLE64(SDL_RWops * dst, Uint64 value)
917 const Uint64 swapped = SDL_SwapLE64(value);
918 return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
922 SDL_WriteBE64(SDL_RWops * dst, Uint64 value)
924 const Uint64 swapped = SDL_SwapBE64(value);
925 return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
928 /* vi: set ts=4 sw=4 expandtab: */