1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
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 
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. */
24 #ifndef __QNXNTO__
25 /* Need this so Linux systems define fseek64o, ftell64o and off64_t */
26 #ifndef _LARGEFILE64_SOURCE
27 #define _LARGEFILE64_SOURCE
28 #endif
29 #endif
30 
31 #include "../SDL_internal.h"
32 
33 #if defined(__WIN32__)
34 #include "../core/windows/SDL_windows.h"
35 #endif
36 
37 #ifdef HAVE_STDIO_H
38 #include <stdio.h>
39 #endif
40 
41 #ifdef HAVE_LIMITS_H
42 #include <limits.h>
43 #endif
44 
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.
47 */
48 
49 #include "SDL_endian.h"
50 #include "SDL_rwops.h"
51 
52 #ifdef __APPLE__
53 #include "cocoa/SDL_rwopsbundlesupport.h"
54 #endif /* __APPLE__ */
55 
56 #ifdef __ANDROID__
57 #include "../core/android/SDL_android.h"
58 #include "SDL_system.h"
59 #endif
60 
61 #if __NACL__
62 #ifndef __ALIOS__
63 #include "nacl_io/nacl_io.h"
64 #endif
65 #endif
66 
67 #ifdef __WIN32__
68 
69 /* Functions to read/write Win32 API file pointers */
70 
71 #ifndef INVALID_SET_FILE_POINTER
72 #define INVALID_SET_FILE_POINTER 0xFFFFFFFF
73 #endif
74 
75 #define READAHEAD_BUFFER_SIZE   1024
76 
77 static int SDLCALL
windows_file_open(SDL_RWops * context,const char * filename,const char * mode)78 windows_file_open(SDL_RWops * context, const char *filename, const char *mode)
79 {
80     UINT old_error_mode;
81     HANDLE h;
82     DWORD r_right, w_right;
83     DWORD must_exist, truncate;
84     int a_mode;
85 
86     if (!context)
87         return -1;              /* failed (invalid call) */
88 
89     context->hidden.windowsio.h = INVALID_HANDLE_VALUE;   /* mark this as unusable */
90     context->hidden.windowsio.buffer.data = NULL;
91     context->hidden.windowsio.buffer.size = 0;
92     context->hidden.windowsio.buffer.left = 0;
93 
94     /* "r" = reading, file must exist */
95     /* "w" = writing, truncate existing, file may not exist */
96     /* "r+"= reading or writing, file must exist            */
97     /* "a" = writing, append file may not exist             */
98     /* "a+"= append + read, file may not exist              */
99     /* "w+" = read, write, truncate. file may not exist    */
100 
101     must_exist = (SDL_strchr(mode, 'r') != NULL) ? OPEN_EXISTING : 0;
102     truncate = (SDL_strchr(mode, 'w') != NULL) ? CREATE_ALWAYS : 0;
103     r_right = (SDL_strchr(mode, '+') != NULL
104                || must_exist) ? GENERIC_READ : 0;
105     a_mode = (SDL_strchr(mode, 'a') != NULL) ? OPEN_ALWAYS : 0;
106     w_right = (a_mode || SDL_strchr(mode, '+')
107                || truncate) ? GENERIC_WRITE : 0;
108 
109     if (!r_right && !w_right)   /* inconsistent mode */
110         return -1;              /* failed (invalid call) */
111 
112     context->hidden.windowsio.buffer.data =
113         (char *) SDL_malloc(READAHEAD_BUFFER_SIZE);
114     if (!context->hidden.windowsio.buffer.data) {
115         return SDL_OutOfMemory();
116     }
117     /* Do not open a dialog box if failure */
118     old_error_mode =
119         SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
120 
121     {
122         LPTSTR tstr = WIN_UTF8ToString(filename);
123         h = CreateFile(tstr, (w_right | r_right),
124                        (w_right) ? 0 : FILE_SHARE_READ, NULL,
125                        (must_exist | truncate | a_mode),
126                        FILE_ATTRIBUTE_NORMAL, NULL);
127         SDL_free(tstr);
128     }
129 
130     /* restore old behavior */
131     SetErrorMode(old_error_mode);
132 
133     if (h == INVALID_HANDLE_VALUE) {
134         SDL_free(context->hidden.windowsio.buffer.data);
135         context->hidden.windowsio.buffer.data = NULL;
136         SDL_SetError("Couldn't open %s", filename);
137         return -2;              /* failed (CreateFile) */
138     }
139     context->hidden.windowsio.h = h;
140     context->hidden.windowsio.append = a_mode ? SDL_TRUE : SDL_FALSE;
141 
142     return 0;                   /* ok */
143 }
144 
145 static Sint64 SDLCALL
windows_file_size(SDL_RWops * context)146 windows_file_size(SDL_RWops * context)
147 {
148     LARGE_INTEGER size;
149 
150     if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE) {
151         return SDL_SetError("windows_file_size: invalid context/file not opened");
152     }
153 
154     if (!GetFileSizeEx(context->hidden.windowsio.h, &size)) {
155         return WIN_SetError("windows_file_size");
156     }
157 
158     return size.QuadPart;
159 }
160 
161 static Sint64 SDLCALL
windows_file_seek(SDL_RWops * context,Sint64 offset,int whence)162 windows_file_seek(SDL_RWops * context, Sint64 offset, int whence)
163 {
164     DWORD windowswhence;
165     LARGE_INTEGER windowsoffset;
166 
167     if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE) {
168         return SDL_SetError("windows_file_seek: invalid context/file not opened");
169     }
170 
171     /* FIXME: We may be able to satisfy the seek within buffered data */
172     if (whence == RW_SEEK_CUR && context->hidden.windowsio.buffer.left) {
173         offset -= (long)context->hidden.windowsio.buffer.left;
174     }
175     context->hidden.windowsio.buffer.left = 0;
176 
177     switch (whence) {
178     case RW_SEEK_SET:
179         windowswhence = FILE_BEGIN;
180         break;
181     case RW_SEEK_CUR:
182         windowswhence = FILE_CURRENT;
183         break;
184     case RW_SEEK_END:
185         windowswhence = FILE_END;
186         break;
187     default:
188         return SDL_SetError("windows_file_seek: Unknown value for 'whence'");
189     }
190 
191     windowsoffset.QuadPart = offset;
192     if (!SetFilePointerEx(context->hidden.windowsio.h, windowsoffset, &windowsoffset, windowswhence)) {
193         return WIN_SetError("windows_file_seek");
194     }
195     return windowsoffset.QuadPart;
196 }
197 
198 static size_t SDLCALL
windows_file_read(SDL_RWops * context,void * ptr,size_t size,size_t maxnum)199 windows_file_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
200 {
201     size_t total_need;
202     size_t total_read = 0;
203     size_t read_ahead;
204     DWORD byte_read;
205 
206     total_need = size * maxnum;
207 
208     if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE
209         || !total_need)
210         return 0;
211 
212     if (context->hidden.windowsio.buffer.left > 0) {
213         void *data = (char *) context->hidden.windowsio.buffer.data +
214             context->hidden.windowsio.buffer.size -
215             context->hidden.windowsio.buffer.left;
216         read_ahead =
217             SDL_min(total_need, context->hidden.windowsio.buffer.left);
218         SDL_memcpy(ptr, data, read_ahead);
219         context->hidden.windowsio.buffer.left -= read_ahead;
220 
221         if (read_ahead == total_need) {
222             return maxnum;
223         }
224         ptr = (char *) ptr + read_ahead;
225         total_need -= read_ahead;
226         total_read += read_ahead;
227     }
228 
229     if (total_need < READAHEAD_BUFFER_SIZE) {
230         if (!ReadFile
231             (context->hidden.windowsio.h, context->hidden.windowsio.buffer.data,
232              READAHEAD_BUFFER_SIZE, &byte_read, NULL)) {
233             SDL_Error(SDL_EFREAD);
234             return 0;
235         }
236         read_ahead = SDL_min(total_need, (int) byte_read);
237         SDL_memcpy(ptr, context->hidden.windowsio.buffer.data, read_ahead);
238         context->hidden.windowsio.buffer.size = byte_read;
239         context->hidden.windowsio.buffer.left = byte_read - read_ahead;
240         total_read += read_ahead;
241     } else {
242         if (!ReadFile
243             (context->hidden.windowsio.h, ptr, (DWORD)total_need, &byte_read, NULL)) {
244             SDL_Error(SDL_EFREAD);
245             return 0;
246         }
247         total_read += byte_read;
248     }
249     return (total_read / size);
250 }
251 
252 static size_t SDLCALL
windows_file_write(SDL_RWops * context,const void * ptr,size_t size,size_t num)253 windows_file_write(SDL_RWops * context, const void *ptr, size_t size,
254                  size_t num)
255 {
256 
257     size_t total_bytes;
258     DWORD byte_written;
259     size_t nwritten;
260 
261     total_bytes = size * num;
262 
263     if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE
264         || total_bytes <= 0 || !size)
265         return 0;
266 
267     if (context->hidden.windowsio.buffer.left) {
268         SetFilePointer(context->hidden.windowsio.h,
269                        -(LONG)context->hidden.windowsio.buffer.left, NULL,
270                        FILE_CURRENT);
271         context->hidden.windowsio.buffer.left = 0;
272     }
273 
274     /* if in append mode, we must go to the EOF before write */
275     if (context->hidden.windowsio.append) {
276         if (SetFilePointer(context->hidden.windowsio.h, 0L, NULL, FILE_END) ==
277             INVALID_SET_FILE_POINTER) {
278             SDL_Error(SDL_EFWRITE);
279             return 0;
280         }
281     }
282 
283     if (!WriteFile
284         (context->hidden.windowsio.h, ptr, (DWORD)total_bytes, &byte_written, NULL)) {
285         SDL_Error(SDL_EFWRITE);
286         return 0;
287     }
288 
289     nwritten = byte_written / size;
290     return nwritten;
291 }
292 
293 static int SDLCALL
windows_file_close(SDL_RWops * context)294 windows_file_close(SDL_RWops * context)
295 {
296 
297     if (context) {
298         if (context->hidden.windowsio.h != INVALID_HANDLE_VALUE) {
299             CloseHandle(context->hidden.windowsio.h);
300             context->hidden.windowsio.h = INVALID_HANDLE_VALUE;   /* to be sure */
301         }
302         SDL_free(context->hidden.windowsio.buffer.data);
303         context->hidden.windowsio.buffer.data = NULL;
304         SDL_FreeRW(context);
305     }
306     return 0;
307 }
308 #endif /* __WIN32__ */
309 
310 #ifdef HAVE_STDIO_H
311 
312 #ifdef HAVE_FOPEN64
313 #define fopen   fopen64
314 #endif
315 #ifdef HAVE_FSEEKO64
316 #define fseek_off_t off64_t
317 #define fseek   fseeko64
318 #define ftell   ftello64
319 #elif defined(HAVE_FSEEKO)
320 #if defined(OFF_MIN) && defined(OFF_MAX)
321 #define FSEEK_OFF_MIN OFF_MIN
322 #define FSEEK_OFF_MAX OFF_MAX
323 #elif defined(HAVE_LIMITS_H)
324 /* POSIX doesn't specify the minimum and maximum macros for off_t so
325  * we have to improvise and dance around implementation-defined
326  * behavior. This may fail if the off_t type has padding bits or
327  * is not a two's-complement representation. The compilers will detect
328  * and eliminate the dead code if off_t has 64 bits.
329  */
330 #define FSEEK_OFF_MAX (((((off_t)1 << (sizeof(off_t) * CHAR_BIT - 2)) - 1) << 1) + 1)
331 #define FSEEK_OFF_MIN (-(FSEEK_OFF_MAX) - 1)
332 #endif
333 #define fseek_off_t off_t
334 #define fseek   fseeko
335 #define ftell   ftello
336 #elif defined(HAVE__FSEEKI64)
337 #define fseek_off_t __int64
338 #define fseek   _fseeki64
339 #define ftell   _ftelli64
340 #else
341 #ifdef HAVE_LIMITS_H
342 #define FSEEK_OFF_MIN LONG_MIN
343 #define FSEEK_OFF_MAX LONG_MAX
344 #endif
345 #define fseek_off_t long
346 #endif
347 
348 /* Functions to read/write stdio file pointers */
349 
350 static Sint64 SDLCALL
stdio_size(SDL_RWops * context)351 stdio_size(SDL_RWops * context)
352 {
353     Sint64 pos, size;
354 
355     pos = SDL_RWseek(context, 0, RW_SEEK_CUR);
356     if (pos < 0) {
357         return -1;
358     }
359     size = SDL_RWseek(context, 0, RW_SEEK_END);
360 
361     SDL_RWseek(context, pos, RW_SEEK_SET);
362     return size;
363 }
364 
365 static Sint64 SDLCALL
stdio_seek(SDL_RWops * context,Sint64 offset,int whence)366 stdio_seek(SDL_RWops * context, Sint64 offset, int whence)
367 {
368     int stdiowhence;
369 
370     switch (whence) {
371     case RW_SEEK_SET:
372         stdiowhence = SEEK_SET;
373         break;
374     case RW_SEEK_CUR:
375         stdiowhence = SEEK_CUR;
376         break;
377     case RW_SEEK_END:
378         stdiowhence = SEEK_END;
379         break;
380     default:
381         return SDL_SetError("Unknown value for 'whence'");
382     }
383 
384 #if defined(FSEEK_OFF_MIN) && defined(FSEEK_OFF_MAX)
385     if (offset < (Sint64)(FSEEK_OFF_MIN) || offset > (Sint64)(FSEEK_OFF_MAX)) {
386         return SDL_SetError("Seek offset out of range");
387     }
388 #endif
389 
390     if (fseek(context->hidden.stdio.fp, (fseek_off_t)offset, stdiowhence) == 0) {
391         Sint64 pos = ftell(context->hidden.stdio.fp);
392         if (pos < 0) {
393             return SDL_SetError("Couldn't get stream offset");
394         }
395         return pos;
396     }
397     return SDL_Error(SDL_EFSEEK);
398 }
399 
400 static size_t SDLCALL
stdio_read(SDL_RWops * context,void * ptr,size_t size,size_t maxnum)401 stdio_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
402 {
403     size_t nread;
404 
405     nread = fread(ptr, size, maxnum, context->hidden.stdio.fp);
406     if (nread == 0 && ferror(context->hidden.stdio.fp)) {
407         SDL_Error(SDL_EFREAD);
408     }
409     return nread;
410 }
411 
412 static size_t SDLCALL
stdio_write(SDL_RWops * context,const void * ptr,size_t size,size_t num)413 stdio_write(SDL_RWops * context, const void *ptr, size_t size, size_t num)
414 {
415     size_t nwrote;
416 
417     nwrote = fwrite(ptr, size, num, context->hidden.stdio.fp);
418     if (nwrote == 0 && ferror(context->hidden.stdio.fp)) {
419         SDL_Error(SDL_EFWRITE);
420     }
421     return nwrote;
422 }
423 
424 static int SDLCALL
stdio_close(SDL_RWops * context)425 stdio_close(SDL_RWops * context)
426 {
427     int status = 0;
428     if (context) {
429         if (context->hidden.stdio.autoclose) {
430             /* WARNING:  Check the return value here! */
431             if (fclose(context->hidden.stdio.fp) != 0) {
432                 status = SDL_Error(SDL_EFWRITE);
433             }
434         }
435         SDL_FreeRW(context);
436     }
437     return status;
438 }
439 #endif /* !HAVE_STDIO_H */
440 
441 /* Functions to read/write memory pointers */
442 
443 static Sint64 SDLCALL
mem_size(SDL_RWops * context)444 mem_size(SDL_RWops * context)
445 {
446     return (Sint64)(context->hidden.mem.stop - context->hidden.mem.base);
447 }
448 
449 static Sint64 SDLCALL
mem_seek(SDL_RWops * context,Sint64 offset,int whence)450 mem_seek(SDL_RWops * context, Sint64 offset, int whence)
451 {
452     Uint8 *newpos;
453 
454     switch (whence) {
455     case RW_SEEK_SET:
456         newpos = context->hidden.mem.base + offset;
457         break;
458     case RW_SEEK_CUR:
459         newpos = context->hidden.mem.here + offset;
460         break;
461     case RW_SEEK_END:
462         newpos = context->hidden.mem.stop + offset;
463         break;
464     default:
465         return SDL_SetError("Unknown value for 'whence'");
466     }
467     if (newpos < context->hidden.mem.base) {
468         newpos = context->hidden.mem.base;
469     }
470     if (newpos > context->hidden.mem.stop) {
471         newpos = context->hidden.mem.stop;
472     }
473     context->hidden.mem.here = newpos;
474     return (Sint64)(context->hidden.mem.here - context->hidden.mem.base);
475 }
476 
477 static size_t SDLCALL
mem_read(SDL_RWops * context,void * ptr,size_t size,size_t maxnum)478 mem_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
479 {
480     size_t total_bytes;
481     size_t mem_available;
482 
483     total_bytes = (maxnum * size);
484     if ((maxnum <= 0) || (size <= 0)
485         || ((total_bytes / maxnum) != size)) {
486         return 0;
487     }
488 
489     mem_available = (context->hidden.mem.stop - context->hidden.mem.here);
490     if (total_bytes > mem_available) {
491         total_bytes = mem_available;
492     }
493 
494     SDL_memcpy(ptr, context->hidden.mem.here, total_bytes);
495     context->hidden.mem.here += total_bytes;
496 
497     return (total_bytes / size);
498 }
499 
500 static size_t SDLCALL
mem_write(SDL_RWops * context,const void * ptr,size_t size,size_t num)501 mem_write(SDL_RWops * context, const void *ptr, size_t size, size_t num)
502 {
503     if ((context->hidden.mem.here + (num * size)) > context->hidden.mem.stop) {
504         num = (context->hidden.mem.stop - context->hidden.mem.here) / size;
505     }
506     SDL_memcpy(context->hidden.mem.here, ptr, num * size);
507     context->hidden.mem.here += num * size;
508     return num;
509 }
510 
511 static size_t SDLCALL
mem_writeconst(SDL_RWops * context,const void * ptr,size_t size,size_t num)512 mem_writeconst(SDL_RWops * context, const void *ptr, size_t size, size_t num)
513 {
514     SDL_SetError("Can't write to read-only memory");
515     return 0;
516 }
517 
518 static int SDLCALL
mem_close(SDL_RWops * context)519 mem_close(SDL_RWops * context)
520 {
521     if (context) {
522         SDL_FreeRW(context);
523     }
524     return 0;
525 }
526 
527 
528 /* Functions to create SDL_RWops structures from various data sources */
529 
530 SDL_RWops *
SDL_RWFromFile(const char * file,const char * mode)531 SDL_RWFromFile(const char *file, const char *mode)
532 {
533     SDL_RWops *rwops = NULL;
534     if (!file || !*file || !mode || !*mode) {
535         SDL_SetError("SDL_RWFromFile(): No file or no mode specified");
536         return NULL;
537     }
538 #if defined(__ANDROID__)
539 #ifdef HAVE_STDIO_H
540     /* Try to open the file on the filesystem first */
541     if (*file == '/') {
542         FILE *fp = fopen(file, mode);
543         if (fp) {
544             return SDL_RWFromFP(fp, 1);
545         }
546     } else {
547         /* Try opening it from internal storage if it's a relative path */
548         char *path;
549         FILE *fp;
550 
551         /* !!! FIXME: why not just "char path[PATH_MAX];" ? */
552         path = SDL_stack_alloc(char, PATH_MAX);
553         if (path) {
554             SDL_snprintf(path, PATH_MAX, "%s/%s",
555                          SDL_AndroidGetInternalStoragePath(), file);
556             fp = fopen(path, mode);
557             SDL_stack_free(path);
558             if (fp) {
559                 return SDL_RWFromFP(fp, 1);
560             }
561         }
562     }
563 #endif /* HAVE_STDIO_H */
564 
565     /* Try to open the file from the asset system */
566     rwops = SDL_AllocRW();
567     if (!rwops)
568         return NULL;            /* SDL_SetError already setup by SDL_AllocRW() */
569     if (Android_JNI_FileOpen(rwops, file, mode) < 0) {
570         SDL_FreeRW(rwops);
571         return NULL;
572     }
573     rwops->size = Android_JNI_FileSize;
574     rwops->seek = Android_JNI_FileSeek;
575     rwops->read = Android_JNI_FileRead;
576     rwops->write = Android_JNI_FileWrite;
577     rwops->close = Android_JNI_FileClose;
578     rwops->type = SDL_RWOPS_JNIFILE;
579 
580 #elif defined(__WIN32__)
581     rwops = SDL_AllocRW();
582     if (!rwops)
583         return NULL;            /* SDL_SetError already setup by SDL_AllocRW() */
584     if (windows_file_open(rwops, file, mode) < 0) {
585         SDL_FreeRW(rwops);
586         return NULL;
587     }
588     rwops->size = windows_file_size;
589     rwops->seek = windows_file_seek;
590     rwops->read = windows_file_read;
591     rwops->write = windows_file_write;
592     rwops->close = windows_file_close;
593     rwops->type = SDL_RWOPS_WINFILE;
594 
595 #elif HAVE_STDIO_H
596     {
597         #ifdef __APPLE__
598         FILE *fp = SDL_OpenFPFromBundleOrFallback(file, mode);
599         #elif __WINRT__
600         FILE *fp = NULL;
601         fopen_s(&fp, file, mode);
602         #else
603         FILE *fp = fopen(file, mode);
604         #endif
605         if (fp == NULL) {
606             SDL_SetError("Couldn't open %s", file);
607         } else {
608             rwops = SDL_RWFromFP(fp, SDL_TRUE);
609             if (!rwops)
610                 printf("rwops is null xxx\n");
611         }
612     }
613 #else
614     SDL_SetError("SDL not compiled with stdio support");
615 #endif /* !HAVE_STDIO_H */
616 
617     return rwops;
618 }
619 
620 #ifdef HAVE_STDIO_H
621 SDL_RWops *
SDL_RWFromFP(FILE * fp,SDL_bool autoclose)622 SDL_RWFromFP(FILE * fp, SDL_bool autoclose)
623 {
624     SDL_RWops *rwops = NULL;
625 
626     rwops = SDL_AllocRW();
627     if (rwops != NULL) {
628         rwops->size = stdio_size;
629         rwops->seek = stdio_seek;
630         rwops->read = stdio_read;
631         rwops->write = stdio_write;
632         rwops->close = stdio_close;
633         rwops->hidden.stdio.fp = fp;
634         rwops->hidden.stdio.autoclose = autoclose;
635         rwops->type = SDL_RWOPS_STDFILE;
636     }
637     return rwops;
638 }
639 #else
640 SDL_RWops *
SDL_RWFromFP(void * fp,SDL_bool autoclose)641 SDL_RWFromFP(void * fp, SDL_bool autoclose)
642 {
643     SDL_SetError("SDL not compiled with stdio support");
644     return NULL;
645 }
646 #endif /* HAVE_STDIO_H */
647 
648 SDL_RWops *
SDL_RWFromMem(void * mem,int size)649 SDL_RWFromMem(void *mem, int size)
650 {
651     SDL_RWops *rwops = NULL;
652     if (!mem) {
653       SDL_InvalidParamError("mem");
654       return rwops;
655     }
656     if (!size) {
657       SDL_InvalidParamError("size");
658       return rwops;
659     }
660 
661     rwops = SDL_AllocRW();
662     if (rwops != NULL) {
663         rwops->size = mem_size;
664         rwops->seek = mem_seek;
665         rwops->read = mem_read;
666         rwops->write = mem_write;
667         rwops->close = mem_close;
668         rwops->hidden.mem.base = (Uint8 *) mem;
669         rwops->hidden.mem.here = rwops->hidden.mem.base;
670         rwops->hidden.mem.stop = rwops->hidden.mem.base + size;
671         rwops->type = SDL_RWOPS_MEMORY;
672     }
673     return rwops;
674 }
675 
676 SDL_RWops *
SDL_RWFromConstMem(const void * mem,int size)677 SDL_RWFromConstMem(const void *mem, int size)
678 {
679     SDL_RWops *rwops = NULL;
680     if (!mem) {
681       SDL_InvalidParamError("mem");
682       return rwops;
683     }
684     if (!size) {
685       SDL_InvalidParamError("size");
686       return rwops;
687     }
688 
689     rwops = SDL_AllocRW();
690     if (rwops != NULL) {
691         rwops->size = mem_size;
692         rwops->seek = mem_seek;
693         rwops->read = mem_read;
694         rwops->write = mem_writeconst;
695         rwops->close = mem_close;
696         rwops->hidden.mem.base = (Uint8 *) mem;
697         rwops->hidden.mem.here = rwops->hidden.mem.base;
698         rwops->hidden.mem.stop = rwops->hidden.mem.base + size;
699         rwops->type = SDL_RWOPS_MEMORY_RO;
700     }
701     return rwops;
702 }
703 
704 SDL_RWops *
SDL_AllocRW(void)705 SDL_AllocRW(void)
706 {
707     SDL_RWops *area;
708 
709     area = (SDL_RWops *) SDL_malloc(sizeof *area);
710     if (area == NULL) {
711         SDL_OutOfMemory();
712     } else {
713         area->type = SDL_RWOPS_UNKNOWN;
714     }
715     return area;
716 }
717 
718 void
SDL_FreeRW(SDL_RWops * area)719 SDL_FreeRW(SDL_RWops * area)
720 {
721     SDL_free(area);
722 }
723 
724 /* Load all the data from an SDL data stream */
725 void *
SDL_LoadFile_RW(SDL_RWops * src,size_t * datasize,int freesrc)726 SDL_LoadFile_RW(SDL_RWops * src, size_t *datasize, int freesrc)
727 {
728     const int FILE_CHUNK_SIZE = 1024;
729     Sint64 size;
730     size_t size_read, size_total;
731     void *data = NULL, *newdata;
732 
733     if (!src) {
734         SDL_InvalidParamError("src");
735         return NULL;
736     }
737 
738     size = SDL_RWsize(src);
739     if (size < 0) {
740         size = FILE_CHUNK_SIZE;
741     }
742     data = SDL_malloc((size_t)(size + 1));
743 
744     size_total = 0;
745     for (;;) {
746         if ((((Sint64)size_total) + FILE_CHUNK_SIZE) > size) {
747             size = (size_total + FILE_CHUNK_SIZE);
748             newdata = SDL_realloc(data, (size_t)(size + 1));
749             if (!newdata) {
750                 SDL_free(data);
751                 data = NULL;
752                 SDL_OutOfMemory();
753                 goto done;
754             }
755             data = newdata;
756         }
757 
758         size_read = SDL_RWread(src, (char *)data+size_total, 1, (size_t)(size-size_total));
759         if (size_read == 0) {
760             break;
761         }
762         size_total += size_read;
763     }
764 
765     if (datasize) {
766         *datasize = size_total;
767     }
768     ((char *)data)[size_total] = '\0';
769 
770 done:
771     if (freesrc && src) {
772         SDL_RWclose(src);
773     }
774     return data;
775 }
776 
777 void *
SDL_LoadFile(const char * file,size_t * datasize)778 SDL_LoadFile(const char *file, size_t *datasize)
779 {
780    return SDL_LoadFile_RW(SDL_RWFromFile(file, "rb"), datasize, 1);
781 }
782 
783 Sint64
SDL_RWsize(SDL_RWops * context)784 SDL_RWsize(SDL_RWops *context)
785 {
786     return context->size(context);
787 }
788 
789 Sint64
SDL_RWseek(SDL_RWops * context,Sint64 offset,int whence)790 SDL_RWseek(SDL_RWops *context, Sint64 offset, int whence)
791 {
792     return context->seek(context, offset, whence);
793 }
794 
795 Sint64
SDL_RWtell(SDL_RWops * context)796 SDL_RWtell(SDL_RWops *context)
797 {
798     return context->seek(context, 0, RW_SEEK_CUR);
799 }
800 
801 size_t
SDL_RWread(SDL_RWops * context,void * ptr,size_t size,size_t maxnum)802 SDL_RWread(SDL_RWops *context, void *ptr, size_t size, size_t maxnum)
803 {
804     return context->read(context, ptr, size, maxnum);
805 }
806 
807 size_t
SDL_RWwrite(SDL_RWops * context,const void * ptr,size_t size,size_t num)808 SDL_RWwrite(SDL_RWops *context, const void *ptr, size_t size, size_t num)
809 {
810     return context->write(context, ptr, size, num);
811 }
812 
813 int
SDL_RWclose(SDL_RWops * context)814 SDL_RWclose(SDL_RWops *context)
815 {
816     return context->close(context);
817 }
818 
819 /* Functions for dynamically reading and writing endian-specific values */
820 
821 Uint8
SDL_ReadU8(SDL_RWops * src)822 SDL_ReadU8(SDL_RWops * src)
823 {
824     Uint8 value = 0;
825 
826     SDL_RWread(src, &value, sizeof (value), 1);
827     return value;
828 }
829 
830 Uint16
SDL_ReadLE16(SDL_RWops * src)831 SDL_ReadLE16(SDL_RWops * src)
832 {
833     Uint16 value = 0;
834 
835     SDL_RWread(src, &value, sizeof (value), 1);
836     return SDL_SwapLE16(value);
837 }
838 
839 Uint16
SDL_ReadBE16(SDL_RWops * src)840 SDL_ReadBE16(SDL_RWops * src)
841 {
842     Uint16 value = 0;
843 
844     SDL_RWread(src, &value, sizeof (value), 1);
845     return SDL_SwapBE16(value);
846 }
847 
848 Uint32
SDL_ReadLE32(SDL_RWops * src)849 SDL_ReadLE32(SDL_RWops * src)
850 {
851     Uint32 value = 0;
852 
853     SDL_RWread(src, &value, sizeof (value), 1);
854     return SDL_SwapLE32(value);
855 }
856 
857 Uint32
SDL_ReadBE32(SDL_RWops * src)858 SDL_ReadBE32(SDL_RWops * src)
859 {
860     Uint32 value = 0;
861 
862     SDL_RWread(src, &value, sizeof (value), 1);
863     return SDL_SwapBE32(value);
864 }
865 
866 Uint64
SDL_ReadLE64(SDL_RWops * src)867 SDL_ReadLE64(SDL_RWops * src)
868 {
869     Uint64 value = 0;
870 
871     SDL_RWread(src, &value, sizeof (value), 1);
872     return SDL_SwapLE64(value);
873 }
874 
875 Uint64
SDL_ReadBE64(SDL_RWops * src)876 SDL_ReadBE64(SDL_RWops * src)
877 {
878     Uint64 value = 0;
879 
880     SDL_RWread(src, &value, sizeof (value), 1);
881     return SDL_SwapBE64(value);
882 }
883 
884 size_t
SDL_WriteU8(SDL_RWops * dst,Uint8 value)885 SDL_WriteU8(SDL_RWops * dst, Uint8 value)
886 {
887     return SDL_RWwrite(dst, &value, sizeof (value), 1);
888 }
889 
890 size_t
SDL_WriteLE16(SDL_RWops * dst,Uint16 value)891 SDL_WriteLE16(SDL_RWops * dst, Uint16 value)
892 {
893     const Uint16 swapped = SDL_SwapLE16(value);
894     return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
895 }
896 
897 size_t
SDL_WriteBE16(SDL_RWops * dst,Uint16 value)898 SDL_WriteBE16(SDL_RWops * dst, Uint16 value)
899 {
900     const Uint16 swapped = SDL_SwapBE16(value);
901     return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
902 }
903 
904 size_t
SDL_WriteLE32(SDL_RWops * dst,Uint32 value)905 SDL_WriteLE32(SDL_RWops * dst, Uint32 value)
906 {
907     const Uint32 swapped = SDL_SwapLE32(value);
908     return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
909 }
910 
911 size_t
SDL_WriteBE32(SDL_RWops * dst,Uint32 value)912 SDL_WriteBE32(SDL_RWops * dst, Uint32 value)
913 {
914     const Uint32 swapped = SDL_SwapBE32(value);
915     return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
916 }
917 
918 size_t
SDL_WriteLE64(SDL_RWops * dst,Uint64 value)919 SDL_WriteLE64(SDL_RWops * dst, Uint64 value)
920 {
921     const Uint64 swapped = SDL_SwapLE64(value);
922     return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
923 }
924 
925 size_t
SDL_WriteBE64(SDL_RWops * dst,Uint64 value)926 SDL_WriteBE64(SDL_RWops * dst, Uint64 value)
927 {
928     const Uint64 swapped = SDL_SwapBE64(value);
929     return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
930 }
931 
932 /* vi: set ts=4 sw=4 expandtab: */
933