1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2021 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 #include "nacl_io/nacl_io.h"
63 #endif
64 
65 #ifdef __VITA__
66 
67 #include <psp2/io/fcntl.h>
68 #include <psp2/io/stat.h>
69 
70 #define READAHEAD_BUFFER_SIZE   1024
71 static int SDLCALL
vita_file_open(SDL_RWops * context,const char * filename,const char * mode)72 vita_file_open(SDL_RWops * context, const char *filename, const char *mode)
73 {
74     int h;
75     int open_flags;
76     SDL_bool has_r;
77     SDL_bool has_w;
78     SDL_bool has_a;
79     SDL_bool has_plus;
80 
81     if (!context)
82         return -1;              /* failed (invalid call) */
83 
84     context->hidden.vitaio.h = -1;   /* mark this as unusable */
85     context->hidden.vitaio.buffer.data = NULL;
86     context->hidden.vitaio.buffer.size = 0;
87     context->hidden.vitaio.buffer.left = 0;
88 
89     open_flags = 0;
90 
91     /* "r" = reading, file must exist */
92     /* "w" = writing, truncate existing, file may not exist */
93     /* "r+"= reading or writing, file must exist            */
94     /* "a" = writing, append file may not exist             */
95     /* "a+"= append + read, file may not exist              */
96     /* "w+" = read, write, truncate. file may not exist    */
97 
98     has_r = SDL_strchr(mode, 'r') != NULL;
99     has_w = SDL_strchr(mode, 'w') != NULL;
100     has_a = SDL_strchr(mode, 'a') != NULL;
101     has_plus = SDL_strchr(mode, '+') != NULL;
102 
103     if (has_plus)
104     {
105         if (has_r || has_w || has_a)
106         {
107             open_flags |= SCE_O_RDWR;
108         }
109     }
110     else
111     {
112         if (has_r)
113         {
114             open_flags |= SCE_O_RDONLY;
115         }
116         if (has_w || has_a)
117         {
118             open_flags |= SCE_O_WRONLY;
119         }
120     }
121     if (has_w || has_a)
122     {
123         open_flags |= SCE_O_CREAT;
124     }
125     if (has_w)
126     {
127         open_flags |= SCE_O_TRUNC;
128     }
129     if (has_a)
130     {
131         open_flags |= SCE_O_APPEND;
132     }
133 
134     context->hidden.vitaio.buffer.data =
135         (char *) SDL_malloc(READAHEAD_BUFFER_SIZE);
136     if (!context->hidden.vitaio.buffer.data) {
137         return SDL_OutOfMemory();
138     }
139 
140     /* Try to open the file on the filesystem first */
141     h = sceIoOpen(filename, open_flags, 0777);
142 
143     if (h < 0) {
144         /* Try opening it from app0:/ container if it's a relative path */
145         char path[4096];
146         SDL_snprintf(path, 4096, "app0:/%s", filename);
147         h = sceIoOpen(path, open_flags, 0777);
148     }
149 
150     if (h < 0) {
151         SDL_free(context->hidden.vitaio.buffer.data);
152         context->hidden.vitaio.buffer.data = NULL;
153         SDL_SetError("Couldn't open %s", filename);
154         return -2;              /* failed (sceIoOpen) */
155     }
156     context->hidden.vitaio.h = h;
157 
158     return 0;                   /* ok */
159 }
160 
161 static Sint64 SDLCALL
vita_file_size(SDL_RWops * context)162 vita_file_size(SDL_RWops * context)
163 {
164     SceIoStat st;
165     if (!context || context->hidden.vitaio.h < 0) {
166         return SDL_SetError("vita_file_size: invalid context/file not opened");
167     }
168 
169     if (sceIoGetstatByFd(context->hidden.vitaio.h, &st) < 0)
170     {
171         return SDL_SetError("vita_file_size: could not get file size");
172     }
173     return st.st_size;
174 }
175 
176 static Sint64 SDLCALL
vita_file_seek(SDL_RWops * context,Sint64 offset,int whence)177 vita_file_seek(SDL_RWops * context, Sint64 offset, int whence)
178 {
179     int vitawhence;
180 
181     if (!context || context->hidden.vitaio.h < 0) {
182         return SDL_SetError("vita_file_seek: invalid context/file not opened");
183     }
184 
185     /* FIXME: We may be able to satisfy the seek within buffered data */
186     if (whence == RW_SEEK_CUR && context->hidden.vitaio.buffer.left) {
187         offset -= (long)context->hidden.vitaio.buffer.left;
188     }
189     context->hidden.vitaio.buffer.left = 0;
190 
191     switch (whence) {
192     case RW_SEEK_SET:
193         vitawhence = SCE_SEEK_SET;
194         break;
195     case RW_SEEK_CUR:
196         vitawhence = SCE_SEEK_CUR;
197         break;
198     case RW_SEEK_END:
199         vitawhence = SCE_SEEK_END;
200         break;
201     default:
202         return SDL_SetError("vita_file_seek: Unknown value for 'whence'");
203     }
204 
205     return sceIoLseek(context->hidden.vitaio.h, offset, vitawhence);
206 }
207 
208 static size_t SDLCALL
vita_file_read(SDL_RWops * context,void * ptr,size_t size,size_t maxnum)209 vita_file_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
210 {
211     size_t total_need;
212     size_t total_read = 0;
213     size_t read_ahead;
214     size_t byte_read;
215 
216     total_need = size * maxnum;
217 
218     if (!context || context->hidden.vitaio.h < 0
219         || !total_need)
220         return 0;
221 
222     if (context->hidden.vitaio.buffer.left > 0) {
223         void *data = (char *) context->hidden.vitaio.buffer.data +
224             context->hidden.vitaio.buffer.size -
225             context->hidden.vitaio.buffer.left;
226         read_ahead =
227             SDL_min(total_need, context->hidden.vitaio.buffer.left);
228         SDL_memcpy(ptr, data, read_ahead);
229         context->hidden.vitaio.buffer.left -= read_ahead;
230 
231         if (read_ahead == total_need) {
232             return maxnum;
233         }
234         ptr = (char *) ptr + read_ahead;
235         total_need -= read_ahead;
236         total_read += read_ahead;
237     }
238 
239     if (total_need < READAHEAD_BUFFER_SIZE) {
240         byte_read = sceIoRead(context->hidden.vitaio.h, context->hidden.vitaio.buffer.data, READAHEAD_BUFFER_SIZE);
241         read_ahead = SDL_min(total_need, (int) byte_read);
242         SDL_memcpy(ptr, context->hidden.vitaio.buffer.data, read_ahead);
243         context->hidden.vitaio.buffer.size = byte_read;
244         context->hidden.vitaio.buffer.left = byte_read - read_ahead;
245         total_read += read_ahead;
246     } else {
247         byte_read = sceIoRead(context->hidden.vitaio.h, ptr, total_need);
248         total_read += byte_read;
249     }
250     return (total_read / size);
251 }
252 
253 static size_t SDLCALL
vita_file_write(SDL_RWops * context,const void * ptr,size_t size,size_t num)254 vita_file_write(SDL_RWops * context, const void *ptr, size_t size,
255                  size_t num)
256 {
257 
258     size_t total_bytes;
259     size_t byte_written;
260     size_t nwritten;
261 
262     total_bytes = size * num;
263 
264     if (!context || context->hidden.vitaio.h < 0
265         || total_bytes <= 0 || !size)
266         return 0;
267 
268     if (context->hidden.vitaio.buffer.left) {
269         sceIoLseek(context->hidden.vitaio.h, -(SceOff)context->hidden.vitaio.buffer.left, SCE_SEEK_CUR);
270         context->hidden.vitaio.buffer.left = 0;
271     }
272 
273     byte_written = sceIoWrite(context->hidden.vitaio.h, ptr, total_bytes);
274 
275     nwritten = byte_written / size;
276     return nwritten;
277 }
278 
279 static int SDLCALL
vita_file_close(SDL_RWops * context)280 vita_file_close(SDL_RWops * context)
281 {
282     if (context) {
283         if (context->hidden.vitaio.h >= 0) {
284             sceIoClose(context->hidden.vitaio.h);
285             context->hidden.vitaio.h = -1;   /* to be sure */
286         }
287         SDL_free(context->hidden.vitaio.buffer.data);
288         context->hidden.vitaio.buffer.data = NULL;
289         SDL_FreeRW(context);
290     }
291     return 0;
292 }
293 #endif
294 
295 #ifdef __WIN32__
296 
297 /* Functions to read/write Win32 API file pointers */
298 
299 #ifndef INVALID_SET_FILE_POINTER
300 #define INVALID_SET_FILE_POINTER 0xFFFFFFFF
301 #endif
302 
303 #define READAHEAD_BUFFER_SIZE   1024
304 
305 static int SDLCALL
windows_file_open(SDL_RWops * context,const char * filename,const char * mode)306 windows_file_open(SDL_RWops * context, const char *filename, const char *mode)
307 {
308     UINT old_error_mode;
309     HANDLE h;
310     DWORD r_right, w_right;
311     DWORD must_exist, truncate;
312     int a_mode;
313 
314     if (!context)
315         return -1;              /* failed (invalid call) */
316 
317     context->hidden.windowsio.h = INVALID_HANDLE_VALUE;   /* mark this as unusable */
318     context->hidden.windowsio.buffer.data = NULL;
319     context->hidden.windowsio.buffer.size = 0;
320     context->hidden.windowsio.buffer.left = 0;
321 
322     /* "r" = reading, file must exist */
323     /* "w" = writing, truncate existing, file may not exist */
324     /* "r+"= reading or writing, file must exist            */
325     /* "a" = writing, append file may not exist             */
326     /* "a+"= append + read, file may not exist              */
327     /* "w+" = read, write, truncate. file may not exist    */
328 
329     must_exist = (SDL_strchr(mode, 'r') != NULL) ? OPEN_EXISTING : 0;
330     truncate = (SDL_strchr(mode, 'w') != NULL) ? CREATE_ALWAYS : 0;
331     r_right = (SDL_strchr(mode, '+') != NULL
332                || must_exist) ? GENERIC_READ : 0;
333     a_mode = (SDL_strchr(mode, 'a') != NULL) ? OPEN_ALWAYS : 0;
334     w_right = (a_mode || SDL_strchr(mode, '+')
335                || truncate) ? GENERIC_WRITE : 0;
336 
337     if (!r_right && !w_right)   /* inconsistent mode */
338         return -1;              /* failed (invalid call) */
339 
340     context->hidden.windowsio.buffer.data =
341         (char *) SDL_malloc(READAHEAD_BUFFER_SIZE);
342     if (!context->hidden.windowsio.buffer.data) {
343         return SDL_OutOfMemory();
344     }
345     /* Do not open a dialog box if failure */
346     old_error_mode =
347         SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
348 
349     {
350         LPTSTR tstr = WIN_UTF8ToString(filename);
351         h = CreateFile(tstr, (w_right | r_right),
352                        (w_right) ? 0 : FILE_SHARE_READ, NULL,
353                        (must_exist | truncate | a_mode),
354                        FILE_ATTRIBUTE_NORMAL, NULL);
355         SDL_free(tstr);
356     }
357 
358     /* restore old behavior */
359     SetErrorMode(old_error_mode);
360 
361     if (h == INVALID_HANDLE_VALUE) {
362         SDL_free(context->hidden.windowsio.buffer.data);
363         context->hidden.windowsio.buffer.data = NULL;
364         SDL_SetError("Couldn't open %s", filename);
365         return -2;              /* failed (CreateFile) */
366     }
367     context->hidden.windowsio.h = h;
368     context->hidden.windowsio.append = a_mode ? SDL_TRUE : SDL_FALSE;
369 
370     return 0;                   /* ok */
371 }
372 
373 static Sint64 SDLCALL
windows_file_size(SDL_RWops * context)374 windows_file_size(SDL_RWops * context)
375 {
376     LARGE_INTEGER size;
377 
378     if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE) {
379         return SDL_SetError("windows_file_size: invalid context/file not opened");
380     }
381 
382     if (!GetFileSizeEx(context->hidden.windowsio.h, &size)) {
383         return WIN_SetError("windows_file_size");
384     }
385 
386     return size.QuadPart;
387 }
388 
389 static Sint64 SDLCALL
windows_file_seek(SDL_RWops * context,Sint64 offset,int whence)390 windows_file_seek(SDL_RWops * context, Sint64 offset, int whence)
391 {
392     DWORD windowswhence;
393     LARGE_INTEGER windowsoffset;
394 
395     if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE) {
396         return SDL_SetError("windows_file_seek: invalid context/file not opened");
397     }
398 
399     /* FIXME: We may be able to satisfy the seek within buffered data */
400     if (whence == RW_SEEK_CUR && context->hidden.windowsio.buffer.left) {
401         offset -= (long)context->hidden.windowsio.buffer.left;
402     }
403     context->hidden.windowsio.buffer.left = 0;
404 
405     switch (whence) {
406     case RW_SEEK_SET:
407         windowswhence = FILE_BEGIN;
408         break;
409     case RW_SEEK_CUR:
410         windowswhence = FILE_CURRENT;
411         break;
412     case RW_SEEK_END:
413         windowswhence = FILE_END;
414         break;
415     default:
416         return SDL_SetError("windows_file_seek: Unknown value for 'whence'");
417     }
418 
419     windowsoffset.QuadPart = offset;
420     if (!SetFilePointerEx(context->hidden.windowsio.h, windowsoffset, &windowsoffset, windowswhence)) {
421         return WIN_SetError("windows_file_seek");
422     }
423     return windowsoffset.QuadPart;
424 }
425 
426 static size_t SDLCALL
windows_file_read(SDL_RWops * context,void * ptr,size_t size,size_t maxnum)427 windows_file_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
428 {
429     size_t total_need;
430     size_t total_read = 0;
431     size_t read_ahead;
432     DWORD byte_read;
433 
434     total_need = size * maxnum;
435 
436     if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE
437         || !total_need)
438         return 0;
439 
440     if (context->hidden.windowsio.buffer.left > 0) {
441         void *data = (char *) context->hidden.windowsio.buffer.data +
442             context->hidden.windowsio.buffer.size -
443             context->hidden.windowsio.buffer.left;
444         read_ahead =
445             SDL_min(total_need, context->hidden.windowsio.buffer.left);
446         SDL_memcpy(ptr, data, read_ahead);
447         context->hidden.windowsio.buffer.left -= read_ahead;
448 
449         if (read_ahead == total_need) {
450             return maxnum;
451         }
452         ptr = (char *) ptr + read_ahead;
453         total_need -= read_ahead;
454         total_read += read_ahead;
455     }
456 
457     if (total_need < READAHEAD_BUFFER_SIZE) {
458         if (!ReadFile
459             (context->hidden.windowsio.h, context->hidden.windowsio.buffer.data,
460              READAHEAD_BUFFER_SIZE, &byte_read, NULL)) {
461             SDL_Error(SDL_EFREAD);
462             return 0;
463         }
464         read_ahead = SDL_min(total_need, (int) byte_read);
465         SDL_memcpy(ptr, context->hidden.windowsio.buffer.data, read_ahead);
466         context->hidden.windowsio.buffer.size = byte_read;
467         context->hidden.windowsio.buffer.left = byte_read - read_ahead;
468         total_read += read_ahead;
469     } else {
470         if (!ReadFile
471             (context->hidden.windowsio.h, ptr, (DWORD)total_need, &byte_read, NULL)) {
472             SDL_Error(SDL_EFREAD);
473             return 0;
474         }
475         total_read += byte_read;
476     }
477     return (total_read / size);
478 }
479 
480 static size_t SDLCALL
windows_file_write(SDL_RWops * context,const void * ptr,size_t size,size_t num)481 windows_file_write(SDL_RWops * context, const void *ptr, size_t size,
482                  size_t num)
483 {
484 
485     size_t total_bytes;
486     DWORD byte_written;
487     size_t nwritten;
488 
489     total_bytes = size * num;
490 
491     if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE
492         || total_bytes <= 0 || !size)
493         return 0;
494 
495     if (context->hidden.windowsio.buffer.left) {
496         SetFilePointer(context->hidden.windowsio.h,
497                        -(LONG)context->hidden.windowsio.buffer.left, NULL,
498                        FILE_CURRENT);
499         context->hidden.windowsio.buffer.left = 0;
500     }
501 
502     /* if in append mode, we must go to the EOF before write */
503     if (context->hidden.windowsio.append) {
504         if (SetFilePointer(context->hidden.windowsio.h, 0L, NULL, FILE_END) ==
505             INVALID_SET_FILE_POINTER) {
506             SDL_Error(SDL_EFWRITE);
507             return 0;
508         }
509     }
510 
511     if (!WriteFile
512         (context->hidden.windowsio.h, ptr, (DWORD)total_bytes, &byte_written, NULL)) {
513         SDL_Error(SDL_EFWRITE);
514         return 0;
515     }
516 
517     nwritten = byte_written / size;
518     return nwritten;
519 }
520 
521 static int SDLCALL
windows_file_close(SDL_RWops * context)522 windows_file_close(SDL_RWops * context)
523 {
524 
525     if (context) {
526         if (context->hidden.windowsio.h != INVALID_HANDLE_VALUE) {
527             CloseHandle(context->hidden.windowsio.h);
528             context->hidden.windowsio.h = INVALID_HANDLE_VALUE;   /* to be sure */
529         }
530         SDL_free(context->hidden.windowsio.buffer.data);
531         context->hidden.windowsio.buffer.data = NULL;
532         SDL_FreeRW(context);
533     }
534     return 0;
535 }
536 #endif /* __WIN32__ */
537 
538 #ifdef HAVE_STDIO_H
539 
540 #ifdef HAVE_FOPEN64
541 #define fopen   fopen64
542 #endif
543 #ifdef HAVE_FSEEKO64
544 #define fseek_off_t off64_t
545 #define fseek   fseeko64
546 #define ftell   ftello64
547 #elif defined(HAVE_FSEEKO)
548 #if defined(OFF_MIN) && defined(OFF_MAX)
549 #define FSEEK_OFF_MIN OFF_MIN
550 #define FSEEK_OFF_MAX OFF_MAX
551 #elif defined(HAVE_LIMITS_H)
552 /* POSIX doesn't specify the minimum and maximum macros for off_t so
553  * we have to improvise and dance around implementation-defined
554  * behavior. This may fail if the off_t type has padding bits or
555  * is not a two's-complement representation. The compilers will detect
556  * and eliminate the dead code if off_t has 64 bits.
557  */
558 #define FSEEK_OFF_MAX (((((off_t)1 << (sizeof(off_t) * CHAR_BIT - 2)) - 1) << 1) + 1)
559 #define FSEEK_OFF_MIN (-(FSEEK_OFF_MAX) - 1)
560 #endif
561 #define fseek_off_t off_t
562 #define fseek   fseeko
563 #define ftell   ftello
564 #elif defined(HAVE__FSEEKI64)
565 #define fseek_off_t __int64
566 #define fseek   _fseeki64
567 #define ftell   _ftelli64
568 #else
569 #ifdef HAVE_LIMITS_H
570 #define FSEEK_OFF_MIN LONG_MIN
571 #define FSEEK_OFF_MAX LONG_MAX
572 #endif
573 #define fseek_off_t long
574 #endif
575 
576 /* Functions to read/write stdio file pointers */
577 
578 static Sint64 SDLCALL
stdio_size(SDL_RWops * context)579 stdio_size(SDL_RWops * context)
580 {
581     Sint64 pos, size;
582 
583     pos = SDL_RWseek(context, 0, RW_SEEK_CUR);
584     if (pos < 0) {
585         return -1;
586     }
587     size = SDL_RWseek(context, 0, RW_SEEK_END);
588 
589     SDL_RWseek(context, pos, RW_SEEK_SET);
590     return size;
591 }
592 
593 static Sint64 SDLCALL
stdio_seek(SDL_RWops * context,Sint64 offset,int whence)594 stdio_seek(SDL_RWops * context, Sint64 offset, int whence)
595 {
596     int stdiowhence;
597 
598     switch (whence) {
599     case RW_SEEK_SET:
600         stdiowhence = SEEK_SET;
601         break;
602     case RW_SEEK_CUR:
603         stdiowhence = SEEK_CUR;
604         break;
605     case RW_SEEK_END:
606         stdiowhence = SEEK_END;
607         break;
608     default:
609         return SDL_SetError("Unknown value for 'whence'");
610     }
611 
612 #if defined(FSEEK_OFF_MIN) && defined(FSEEK_OFF_MAX)
613     if (offset < (Sint64)(FSEEK_OFF_MIN) || offset > (Sint64)(FSEEK_OFF_MAX)) {
614         return SDL_SetError("Seek offset out of range");
615     }
616 #endif
617 
618     if (fseek(context->hidden.stdio.fp, (fseek_off_t)offset, stdiowhence) == 0) {
619         Sint64 pos = ftell(context->hidden.stdio.fp);
620         if (pos < 0) {
621             return SDL_SetError("Couldn't get stream offset");
622         }
623         return pos;
624     }
625     return SDL_Error(SDL_EFSEEK);
626 }
627 
628 static size_t SDLCALL
stdio_read(SDL_RWops * context,void * ptr,size_t size,size_t maxnum)629 stdio_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
630 {
631     size_t nread;
632 
633     nread = fread(ptr, size, maxnum, context->hidden.stdio.fp);
634     if (nread == 0 && ferror(context->hidden.stdio.fp)) {
635         SDL_Error(SDL_EFREAD);
636     }
637     return nread;
638 }
639 
640 static size_t SDLCALL
stdio_write(SDL_RWops * context,const void * ptr,size_t size,size_t num)641 stdio_write(SDL_RWops * context, const void *ptr, size_t size, size_t num)
642 {
643     size_t nwrote;
644 
645     nwrote = fwrite(ptr, size, num, context->hidden.stdio.fp);
646     if (nwrote == 0 && ferror(context->hidden.stdio.fp)) {
647         SDL_Error(SDL_EFWRITE);
648     }
649     return nwrote;
650 }
651 
652 static int SDLCALL
stdio_close(SDL_RWops * context)653 stdio_close(SDL_RWops * context)
654 {
655     int status = 0;
656     if (context) {
657         if (context->hidden.stdio.autoclose) {
658             /* WARNING:  Check the return value here! */
659             if (fclose(context->hidden.stdio.fp) != 0) {
660                 status = SDL_Error(SDL_EFWRITE);
661             }
662         }
663         SDL_FreeRW(context);
664     }
665     return status;
666 }
667 #endif /* !HAVE_STDIO_H */
668 
669 /* Functions to read/write memory pointers */
670 
671 static Sint64 SDLCALL
mem_size(SDL_RWops * context)672 mem_size(SDL_RWops * context)
673 {
674     return (Sint64)(context->hidden.mem.stop - context->hidden.mem.base);
675 }
676 
677 static Sint64 SDLCALL
mem_seek(SDL_RWops * context,Sint64 offset,int whence)678 mem_seek(SDL_RWops * context, Sint64 offset, int whence)
679 {
680     Uint8 *newpos;
681 
682     switch (whence) {
683     case RW_SEEK_SET:
684         newpos = context->hidden.mem.base + offset;
685         break;
686     case RW_SEEK_CUR:
687         newpos = context->hidden.mem.here + offset;
688         break;
689     case RW_SEEK_END:
690         newpos = context->hidden.mem.stop + offset;
691         break;
692     default:
693         return SDL_SetError("Unknown value for 'whence'");
694     }
695     if (newpos < context->hidden.mem.base) {
696         newpos = context->hidden.mem.base;
697     }
698     if (newpos > context->hidden.mem.stop) {
699         newpos = context->hidden.mem.stop;
700     }
701     context->hidden.mem.here = newpos;
702     return (Sint64)(context->hidden.mem.here - context->hidden.mem.base);
703 }
704 
705 static size_t SDLCALL
mem_read(SDL_RWops * context,void * ptr,size_t size,size_t maxnum)706 mem_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
707 {
708     size_t total_bytes;
709     size_t mem_available;
710 
711     total_bytes = (maxnum * size);
712     if ((maxnum <= 0) || (size <= 0)
713         || ((total_bytes / maxnum) != size)) {
714         return 0;
715     }
716 
717     mem_available = (context->hidden.mem.stop - context->hidden.mem.here);
718     if (total_bytes > mem_available) {
719         total_bytes = mem_available;
720     }
721 
722     SDL_memcpy(ptr, context->hidden.mem.here, total_bytes);
723     context->hidden.mem.here += total_bytes;
724 
725     return (total_bytes / size);
726 }
727 
728 static size_t SDLCALL
mem_write(SDL_RWops * context,const void * ptr,size_t size,size_t num)729 mem_write(SDL_RWops * context, const void *ptr, size_t size, size_t num)
730 {
731     if ((context->hidden.mem.here + (num * size)) > context->hidden.mem.stop) {
732         num = (context->hidden.mem.stop - context->hidden.mem.here) / size;
733     }
734     SDL_memcpy(context->hidden.mem.here, ptr, num * size);
735     context->hidden.mem.here += num * size;
736     return num;
737 }
738 
739 static size_t SDLCALL
mem_writeconst(SDL_RWops * context,const void * ptr,size_t size,size_t num)740 mem_writeconst(SDL_RWops * context, const void *ptr, size_t size, size_t num)
741 {
742     SDL_SetError("Can't write to read-only memory");
743     return 0;
744 }
745 
746 static int SDLCALL
mem_close(SDL_RWops * context)747 mem_close(SDL_RWops * context)
748 {
749     if (context) {
750         SDL_FreeRW(context);
751     }
752     return 0;
753 }
754 
755 
756 /* Functions to create SDL_RWops structures from various data sources */
757 
758 SDL_RWops *
SDL_RWFromFile(const char * file,const char * mode)759 SDL_RWFromFile(const char *file, const char *mode)
760 {
761     SDL_RWops *rwops = NULL;
762     if (!file || !*file || !mode || !*mode) {
763         SDL_SetError("SDL_RWFromFile(): No file or no mode specified");
764         return NULL;
765     }
766 #if defined(__ANDROID__)
767 #ifdef HAVE_STDIO_H
768     /* Try to open the file on the filesystem first */
769     if (*file == '/') {
770         FILE *fp = fopen(file, mode);
771         if (fp) {
772             return SDL_RWFromFP(fp, 1);
773         }
774     } else {
775         /* Try opening it from internal storage if it's a relative path */
776         char *path;
777         FILE *fp;
778 
779         /* !!! FIXME: why not just "char path[PATH_MAX];" ? */
780         path = SDL_stack_alloc(char, PATH_MAX);
781         if (path) {
782             SDL_snprintf(path, PATH_MAX, "%s/%s",
783                          SDL_AndroidGetInternalStoragePath(), file);
784             fp = fopen(path, mode);
785             SDL_stack_free(path);
786             if (fp) {
787                 return SDL_RWFromFP(fp, 1);
788             }
789         }
790     }
791 #endif /* HAVE_STDIO_H */
792 
793     /* Try to open the file from the asset system */
794     rwops = SDL_AllocRW();
795     if (!rwops)
796         return NULL;            /* SDL_SetError already setup by SDL_AllocRW() */
797     if (Android_JNI_FileOpen(rwops, file, mode) < 0) {
798         SDL_FreeRW(rwops);
799         return NULL;
800     }
801     rwops->size = Android_JNI_FileSize;
802     rwops->seek = Android_JNI_FileSeek;
803     rwops->read = Android_JNI_FileRead;
804     rwops->write = Android_JNI_FileWrite;
805     rwops->close = Android_JNI_FileClose;
806     rwops->type = SDL_RWOPS_JNIFILE;
807 
808 #elif defined(__WIN32__)
809     rwops = SDL_AllocRW();
810     if (!rwops)
811         return NULL;            /* SDL_SetError already setup by SDL_AllocRW() */
812     if (windows_file_open(rwops, file, mode) < 0) {
813         SDL_FreeRW(rwops);
814         return NULL;
815     }
816     rwops->size = windows_file_size;
817     rwops->seek = windows_file_seek;
818     rwops->read = windows_file_read;
819     rwops->write = windows_file_write;
820     rwops->close = windows_file_close;
821     rwops->type = SDL_RWOPS_WINFILE;
822 #elif defined(__VITA__)
823     rwops = SDL_AllocRW();
824     if (!rwops)
825         return NULL;            /* SDL_SetError already setup by SDL_AllocRW() */
826     if (vita_file_open(rwops, file, mode) < 0) {
827         SDL_FreeRW(rwops);
828         return NULL;
829     }
830     rwops->size = vita_file_size;
831     rwops->seek = vita_file_seek;
832     rwops->read = vita_file_read;
833     rwops->write = vita_file_write;
834     rwops->close = vita_file_close;
835     rwops->type = SDL_RWOPS_VITAFILE;
836 #elif HAVE_STDIO_H
837     {
838         #ifdef __APPLE__
839         FILE *fp = SDL_OpenFPFromBundleOrFallback(file, mode);
840         #elif __WINRT__
841         FILE *fp = NULL;
842         fopen_s(&fp, file, mode);
843         #else
844         FILE *fp = fopen(file, mode);
845         #endif
846         if (fp == NULL) {
847             SDL_SetError("Couldn't open %s", file);
848         } else {
849             rwops = SDL_RWFromFP(fp, SDL_TRUE);
850         }
851     }
852 #else
853     SDL_SetError("SDL not compiled with stdio support");
854 #endif /* !HAVE_STDIO_H */
855 
856     return rwops;
857 }
858 
859 #ifdef HAVE_STDIO_H
860 SDL_RWops *
SDL_RWFromFP(FILE * fp,SDL_bool autoclose)861 SDL_RWFromFP(FILE * fp, SDL_bool autoclose)
862 {
863     SDL_RWops *rwops = NULL;
864 
865     rwops = SDL_AllocRW();
866     if (rwops != NULL) {
867         rwops->size = stdio_size;
868         rwops->seek = stdio_seek;
869         rwops->read = stdio_read;
870         rwops->write = stdio_write;
871         rwops->close = stdio_close;
872         rwops->hidden.stdio.fp = fp;
873         rwops->hidden.stdio.autoclose = autoclose;
874         rwops->type = SDL_RWOPS_STDFILE;
875     }
876     return rwops;
877 }
878 #else
879 SDL_RWops *
SDL_RWFromFP(void * fp,SDL_bool autoclose)880 SDL_RWFromFP(void * fp, SDL_bool autoclose)
881 {
882     SDL_SetError("SDL not compiled with stdio support");
883     return NULL;
884 }
885 #endif /* HAVE_STDIO_H */
886 
887 SDL_RWops *
SDL_RWFromMem(void * mem,int size)888 SDL_RWFromMem(void *mem, int size)
889 {
890     SDL_RWops *rwops = NULL;
891     if (!mem) {
892       SDL_InvalidParamError("mem");
893       return rwops;
894     }
895     if (!size) {
896       SDL_InvalidParamError("size");
897       return rwops;
898     }
899 
900     rwops = SDL_AllocRW();
901     if (rwops != NULL) {
902         rwops->size = mem_size;
903         rwops->seek = mem_seek;
904         rwops->read = mem_read;
905         rwops->write = mem_write;
906         rwops->close = mem_close;
907         rwops->hidden.mem.base = (Uint8 *) mem;
908         rwops->hidden.mem.here = rwops->hidden.mem.base;
909         rwops->hidden.mem.stop = rwops->hidden.mem.base + size;
910         rwops->type = SDL_RWOPS_MEMORY;
911     }
912     return rwops;
913 }
914 
915 SDL_RWops *
SDL_RWFromConstMem(const void * mem,int size)916 SDL_RWFromConstMem(const void *mem, int size)
917 {
918     SDL_RWops *rwops = NULL;
919     if (!mem) {
920       SDL_InvalidParamError("mem");
921       return rwops;
922     }
923     if (!size) {
924       SDL_InvalidParamError("size");
925       return rwops;
926     }
927 
928     rwops = SDL_AllocRW();
929     if (rwops != NULL) {
930         rwops->size = mem_size;
931         rwops->seek = mem_seek;
932         rwops->read = mem_read;
933         rwops->write = mem_writeconst;
934         rwops->close = mem_close;
935         rwops->hidden.mem.base = (Uint8 *) mem;
936         rwops->hidden.mem.here = rwops->hidden.mem.base;
937         rwops->hidden.mem.stop = rwops->hidden.mem.base + size;
938         rwops->type = SDL_RWOPS_MEMORY_RO;
939     }
940     return rwops;
941 }
942 
943 SDL_RWops *
SDL_AllocRW(void)944 SDL_AllocRW(void)
945 {
946     SDL_RWops *area;
947 
948     area = (SDL_RWops *) SDL_malloc(sizeof *area);
949     if (area == NULL) {
950         SDL_OutOfMemory();
951     } else {
952         area->type = SDL_RWOPS_UNKNOWN;
953     }
954     return area;
955 }
956 
957 void
SDL_FreeRW(SDL_RWops * area)958 SDL_FreeRW(SDL_RWops * area)
959 {
960     SDL_free(area);
961 }
962 
963 /* Load all the data from an SDL data stream */
964 void *
SDL_LoadFile_RW(SDL_RWops * src,size_t * datasize,int freesrc)965 SDL_LoadFile_RW(SDL_RWops * src, size_t *datasize, int freesrc)
966 {
967     const int FILE_CHUNK_SIZE = 1024;
968     Sint64 size;
969     size_t size_read, size_total;
970     void *data = NULL, *newdata;
971 
972     if (!src) {
973         SDL_InvalidParamError("src");
974         return NULL;
975     }
976 
977     size = SDL_RWsize(src);
978     if (size < 0) {
979         size = FILE_CHUNK_SIZE;
980     }
981     data = SDL_malloc((size_t)(size + 1));
982 
983     size_total = 0;
984     for (;;) {
985         if ((((Sint64)size_total) + FILE_CHUNK_SIZE) > size) {
986             size = (size_total + FILE_CHUNK_SIZE);
987             newdata = SDL_realloc(data, (size_t)(size + 1));
988             if (!newdata) {
989                 SDL_free(data);
990                 data = NULL;
991                 SDL_OutOfMemory();
992                 goto done;
993             }
994             data = newdata;
995         }
996 
997         size_read = SDL_RWread(src, (char *)data+size_total, 1, (size_t)(size-size_total));
998         if (size_read == 0) {
999             break;
1000         }
1001         size_total += size_read;
1002     }
1003 
1004     if (datasize) {
1005         *datasize = size_total;
1006     }
1007     ((char *)data)[size_total] = '\0';
1008 
1009 done:
1010     if (freesrc && src) {
1011         SDL_RWclose(src);
1012     }
1013     return data;
1014 }
1015 
1016 void *
SDL_LoadFile(const char * file,size_t * datasize)1017 SDL_LoadFile(const char *file, size_t *datasize)
1018 {
1019    return SDL_LoadFile_RW(SDL_RWFromFile(file, "rb"), datasize, 1);
1020 }
1021 
1022 Sint64
SDL_RWsize(SDL_RWops * context)1023 SDL_RWsize(SDL_RWops *context)
1024 {
1025     return context->size(context);
1026 }
1027 
1028 Sint64
SDL_RWseek(SDL_RWops * context,Sint64 offset,int whence)1029 SDL_RWseek(SDL_RWops *context, Sint64 offset, int whence)
1030 {
1031     return context->seek(context, offset, whence);
1032 }
1033 
1034 Sint64
SDL_RWtell(SDL_RWops * context)1035 SDL_RWtell(SDL_RWops *context)
1036 {
1037     return context->seek(context, 0, RW_SEEK_CUR);
1038 }
1039 
1040 size_t
SDL_RWread(SDL_RWops * context,void * ptr,size_t size,size_t maxnum)1041 SDL_RWread(SDL_RWops *context, void *ptr, size_t size, size_t maxnum)
1042 {
1043     return context->read(context, ptr, size, maxnum);
1044 }
1045 
1046 size_t
SDL_RWwrite(SDL_RWops * context,const void * ptr,size_t size,size_t num)1047 SDL_RWwrite(SDL_RWops *context, const void *ptr, size_t size, size_t num)
1048 {
1049     return context->write(context, ptr, size, num);
1050 }
1051 
1052 int
SDL_RWclose(SDL_RWops * context)1053 SDL_RWclose(SDL_RWops *context)
1054 {
1055     return context->close(context);
1056 }
1057 
1058 /* Functions for dynamically reading and writing endian-specific values */
1059 
1060 Uint8
SDL_ReadU8(SDL_RWops * src)1061 SDL_ReadU8(SDL_RWops * src)
1062 {
1063     Uint8 value = 0;
1064 
1065     SDL_RWread(src, &value, sizeof (value), 1);
1066     return value;
1067 }
1068 
1069 Uint16
SDL_ReadLE16(SDL_RWops * src)1070 SDL_ReadLE16(SDL_RWops * src)
1071 {
1072     Uint16 value = 0;
1073 
1074     SDL_RWread(src, &value, sizeof (value), 1);
1075     return SDL_SwapLE16(value);
1076 }
1077 
1078 Uint16
SDL_ReadBE16(SDL_RWops * src)1079 SDL_ReadBE16(SDL_RWops * src)
1080 {
1081     Uint16 value = 0;
1082 
1083     SDL_RWread(src, &value, sizeof (value), 1);
1084     return SDL_SwapBE16(value);
1085 }
1086 
1087 Uint32
SDL_ReadLE32(SDL_RWops * src)1088 SDL_ReadLE32(SDL_RWops * src)
1089 {
1090     Uint32 value = 0;
1091 
1092     SDL_RWread(src, &value, sizeof (value), 1);
1093     return SDL_SwapLE32(value);
1094 }
1095 
1096 Uint32
SDL_ReadBE32(SDL_RWops * src)1097 SDL_ReadBE32(SDL_RWops * src)
1098 {
1099     Uint32 value = 0;
1100 
1101     SDL_RWread(src, &value, sizeof (value), 1);
1102     return SDL_SwapBE32(value);
1103 }
1104 
1105 Uint64
SDL_ReadLE64(SDL_RWops * src)1106 SDL_ReadLE64(SDL_RWops * src)
1107 {
1108     Uint64 value = 0;
1109 
1110     SDL_RWread(src, &value, sizeof (value), 1);
1111     return SDL_SwapLE64(value);
1112 }
1113 
1114 Uint64
SDL_ReadBE64(SDL_RWops * src)1115 SDL_ReadBE64(SDL_RWops * src)
1116 {
1117     Uint64 value = 0;
1118 
1119     SDL_RWread(src, &value, sizeof (value), 1);
1120     return SDL_SwapBE64(value);
1121 }
1122 
1123 size_t
SDL_WriteU8(SDL_RWops * dst,Uint8 value)1124 SDL_WriteU8(SDL_RWops * dst, Uint8 value)
1125 {
1126     return SDL_RWwrite(dst, &value, sizeof (value), 1);
1127 }
1128 
1129 size_t
SDL_WriteLE16(SDL_RWops * dst,Uint16 value)1130 SDL_WriteLE16(SDL_RWops * dst, Uint16 value)
1131 {
1132     const Uint16 swapped = SDL_SwapLE16(value);
1133     return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
1134 }
1135 
1136 size_t
SDL_WriteBE16(SDL_RWops * dst,Uint16 value)1137 SDL_WriteBE16(SDL_RWops * dst, Uint16 value)
1138 {
1139     const Uint16 swapped = SDL_SwapBE16(value);
1140     return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
1141 }
1142 
1143 size_t
SDL_WriteLE32(SDL_RWops * dst,Uint32 value)1144 SDL_WriteLE32(SDL_RWops * dst, Uint32 value)
1145 {
1146     const Uint32 swapped = SDL_SwapLE32(value);
1147     return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
1148 }
1149 
1150 size_t
SDL_WriteBE32(SDL_RWops * dst,Uint32 value)1151 SDL_WriteBE32(SDL_RWops * dst, Uint32 value)
1152 {
1153     const Uint32 swapped = SDL_SwapBE32(value);
1154     return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
1155 }
1156 
1157 size_t
SDL_WriteLE64(SDL_RWops * dst,Uint64 value)1158 SDL_WriteLE64(SDL_RWops * dst, Uint64 value)
1159 {
1160     const Uint64 swapped = SDL_SwapLE64(value);
1161     return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
1162 }
1163 
1164 size_t
SDL_WriteBE64(SDL_RWops * dst,Uint64 value)1165 SDL_WriteBE64(SDL_RWops * dst, Uint64 value)
1166 {
1167     const Uint64 swapped = SDL_SwapBE64(value);
1168     return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
1169 }
1170 
1171 /* vi: set ts=4 sw=4 expandtab: */
1172