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
71static int SDLCALL
72vita_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
161static Sint64 SDLCALL
162vita_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
176static Sint64 SDLCALL
177vita_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
208static size_t SDLCALL
209vita_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
253static size_t SDLCALL
254vita_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
279static int SDLCALL
280vita_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
305static int SDLCALL
306windows_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
373static Sint64 SDLCALL
374windows_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
389static Sint64 SDLCALL
390windows_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
426static size_t SDLCALL
427windows_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
480static size_t SDLCALL
481windows_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
521static int SDLCALL
522windows_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
578static Sint64 SDLCALL
579stdio_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
593static Sint64 SDLCALL
594stdio_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
628static size_t SDLCALL
629stdio_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
640static size_t SDLCALL
641stdio_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
652static int SDLCALL
653stdio_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
671static Sint64 SDLCALL
672mem_size(SDL_RWops * context)
673{
674 return (Sint64)(context->hidden.mem.stop - context->hidden.mem.base);
675}
676
677static Sint64 SDLCALL
678mem_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
705static size_t SDLCALL
706mem_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
728static size_t SDLCALL
729mem_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
739static size_t SDLCALL
740mem_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
746static int SDLCALL
747mem_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
758SDL_RWops *
759SDL_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
860SDL_RWops *
861SDL_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
879SDL_RWops *
880SDL_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
887SDL_RWops *
888SDL_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
915SDL_RWops *
916SDL_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
943SDL_RWops *
944SDL_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
957void
958SDL_FreeRW(SDL_RWops * area)
959{
960 SDL_free(area);
961}
962
963/* Load all the data from an SDL data stream */
964void *
965SDL_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
1009done:
1010 if (freesrc && src) {
1011 SDL_RWclose(src);
1012 }
1013 return data;
1014}
1015
1016void *
1017SDL_LoadFile(const char *file, size_t *datasize)
1018{
1019 return SDL_LoadFile_RW(SDL_RWFromFile(file, "rb"), datasize, 1);
1020}
1021
1022Sint64
1023SDL_RWsize(SDL_RWops *context)
1024{
1025 return context->size(context);
1026}
1027
1028Sint64
1029SDL_RWseek(SDL_RWops *context, Sint64 offset, int whence)
1030{
1031 return context->seek(context, offset, whence);
1032}
1033
1034Sint64
1035SDL_RWtell(SDL_RWops *context)
1036{
1037 return context->seek(context, 0, RW_SEEK_CUR);
1038}
1039
1040size_t
1041SDL_RWread(SDL_RWops *context, void *ptr, size_t size, size_t maxnum)
1042{
1043 return context->read(context, ptr, size, maxnum);
1044}
1045
1046size_t
1047SDL_RWwrite(SDL_RWops *context, const void *ptr, size_t size, size_t num)
1048{
1049 return context->write(context, ptr, size, num);
1050}
1051
1052int
1053SDL_RWclose(SDL_RWops *context)
1054{
1055 return context->close(context);
1056}
1057
1058/* Functions for dynamically reading and writing endian-specific values */
1059
1060Uint8
1061SDL_ReadU8(SDL_RWops * src)
1062{
1063 Uint8 value = 0;
1064
1065 SDL_RWread(src, &value, sizeof (value), 1);
1066 return value;
1067}
1068
1069Uint16
1070SDL_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
1078Uint16
1079SDL_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
1087Uint32
1088SDL_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
1096Uint32
1097SDL_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
1105Uint64
1106SDL_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
1114Uint64
1115SDL_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
1123size_t
1124SDL_WriteU8(SDL_RWops * dst, Uint8 value)
1125{
1126 return SDL_RWwrite(dst, &value, sizeof (value), 1);
1127}
1128
1129size_t
1130SDL_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
1136size_t
1137SDL_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
1143size_t
1144SDL_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
1150size_t
1151SDL_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
1157size_t
1158SDL_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
1164size_t
1165SDL_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