src/file/SDL_rwops.c
author Ryan C. Gordon <icculus@icculus.org>
Sat, 15 Sep 2012 10:59:39 -0400
changeset 6430 48d519500f7e
parent 6375 93f9a24d1c02
child 6629 8e2c731103e6
child 8316 88f011703f39
permissions -rwxr-xr-x
Removed Windows CE support from SDL 2.0.

It's a long-dead platform, and we don't have any way to build for, test, or
maintain it, so there's no sense in doing acrobatics to support it.

If you need Windows CE support, use SDL 1.2. If you need Windows Phone support,
send SDL 2.0 patches for the newer Windows Mobile platform.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2012 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 #include "SDL_config.h"
    22 
    23 /* This file provides a general interface for SDL to read and write
    24    data sources.  It can easily be extended to files, memory, etc.
    25 */
    26 
    27 #include "SDL_endian.h"
    28 #include "SDL_rwops.h"
    29 
    30 #ifdef __APPLE__
    31 #include "cocoa/SDL_rwopsbundlesupport.h"
    32 #endif /* __APPLE__ */
    33 
    34 #ifdef ANDROID
    35 #include "../core/android/SDL_android.h"
    36 #endif
    37 
    38 #ifdef __NDS__
    39 /* include libfat headers for fatInitDefault(). */
    40 #include <fat.h>
    41 #endif /* __NDS__ */
    42 
    43 #ifdef __WIN32__
    44 
    45 /* Functions to read/write Win32 API file pointers */
    46 
    47 #include "../core/windows/SDL_windows.h"
    48 
    49 #ifndef INVALID_SET_FILE_POINTER
    50 #define INVALID_SET_FILE_POINTER 0xFFFFFFFF
    51 #endif
    52 
    53 #define READAHEAD_BUFFER_SIZE	1024
    54 
    55 static int SDLCALL
    56 windows_file_open(SDL_RWops * context, const char *filename, const char *mode)
    57 {
    58     UINT old_error_mode;
    59     HANDLE h;
    60     DWORD r_right, w_right;
    61     DWORD must_exist, truncate;
    62     int a_mode;
    63 
    64     if (!context)
    65         return -1;              /* failed (invalid call) */
    66 
    67     context->hidden.windowsio.h = INVALID_HANDLE_VALUE;   /* mark this as unusable */
    68     context->hidden.windowsio.buffer.data = NULL;
    69     context->hidden.windowsio.buffer.size = 0;
    70     context->hidden.windowsio.buffer.left = 0;
    71 
    72     /* "r" = reading, file must exist */
    73     /* "w" = writing, truncate existing, file may not exist */
    74     /* "r+"= reading or writing, file must exist            */
    75     /* "a" = writing, append file may not exist             */
    76     /* "a+"= append + read, file may not exist              */
    77     /* "w+" = read, write, truncate. file may not exist    */
    78 
    79     must_exist = (SDL_strchr(mode, 'r') != NULL) ? OPEN_EXISTING : 0;
    80     truncate = (SDL_strchr(mode, 'w') != NULL) ? CREATE_ALWAYS : 0;
    81     r_right = (SDL_strchr(mode, '+') != NULL
    82                || must_exist) ? GENERIC_READ : 0;
    83     a_mode = (SDL_strchr(mode, 'a') != NULL) ? OPEN_ALWAYS : 0;
    84     w_right = (a_mode || SDL_strchr(mode, '+')
    85                || truncate) ? GENERIC_WRITE : 0;
    86 
    87     if (!r_right && !w_right)   /* inconsistent mode */
    88         return -1;              /* failed (invalid call) */
    89 
    90     context->hidden.windowsio.buffer.data =
    91         (char *) SDL_malloc(READAHEAD_BUFFER_SIZE);
    92     if (!context->hidden.windowsio.buffer.data) {
    93         SDL_OutOfMemory();
    94         return -1;
    95     }
    96     /* Do not open a dialog box if failure */
    97     old_error_mode =
    98         SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
    99 
   100     {
   101         LPTSTR tstr = WIN_UTF8ToString(filename);
   102         h = CreateFile(tstr, (w_right | r_right),
   103                        (w_right) ? 0 : FILE_SHARE_READ, NULL,
   104                        (must_exist | truncate | a_mode),
   105                        FILE_ATTRIBUTE_NORMAL, NULL);
   106         SDL_free(tstr);
   107     }
   108 
   109     /* restore old behavior */
   110     SetErrorMode(old_error_mode);
   111 
   112     if (h == INVALID_HANDLE_VALUE) {
   113         SDL_free(context->hidden.windowsio.buffer.data);
   114         context->hidden.windowsio.buffer.data = NULL;
   115         SDL_SetError("Couldn't open %s", filename);
   116         return -2;              /* failed (CreateFile) */
   117     }
   118     context->hidden.windowsio.h = h;
   119     context->hidden.windowsio.append = a_mode ? SDL_TRUE : SDL_FALSE;
   120 
   121     return 0;                   /* ok */
   122 }
   123 
   124 static long SDLCALL
   125 windows_file_seek(SDL_RWops * context, long offset, int whence)
   126 {
   127     DWORD windowswhence;
   128     long file_pos;
   129 
   130     if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE) {
   131         SDL_SetError("windows_file_seek: invalid context/file not opened");
   132         return -1;
   133     }
   134 
   135     /* FIXME: We may be able to satisfy the seek within buffered data */
   136     if (whence == RW_SEEK_CUR && context->hidden.windowsio.buffer.left) {
   137         offset -= (long)context->hidden.windowsio.buffer.left;
   138     }
   139     context->hidden.windowsio.buffer.left = 0;
   140 
   141     switch (whence) {
   142     case RW_SEEK_SET:
   143         windowswhence = FILE_BEGIN;
   144         break;
   145     case RW_SEEK_CUR:
   146         windowswhence = FILE_CURRENT;
   147         break;
   148     case RW_SEEK_END:
   149         windowswhence = FILE_END;
   150         break;
   151     default:
   152         SDL_SetError("windows_file_seek: Unknown value for 'whence'");
   153         return -1;
   154     }
   155 
   156     file_pos =
   157         SetFilePointer(context->hidden.windowsio.h, offset, NULL, windowswhence);
   158 
   159     if (file_pos != INVALID_SET_FILE_POINTER)
   160         return file_pos;        /* success */
   161 
   162     SDL_Error(SDL_EFSEEK);
   163     return -1;                  /* error */
   164 }
   165 
   166 static size_t SDLCALL
   167 windows_file_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
   168 {
   169     size_t total_need;
   170     size_t total_read = 0;
   171     size_t read_ahead;
   172     DWORD byte_read;
   173 
   174     total_need = size * maxnum;
   175 
   176     if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE
   177         || !total_need)
   178         return 0;
   179 
   180     if (context->hidden.windowsio.buffer.left > 0) {
   181         void *data = (char *) context->hidden.windowsio.buffer.data +
   182             context->hidden.windowsio.buffer.size -
   183             context->hidden.windowsio.buffer.left;
   184         read_ahead =
   185             SDL_min(total_need, context->hidden.windowsio.buffer.left);
   186         SDL_memcpy(ptr, data, read_ahead);
   187         context->hidden.windowsio.buffer.left -= read_ahead;
   188 
   189         if (read_ahead == total_need) {
   190             return maxnum;
   191         }
   192         ptr = (char *) ptr + read_ahead;
   193         total_need -= read_ahead;
   194         total_read += read_ahead;
   195     }
   196 
   197     if (total_need < READAHEAD_BUFFER_SIZE) {
   198         if (!ReadFile
   199             (context->hidden.windowsio.h, context->hidden.windowsio.buffer.data,
   200              READAHEAD_BUFFER_SIZE, &byte_read, NULL)) {
   201             SDL_Error(SDL_EFREAD);
   202             return 0;
   203         }
   204         read_ahead = SDL_min(total_need, (int) byte_read);
   205         SDL_memcpy(ptr, context->hidden.windowsio.buffer.data, read_ahead);
   206         context->hidden.windowsio.buffer.size = byte_read;
   207         context->hidden.windowsio.buffer.left = byte_read - read_ahead;
   208         total_read += read_ahead;
   209     } else {
   210         if (!ReadFile
   211             (context->hidden.windowsio.h, ptr, (DWORD)total_need, &byte_read, NULL)) {
   212             SDL_Error(SDL_EFREAD);
   213             return 0;
   214         }
   215         total_read += byte_read;
   216     }
   217     return (total_read / size);
   218 }
   219 
   220 static size_t SDLCALL
   221 windows_file_write(SDL_RWops * context, const void *ptr, size_t size,
   222                  size_t num)
   223 {
   224 
   225     size_t total_bytes;
   226     DWORD byte_written;
   227     size_t nwritten;
   228 
   229     total_bytes = size * num;
   230 
   231     if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE
   232         || total_bytes <= 0 || !size)
   233         return 0;
   234 
   235     if (context->hidden.windowsio.buffer.left) {
   236         SetFilePointer(context->hidden.windowsio.h,
   237                        -(LONG)context->hidden.windowsio.buffer.left, NULL,
   238                        FILE_CURRENT);
   239         context->hidden.windowsio.buffer.left = 0;
   240     }
   241 
   242     /* if in append mode, we must go to the EOF before write */
   243     if (context->hidden.windowsio.append) {
   244         if (SetFilePointer(context->hidden.windowsio.h, 0L, NULL, FILE_END) ==
   245             INVALID_SET_FILE_POINTER) {
   246             SDL_Error(SDL_EFWRITE);
   247             return 0;
   248         }
   249     }
   250 
   251     if (!WriteFile
   252         (context->hidden.windowsio.h, ptr, (DWORD)total_bytes, &byte_written, NULL)) {
   253         SDL_Error(SDL_EFWRITE);
   254         return 0;
   255     }
   256 
   257     nwritten = byte_written / size;
   258     return nwritten;
   259 }
   260 
   261 static int SDLCALL
   262 windows_file_close(SDL_RWops * context)
   263 {
   264 
   265     if (context) {
   266         if (context->hidden.windowsio.h != INVALID_HANDLE_VALUE) {
   267             CloseHandle(context->hidden.windowsio.h);
   268             context->hidden.windowsio.h = INVALID_HANDLE_VALUE;   /* to be sure */
   269         }
   270         if (context->hidden.windowsio.buffer.data) {
   271             SDL_free(context->hidden.windowsio.buffer.data);
   272             context->hidden.windowsio.buffer.data = NULL;
   273         }
   274         SDL_FreeRW(context);
   275     }
   276     return (0);
   277 }
   278 #endif /* __WIN32__ */
   279 
   280 #ifdef HAVE_STDIO_H
   281 
   282 /* Functions to read/write stdio file pointers */
   283 
   284 static long SDLCALL
   285 stdio_seek(SDL_RWops * context, long offset, int whence)
   286 {
   287     if (fseek(context->hidden.stdio.fp, offset, whence) == 0) {
   288         return (ftell(context->hidden.stdio.fp));
   289     } else {
   290         SDL_Error(SDL_EFSEEK);
   291         return (-1);
   292     }
   293 }
   294 
   295 static size_t SDLCALL
   296 stdio_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
   297 {
   298     size_t nread;
   299 
   300     nread = fread(ptr, size, maxnum, context->hidden.stdio.fp);
   301     if (nread == 0 && ferror(context->hidden.stdio.fp)) {
   302         SDL_Error(SDL_EFREAD);
   303     }
   304     return (nread);
   305 }
   306 
   307 static size_t SDLCALL
   308 stdio_write(SDL_RWops * context, const void *ptr, size_t size, size_t num)
   309 {
   310     size_t nwrote;
   311 
   312     nwrote = fwrite(ptr, size, num, context->hidden.stdio.fp);
   313     if (nwrote == 0 && ferror(context->hidden.stdio.fp)) {
   314         SDL_Error(SDL_EFWRITE);
   315     }
   316     return (nwrote);
   317 }
   318 
   319 static int SDLCALL
   320 stdio_close(SDL_RWops * context)
   321 {
   322     int status = 0;
   323     if (context) {
   324         if (context->hidden.stdio.autoclose) {
   325             /* WARNING:  Check the return value here! */
   326             if (fclose(context->hidden.stdio.fp) != 0) {
   327                 SDL_Error(SDL_EFWRITE);
   328                 status = -1;
   329             }
   330         }
   331         SDL_FreeRW(context);
   332     }
   333     return status;
   334 }
   335 #endif /* !HAVE_STDIO_H */
   336 
   337 /* Functions to read/write memory pointers */
   338 
   339 static long SDLCALL
   340 mem_seek(SDL_RWops * context, long offset, int whence)
   341 {
   342     Uint8 *newpos;
   343 
   344     switch (whence) {
   345     case RW_SEEK_SET:
   346         newpos = context->hidden.mem.base + offset;
   347         break;
   348     case RW_SEEK_CUR:
   349         newpos = context->hidden.mem.here + offset;
   350         break;
   351     case RW_SEEK_END:
   352         newpos = context->hidden.mem.stop + offset;
   353         break;
   354     default:
   355         SDL_SetError("Unknown value for 'whence'");
   356         return (-1);
   357     }
   358     if (newpos < context->hidden.mem.base) {
   359         newpos = context->hidden.mem.base;
   360     }
   361     if (newpos > context->hidden.mem.stop) {
   362         newpos = context->hidden.mem.stop;
   363     }
   364     context->hidden.mem.here = newpos;
   365     return (long)(context->hidden.mem.here - context->hidden.mem.base);
   366 }
   367 
   368 static size_t SDLCALL
   369 mem_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
   370 {
   371     size_t total_bytes;
   372     size_t mem_available;
   373 
   374     total_bytes = (maxnum * size);
   375     if ((maxnum <= 0) || (size <= 0)
   376         || ((total_bytes / maxnum) != (size_t) size)) {
   377         return 0;
   378     }
   379 
   380     mem_available = (context->hidden.mem.stop - context->hidden.mem.here);
   381     if (total_bytes > mem_available) {
   382         total_bytes = mem_available;
   383     }
   384 
   385     SDL_memcpy(ptr, context->hidden.mem.here, total_bytes);
   386     context->hidden.mem.here += total_bytes;
   387 
   388     return (total_bytes / size);
   389 }
   390 
   391 static size_t SDLCALL
   392 mem_write(SDL_RWops * context, const void *ptr, size_t size, size_t num)
   393 {
   394     if ((context->hidden.mem.here + (num * size)) > context->hidden.mem.stop) {
   395         num = (context->hidden.mem.stop - context->hidden.mem.here) / size;
   396     }
   397     SDL_memcpy(context->hidden.mem.here, ptr, num * size);
   398     context->hidden.mem.here += num * size;
   399     return (num);
   400 }
   401 
   402 static size_t SDLCALL
   403 mem_writeconst(SDL_RWops * context, const void *ptr, size_t size, size_t num)
   404 {
   405     SDL_SetError("Can't write to read-only memory");
   406     return (-1);
   407 }
   408 
   409 static int SDLCALL
   410 mem_close(SDL_RWops * context)
   411 {
   412     if (context) {
   413         SDL_FreeRW(context);
   414     }
   415     return (0);
   416 }
   417 
   418 
   419 /* Functions to create SDL_RWops structures from various data sources */
   420 
   421 SDL_RWops *
   422 SDL_RWFromFile(const char *file, const char *mode)
   423 {
   424     SDL_RWops *rwops = NULL;
   425     if (!file || !*file || !mode || !*mode) {
   426         SDL_SetError("SDL_RWFromFile(): No file or no mode specified");
   427         return NULL;
   428     }
   429 #if defined(ANDROID)
   430     rwops = SDL_AllocRW();
   431     if (!rwops)
   432         return NULL;            /* SDL_SetError already setup by SDL_AllocRW() */
   433     if (Android_JNI_FileOpen(rwops, file, mode) < 0) {
   434         SDL_FreeRW(rwops);
   435         return NULL;
   436     }
   437     rwops->seek = Android_JNI_FileSeek;
   438     rwops->read = Android_JNI_FileRead;
   439     rwops->write = Android_JNI_FileWrite;
   440     rwops->close = Android_JNI_FileClose;
   441 
   442 #elif defined(__WIN32__)
   443     rwops = SDL_AllocRW();
   444     if (!rwops)
   445         return NULL;            /* SDL_SetError already setup by SDL_AllocRW() */
   446     if (windows_file_open(rwops, file, mode) < 0) {
   447         SDL_FreeRW(rwops);
   448         return NULL;
   449     }
   450     rwops->seek = windows_file_seek;
   451     rwops->read = windows_file_read;
   452     rwops->write = windows_file_write;
   453     rwops->close = windows_file_close;
   454 
   455 #elif HAVE_STDIO_H
   456     {
   457     	#ifdef __APPLE__
   458     	FILE *fp = SDL_OpenFPFromBundleOrFallback(file, mode);
   459         #else
   460     	FILE *fp = fopen(file, mode);
   461     	#endif
   462     	if (fp == NULL) {
   463             SDL_SetError("Couldn't open %s", file);
   464         } else {
   465             rwops = SDL_RWFromFP(fp, 1);
   466         }
   467     }
   468 #else
   469     SDL_SetError("SDL not compiled with stdio support");
   470 #endif /* !HAVE_STDIO_H */
   471 
   472     return (rwops);
   473 }
   474 
   475 #ifdef HAVE_STDIO_H
   476 SDL_RWops *
   477 SDL_RWFromFP(FILE * fp, SDL_bool autoclose)
   478 {
   479     SDL_RWops *rwops = NULL;
   480 
   481 #if 0
   482 /*#ifdef __NDS__*/
   483     /* set it up so we can use stdio file function */
   484     fatInitDefault();
   485     printf("called fatInitDefault()");
   486 #endif /* __NDS__ */
   487 
   488     rwops = SDL_AllocRW();
   489     if (rwops != NULL) {
   490         rwops->seek = stdio_seek;
   491         rwops->read = stdio_read;
   492         rwops->write = stdio_write;
   493         rwops->close = stdio_close;
   494         rwops->hidden.stdio.fp = fp;
   495         rwops->hidden.stdio.autoclose = autoclose;
   496     }
   497     return (rwops);
   498 }
   499 #else
   500 SDL_RWops *
   501 SDL_RWFromFP(void * fp, SDL_bool autoclose)
   502 {
   503     SDL_SetError("SDL not compiled with stdio support");
   504     return NULL;
   505 }
   506 #endif /* HAVE_STDIO_H */
   507 
   508 SDL_RWops *
   509 SDL_RWFromMem(void *mem, int size)
   510 {
   511     SDL_RWops *rwops;
   512 
   513     rwops = SDL_AllocRW();
   514     if (rwops != NULL) {
   515         rwops->seek = mem_seek;
   516         rwops->read = mem_read;
   517         rwops->write = mem_write;
   518         rwops->close = mem_close;
   519         rwops->hidden.mem.base = (Uint8 *) mem;
   520         rwops->hidden.mem.here = rwops->hidden.mem.base;
   521         rwops->hidden.mem.stop = rwops->hidden.mem.base + size;
   522     }
   523     return (rwops);
   524 }
   525 
   526 SDL_RWops *
   527 SDL_RWFromConstMem(const void *mem, int size)
   528 {
   529     SDL_RWops *rwops;
   530 
   531     rwops = SDL_AllocRW();
   532     if (rwops != NULL) {
   533         rwops->seek = mem_seek;
   534         rwops->read = mem_read;
   535         rwops->write = mem_writeconst;
   536         rwops->close = mem_close;
   537         rwops->hidden.mem.base = (Uint8 *) mem;
   538         rwops->hidden.mem.here = rwops->hidden.mem.base;
   539         rwops->hidden.mem.stop = rwops->hidden.mem.base + size;
   540     }
   541     return (rwops);
   542 }
   543 
   544 SDL_RWops *
   545 SDL_AllocRW(void)
   546 {
   547     SDL_RWops *area;
   548 
   549     area = (SDL_RWops *) SDL_malloc(sizeof *area);
   550     if (area == NULL) {
   551         SDL_OutOfMemory();
   552     }
   553     return (area);
   554 }
   555 
   556 void
   557 SDL_FreeRW(SDL_RWops * area)
   558 {
   559     SDL_free(area);
   560 }
   561 
   562 /* Functions for dynamically reading and writing endian-specific values */
   563 
   564 Uint16
   565 SDL_ReadLE16(SDL_RWops * src)
   566 {
   567     Uint16 value;
   568 
   569     SDL_RWread(src, &value, (sizeof value), 1);
   570     return (SDL_SwapLE16(value));
   571 }
   572 
   573 Uint16
   574 SDL_ReadBE16(SDL_RWops * src)
   575 {
   576     Uint16 value;
   577 
   578     SDL_RWread(src, &value, (sizeof value), 1);
   579     return (SDL_SwapBE16(value));
   580 }
   581 
   582 Uint32
   583 SDL_ReadLE32(SDL_RWops * src)
   584 {
   585     Uint32 value;
   586 
   587     SDL_RWread(src, &value, (sizeof value), 1);
   588     return (SDL_SwapLE32(value));
   589 }
   590 
   591 Uint32
   592 SDL_ReadBE32(SDL_RWops * src)
   593 {
   594     Uint32 value;
   595 
   596     SDL_RWread(src, &value, (sizeof value), 1);
   597     return (SDL_SwapBE32(value));
   598 }
   599 
   600 Uint64
   601 SDL_ReadLE64(SDL_RWops * src)
   602 {
   603     Uint64 value;
   604 
   605     SDL_RWread(src, &value, (sizeof value), 1);
   606     return (SDL_SwapLE64(value));
   607 }
   608 
   609 Uint64
   610 SDL_ReadBE64(SDL_RWops * src)
   611 {
   612     Uint64 value;
   613 
   614     SDL_RWread(src, &value, (sizeof value), 1);
   615     return (SDL_SwapBE64(value));
   616 }
   617 
   618 size_t
   619 SDL_WriteLE16(SDL_RWops * dst, Uint16 value)
   620 {
   621     value = SDL_SwapLE16(value);
   622     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   623 }
   624 
   625 size_t
   626 SDL_WriteBE16(SDL_RWops * dst, Uint16 value)
   627 {
   628     value = SDL_SwapBE16(value);
   629     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   630 }
   631 
   632 size_t
   633 SDL_WriteLE32(SDL_RWops * dst, Uint32 value)
   634 {
   635     value = SDL_SwapLE32(value);
   636     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   637 }
   638 
   639 size_t
   640 SDL_WriteBE32(SDL_RWops * dst, Uint32 value)
   641 {
   642     value = SDL_SwapBE32(value);
   643     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   644 }
   645 
   646 size_t
   647 SDL_WriteLE64(SDL_RWops * dst, Uint64 value)
   648 {
   649     value = SDL_SwapLE64(value);
   650     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   651 }
   652 
   653 size_t
   654 SDL_WriteBE64(SDL_RWops * dst, Uint64 value)
   655 {
   656     value = SDL_SwapBE64(value);
   657     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   658 }
   659 
   660 /* vi: set ts=4 sw=4 expandtab: */