2 Simple DirectMedia Layer
3 Copyright (C) 1997-2013 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.
21 /* Need this so Linux systems define fseek64o, ftell64o and off64_t */
22 #define _LARGEFILE64_SOURCE
23 #include "SDL_config.h"
25 #if defined(__WIN32__)
26 #include "../core/windows/SDL_windows.h"
30 /* This file provides a general interface for SDL to read and write
31 data sources. It can easily be extended to files, memory, etc.
34 #include "SDL_endian.h"
35 #include "SDL_rwops.h"
38 #include "cocoa/SDL_rwopsbundlesupport.h"
39 #endif /* __APPLE__ */
42 #include "../core/android/SDL_android.h"
43 #include "SDL_system.h"
48 /* Functions to read/write Win32 API file pointers */
50 #ifndef INVALID_SET_FILE_POINTER
51 #define INVALID_SET_FILE_POINTER 0xFFFFFFFF
54 #define READAHEAD_BUFFER_SIZE 1024
57 windows_file_open(SDL_RWops * context, const char *filename, const char *mode)
61 DWORD r_right, w_right;
62 DWORD must_exist, truncate;
66 return -1; /* failed (invalid call) */
68 context->hidden.windowsio.h = INVALID_HANDLE_VALUE; /* mark this as unusable */
69 context->hidden.windowsio.buffer.data = NULL;
70 context->hidden.windowsio.buffer.size = 0;
71 context->hidden.windowsio.buffer.left = 0;
73 /* "r" = reading, file must exist */
74 /* "w" = writing, truncate existing, file may not exist */
75 /* "r+"= reading or writing, file must exist */
76 /* "a" = writing, append file may not exist */
77 /* "a+"= append + read, file may not exist */
78 /* "w+" = read, write, truncate. file may not exist */
80 must_exist = (SDL_strchr(mode, 'r') != NULL) ? OPEN_EXISTING : 0;
81 truncate = (SDL_strchr(mode, 'w') != NULL) ? CREATE_ALWAYS : 0;
82 r_right = (SDL_strchr(mode, '+') != NULL
83 || must_exist) ? GENERIC_READ : 0;
84 a_mode = (SDL_strchr(mode, 'a') != NULL) ? OPEN_ALWAYS : 0;
85 w_right = (a_mode || SDL_strchr(mode, '+')
86 || truncate) ? GENERIC_WRITE : 0;
88 if (!r_right && !w_right) /* inconsistent mode */
89 return -1; /* failed (invalid call) */
91 context->hidden.windowsio.buffer.data =
92 (char *) SDL_malloc(READAHEAD_BUFFER_SIZE);
93 if (!context->hidden.windowsio.buffer.data) {
94 return SDL_OutOfMemory();
96 /* Do not open a dialog box if failure */
98 SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
101 LPTSTR tstr = WIN_UTF8ToString(filename);
102 h = CreateFile(tstr, (w_right | r_right),
103 (w_right) ? 0 : FILE_SHARE_READ, NULL,
104 (must_exist | truncate | a_mode),
105 FILE_ATTRIBUTE_NORMAL, NULL);
109 /* restore old behavior */
110 SetErrorMode(old_error_mode);
112 if (h == INVALID_HANDLE_VALUE) {
113 SDL_free(context->hidden.windowsio.buffer.data);
114 context->hidden.windowsio.buffer.data = NULL;
115 SDL_SetError("Couldn't open %s", filename);
116 return -2; /* failed (CreateFile) */
118 context->hidden.windowsio.h = h;
119 context->hidden.windowsio.append = a_mode ? SDL_TRUE : SDL_FALSE;
124 static Sint64 SDLCALL
125 windows_file_size(SDL_RWops * context)
129 if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE) {
130 return SDL_SetError("windows_file_size: invalid context/file not opened");
133 if (!GetFileSizeEx(context->hidden.windowsio.h, &size)) {
134 return WIN_SetError("windows_file_size");
137 return size.QuadPart;
140 static Sint64 SDLCALL
141 windows_file_seek(SDL_RWops * context, Sint64 offset, int whence)
144 LARGE_INTEGER windowsoffset;
146 if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE) {
147 return SDL_SetError("windows_file_seek: invalid context/file not opened");
150 /* FIXME: We may be able to satisfy the seek within buffered data */
151 if (whence == RW_SEEK_CUR && context->hidden.windowsio.buffer.left) {
152 offset -= (long)context->hidden.windowsio.buffer.left;
154 context->hidden.windowsio.buffer.left = 0;
158 windowswhence = FILE_BEGIN;
161 windowswhence = FILE_CURRENT;
164 windowswhence = FILE_END;
167 return SDL_SetError("windows_file_seek: Unknown value for 'whence'");
170 windowsoffset.QuadPart = offset;
171 if (!SetFilePointerEx(context->hidden.windowsio.h, windowsoffset, &windowsoffset, windowswhence)) {
172 return WIN_SetError("windows_file_seek");
174 return windowsoffset.QuadPart;
177 static size_t SDLCALL
178 windows_file_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
181 size_t total_read = 0;
185 total_need = size * maxnum;
187 if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE
191 if (context->hidden.windowsio.buffer.left > 0) {
192 void *data = (char *) context->hidden.windowsio.buffer.data +
193 context->hidden.windowsio.buffer.size -
194 context->hidden.windowsio.buffer.left;
196 SDL_min(total_need, context->hidden.windowsio.buffer.left);
197 SDL_memcpy(ptr, data, read_ahead);
198 context->hidden.windowsio.buffer.left -= read_ahead;
200 if (read_ahead == total_need) {
203 ptr = (char *) ptr + read_ahead;
204 total_need -= read_ahead;
205 total_read += read_ahead;
208 if (total_need < READAHEAD_BUFFER_SIZE) {
210 (context->hidden.windowsio.h, context->hidden.windowsio.buffer.data,
211 READAHEAD_BUFFER_SIZE, &byte_read, NULL)) {
212 SDL_Error(SDL_EFREAD);
215 read_ahead = SDL_min(total_need, (int) byte_read);
216 SDL_memcpy(ptr, context->hidden.windowsio.buffer.data, read_ahead);
217 context->hidden.windowsio.buffer.size = byte_read;
218 context->hidden.windowsio.buffer.left = byte_read - read_ahead;
219 total_read += read_ahead;
222 (context->hidden.windowsio.h, ptr, (DWORD)total_need, &byte_read, NULL)) {
223 SDL_Error(SDL_EFREAD);
226 total_read += byte_read;
228 return (total_read / size);
231 static size_t SDLCALL
232 windows_file_write(SDL_RWops * context, const void *ptr, size_t size,
240 total_bytes = size * num;
242 if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE
243 || total_bytes <= 0 || !size)
246 if (context->hidden.windowsio.buffer.left) {
247 SetFilePointer(context->hidden.windowsio.h,
248 -(LONG)context->hidden.windowsio.buffer.left, NULL,
250 context->hidden.windowsio.buffer.left = 0;
253 /* if in append mode, we must go to the EOF before write */
254 if (context->hidden.windowsio.append) {
255 if (SetFilePointer(context->hidden.windowsio.h, 0L, NULL, FILE_END) ==
256 INVALID_SET_FILE_POINTER) {
257 SDL_Error(SDL_EFWRITE);
263 (context->hidden.windowsio.h, ptr, (DWORD)total_bytes, &byte_written, NULL)) {
264 SDL_Error(SDL_EFWRITE);
268 nwritten = byte_written / size;
273 windows_file_close(SDL_RWops * context)
277 if (context->hidden.windowsio.h != INVALID_HANDLE_VALUE) {
278 CloseHandle(context->hidden.windowsio.h);
279 context->hidden.windowsio.h = INVALID_HANDLE_VALUE; /* to be sure */
281 SDL_free(context->hidden.windowsio.buffer.data);
282 context->hidden.windowsio.buffer.data = NULL;
287 #endif /* __WIN32__ */
291 /* Functions to read/write stdio file pointers */
293 static Sint64 SDLCALL
294 stdio_size(SDL_RWops * context)
298 pos = SDL_RWseek(context, 0, RW_SEEK_CUR);
302 size = SDL_RWseek(context, 0, RW_SEEK_END);
304 SDL_RWseek(context, pos, RW_SEEK_SET);
308 static Sint64 SDLCALL
309 stdio_seek(SDL_RWops * context, Sint64 offset, int whence)
312 if (fseeko64(context->hidden.stdio.fp, (off64_t)offset, whence) == 0) {
313 return ftello64(context->hidden.stdio.fp);
315 #elif defined(HAVE_FSEEKO)
316 if (fseeko(context->hidden.stdio.fp, (off_t)offset, whence) == 0) {
317 return ftello(context->hidden.stdio.fp);
319 #elif defined(HAVE__FSEEKI64)
320 if (_fseeki64(context->hidden.stdio.fp, offset, whence) == 0) {
321 return _ftelli64(context->hidden.stdio.fp);
324 if (fseek(context->hidden.stdio.fp, offset, whence) == 0) {
325 return (ftell(context->hidden.stdio.fp));
328 return SDL_Error(SDL_EFSEEK);
331 static size_t SDLCALL
332 stdio_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
336 nread = fread(ptr, size, maxnum, context->hidden.stdio.fp);
337 if (nread == 0 && ferror(context->hidden.stdio.fp)) {
338 SDL_Error(SDL_EFREAD);
343 static size_t SDLCALL
344 stdio_write(SDL_RWops * context, const void *ptr, size_t size, size_t num)
348 nwrote = fwrite(ptr, size, num, context->hidden.stdio.fp);
349 if (nwrote == 0 && ferror(context->hidden.stdio.fp)) {
350 SDL_Error(SDL_EFWRITE);
356 stdio_close(SDL_RWops * context)
360 if (context->hidden.stdio.autoclose) {
361 /* WARNING: Check the return value here! */
362 if (fclose(context->hidden.stdio.fp) != 0) {
363 status = SDL_Error(SDL_EFWRITE);
370 #endif /* !HAVE_STDIO_H */
372 /* Functions to read/write memory pointers */
374 static Sint64 SDLCALL
375 mem_size(SDL_RWops * context)
377 return (Sint64)(context->hidden.mem.stop - context->hidden.mem.base);
380 static Sint64 SDLCALL
381 mem_seek(SDL_RWops * context, Sint64 offset, int whence)
387 newpos = context->hidden.mem.base + offset;
390 newpos = context->hidden.mem.here + offset;
393 newpos = context->hidden.mem.stop + offset;
396 return SDL_SetError("Unknown value for 'whence'");
398 if (newpos < context->hidden.mem.base) {
399 newpos = context->hidden.mem.base;
401 if (newpos > context->hidden.mem.stop) {
402 newpos = context->hidden.mem.stop;
404 context->hidden.mem.here = newpos;
405 return (Sint64)(context->hidden.mem.here - context->hidden.mem.base);
408 static size_t SDLCALL
409 mem_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
412 size_t mem_available;
414 total_bytes = (maxnum * size);
415 if ((maxnum <= 0) || (size <= 0)
416 || ((total_bytes / maxnum) != (size_t) size)) {
420 mem_available = (context->hidden.mem.stop - context->hidden.mem.here);
421 if (total_bytes > mem_available) {
422 total_bytes = mem_available;
425 SDL_memcpy(ptr, context->hidden.mem.here, total_bytes);
426 context->hidden.mem.here += total_bytes;
428 return (total_bytes / size);
431 static size_t SDLCALL
432 mem_write(SDL_RWops * context, const void *ptr, size_t size, size_t num)
434 if ((context->hidden.mem.here + (num * size)) > context->hidden.mem.stop) {
435 num = (context->hidden.mem.stop - context->hidden.mem.here) / size;
437 SDL_memcpy(context->hidden.mem.here, ptr, num * size);
438 context->hidden.mem.here += num * size;
442 static size_t SDLCALL
443 mem_writeconst(SDL_RWops * context, const void *ptr, size_t size, size_t num)
445 SDL_SetError("Can't write to read-only memory");
450 mem_close(SDL_RWops * context)
459 /* Functions to create SDL_RWops structures from various data sources */
462 SDL_RWFromFile(const char *file, const char *mode)
464 SDL_RWops *rwops = NULL;
465 if (!file || !*file || !mode || !*mode) {
466 SDL_SetError("SDL_RWFromFile(): No file or no mode specified");
471 /* Try to open the file on the filesystem first */
473 FILE *fp = fopen(file, mode);
475 return SDL_RWFromFP(fp, 1);
478 /* Try opening it from internal storage if it's a relative path */
482 path = SDL_stack_alloc(char, PATH_MAX);
484 SDL_snprintf(path, PATH_MAX, "%s/%s",
485 SDL_AndroidGetInternalStoragePath(), file);
486 fp = fopen(path, mode);
487 SDL_stack_free(path);
489 return SDL_RWFromFP(fp, 1);
493 #endif /* HAVE_STDIO_H */
495 /* Try to open the file from the asset system */
496 rwops = SDL_AllocRW();
498 return NULL; /* SDL_SetError already setup by SDL_AllocRW() */
499 if (Android_JNI_FileOpen(rwops, file, mode) < 0) {
503 rwops->size = Android_JNI_FileSize;
504 rwops->seek = Android_JNI_FileSeek;
505 rwops->read = Android_JNI_FileRead;
506 rwops->write = Android_JNI_FileWrite;
507 rwops->close = Android_JNI_FileClose;
508 rwops->type = SDL_RWOPS_JNIFILE;
510 #elif defined(__WIN32__)
511 rwops = SDL_AllocRW();
513 return NULL; /* SDL_SetError already setup by SDL_AllocRW() */
514 if (windows_file_open(rwops, file, mode) < 0) {
518 rwops->size = windows_file_size;
519 rwops->seek = windows_file_seek;
520 rwops->read = windows_file_read;
521 rwops->write = windows_file_write;
522 rwops->close = windows_file_close;
523 rwops->type = SDL_RWOPS_WINFILE;
528 FILE *fp = SDL_OpenFPFromBundleOrFallback(file, mode);
531 fopen_s(&fp, file, mode);
533 FILE *fp = fopen(file, mode);
536 SDL_SetError("Couldn't open %s", file);
538 rwops = SDL_RWFromFP(fp, 1);
542 SDL_SetError("SDL not compiled with stdio support");
543 #endif /* !HAVE_STDIO_H */
550 SDL_RWFromFP(FILE * fp, SDL_bool autoclose)
552 SDL_RWops *rwops = NULL;
554 rwops = SDL_AllocRW();
556 rwops->size = stdio_size;
557 rwops->seek = stdio_seek;
558 rwops->read = stdio_read;
559 rwops->write = stdio_write;
560 rwops->close = stdio_close;
561 rwops->hidden.stdio.fp = fp;
562 rwops->hidden.stdio.autoclose = autoclose;
563 rwops->type = SDL_RWOPS_STDFILE;
569 SDL_RWFromFP(void * fp, SDL_bool autoclose)
571 SDL_SetError("SDL not compiled with stdio support");
574 #endif /* HAVE_STDIO_H */
577 SDL_RWFromMem(void *mem, int size)
579 SDL_RWops *rwops = NULL;
581 SDL_InvalidParamError("mem");
585 SDL_InvalidParamError("size");
589 rwops = SDL_AllocRW();
591 rwops->size = mem_size;
592 rwops->seek = mem_seek;
593 rwops->read = mem_read;
594 rwops->write = mem_write;
595 rwops->close = mem_close;
596 rwops->hidden.mem.base = (Uint8 *) mem;
597 rwops->hidden.mem.here = rwops->hidden.mem.base;
598 rwops->hidden.mem.stop = rwops->hidden.mem.base + size;
599 rwops->type = SDL_RWOPS_MEMORY;
605 SDL_RWFromConstMem(const void *mem, int size)
607 SDL_RWops *rwops = NULL;
609 SDL_InvalidParamError("mem");
613 SDL_InvalidParamError("size");
617 rwops = SDL_AllocRW();
619 rwops->size = mem_size;
620 rwops->seek = mem_seek;
621 rwops->read = mem_read;
622 rwops->write = mem_writeconst;
623 rwops->close = mem_close;
624 rwops->hidden.mem.base = (Uint8 *) mem;
625 rwops->hidden.mem.here = rwops->hidden.mem.base;
626 rwops->hidden.mem.stop = rwops->hidden.mem.base + size;
627 rwops->type = SDL_RWOPS_MEMORY_RO;
637 area = (SDL_RWops *) SDL_malloc(sizeof *area);
641 area->type = SDL_RWOPS_UNKNOWN;
647 SDL_FreeRW(SDL_RWops * area)
652 /* Functions for dynamically reading and writing endian-specific values */
655 SDL_ReadU8(SDL_RWops * src)
659 SDL_RWread(src, &value, (sizeof value), 1);
664 SDL_ReadLE16(SDL_RWops * src)
668 SDL_RWread(src, &value, (sizeof value), 1);
669 return (SDL_SwapLE16(value));
673 SDL_ReadBE16(SDL_RWops * src)
677 SDL_RWread(src, &value, (sizeof value), 1);
678 return (SDL_SwapBE16(value));
682 SDL_ReadLE32(SDL_RWops * src)
686 SDL_RWread(src, &value, (sizeof value), 1);
687 return (SDL_SwapLE32(value));
691 SDL_ReadBE32(SDL_RWops * src)
695 SDL_RWread(src, &value, (sizeof value), 1);
696 return (SDL_SwapBE32(value));
700 SDL_ReadLE64(SDL_RWops * src)
704 SDL_RWread(src, &value, (sizeof value), 1);
705 return (SDL_SwapLE64(value));
709 SDL_ReadBE64(SDL_RWops * src)
713 SDL_RWread(src, &value, (sizeof value), 1);
714 return (SDL_SwapBE64(value));
718 SDL_WriteU8(SDL_RWops * dst, Uint8 value)
720 return (SDL_RWwrite(dst, &value, (sizeof value), 1));
724 SDL_WriteLE16(SDL_RWops * dst, Uint16 value)
726 value = SDL_SwapLE16(value);
727 return (SDL_RWwrite(dst, &value, (sizeof value), 1));
731 SDL_WriteBE16(SDL_RWops * dst, Uint16 value)
733 value = SDL_SwapBE16(value);
734 return (SDL_RWwrite(dst, &value, (sizeof value), 1));
738 SDL_WriteLE32(SDL_RWops * dst, Uint32 value)
740 value = SDL_SwapLE32(value);
741 return (SDL_RWwrite(dst, &value, (sizeof value), 1));
745 SDL_WriteBE32(SDL_RWops * dst, Uint32 value)
747 value = SDL_SwapBE32(value);
748 return (SDL_RWwrite(dst, &value, (sizeof value), 1));
752 SDL_WriteLE64(SDL_RWops * dst, Uint64 value)
754 value = SDL_SwapLE64(value);
755 return (SDL_RWwrite(dst, &value, (sizeof value), 1));
759 SDL_WriteBE64(SDL_RWops * dst, Uint64 value)
761 value = SDL_SwapBE64(value);
762 return (SDL_RWwrite(dst, &value, (sizeof value), 1));
765 /* vi: set ts=4 sw=4 expandtab: */