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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
672 | mem_size(SDL_RWops * context) |
673 | { |
674 | return (Sint64)(context->hidden.mem.stop - context->hidden.mem.base); |
675 | } |
676 | |
677 | static Sint64 SDLCALL |
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 |
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 |
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 |
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 |
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 * |
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 * |
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 * |
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 * |
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 * |
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 * |
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 |
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 * |
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 * |
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 |
1023 | SDL_RWsize(SDL_RWops *context) |
1024 | { |
1025 | return context->size(context); |
1026 | } |
1027 | |
1028 | Sint64 |
1029 | SDL_RWseek(SDL_RWops *context, Sint64 offset, int whence) |
1030 | { |
1031 | return context->seek(context, offset, whence); |
1032 | } |
1033 | |
1034 | Sint64 |
1035 | SDL_RWtell(SDL_RWops *context) |
1036 | { |
1037 | return context->seek(context, 0, RW_SEEK_CUR); |
1038 | } |
1039 | |
1040 | size_t |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
1124 | SDL_WriteU8(SDL_RWops * dst, Uint8 value) |
1125 | { |
1126 | return SDL_RWwrite(dst, &value, sizeof (value), 1); |
1127 | } |
1128 | |
1129 | size_t |
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 |
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 |
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 |
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 |
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 |
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 | |