src/file/SDL_rwops.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 02 Nov 2012 00:35:48 -0700
changeset 6629 8e2c731103e6
parent 6430 48d519500f7e
child 6633 e36b94438fcf
permissions -rw-r--r--
Try opening the file path directly before hitting the asset system.
     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 #ifdef HAVE_STDIO_H
   431     /* Try to open the file on the filesystem first */
   432     {
   433         FILE *fp = fopen(file, mode);
   434         if (fp) {
   435             return SDL_RWFromFP(fp, 1);
   436         }
   437     }
   438 #endif
   439 
   440     /* Try to open the file from the asset system */
   441     rwops = SDL_AllocRW();
   442     if (!rwops)
   443         return NULL;            /* SDL_SetError already setup by SDL_AllocRW() */
   444     if (Android_JNI_FileOpen(rwops, file, mode) < 0) {
   445         SDL_FreeRW(rwops);
   446         return NULL;
   447     }
   448     rwops->seek = Android_JNI_FileSeek;
   449     rwops->read = Android_JNI_FileRead;
   450     rwops->write = Android_JNI_FileWrite;
   451     rwops->close = Android_JNI_FileClose;
   452 
   453 #elif defined(__WIN32__)
   454     rwops = SDL_AllocRW();
   455     if (!rwops)
   456         return NULL;            /* SDL_SetError already setup by SDL_AllocRW() */
   457     if (windows_file_open(rwops, file, mode) < 0) {
   458         SDL_FreeRW(rwops);
   459         return NULL;
   460     }
   461     rwops->seek = windows_file_seek;
   462     rwops->read = windows_file_read;
   463     rwops->write = windows_file_write;
   464     rwops->close = windows_file_close;
   465 
   466 #elif HAVE_STDIO_H
   467     {
   468     	#ifdef __APPLE__
   469     	FILE *fp = SDL_OpenFPFromBundleOrFallback(file, mode);
   470         #else
   471     	FILE *fp = fopen(file, mode);
   472     	#endif
   473     	if (fp == NULL) {
   474             SDL_SetError("Couldn't open %s", file);
   475         } else {
   476             rwops = SDL_RWFromFP(fp, 1);
   477         }
   478     }
   479 #else
   480     SDL_SetError("SDL not compiled with stdio support");
   481 #endif /* !HAVE_STDIO_H */
   482 
   483     return (rwops);
   484 }
   485 
   486 #ifdef HAVE_STDIO_H
   487 SDL_RWops *
   488 SDL_RWFromFP(FILE * fp, SDL_bool autoclose)
   489 {
   490     SDL_RWops *rwops = NULL;
   491 
   492 #if 0
   493 /*#ifdef __NDS__*/
   494     /* set it up so we can use stdio file function */
   495     fatInitDefault();
   496     printf("called fatInitDefault()");
   497 #endif /* __NDS__ */
   498 
   499     rwops = SDL_AllocRW();
   500     if (rwops != NULL) {
   501         rwops->seek = stdio_seek;
   502         rwops->read = stdio_read;
   503         rwops->write = stdio_write;
   504         rwops->close = stdio_close;
   505         rwops->hidden.stdio.fp = fp;
   506         rwops->hidden.stdio.autoclose = autoclose;
   507     }
   508     return (rwops);
   509 }
   510 #else
   511 SDL_RWops *
   512 SDL_RWFromFP(void * fp, SDL_bool autoclose)
   513 {
   514     SDL_SetError("SDL not compiled with stdio support");
   515     return NULL;
   516 }
   517 #endif /* HAVE_STDIO_H */
   518 
   519 SDL_RWops *
   520 SDL_RWFromMem(void *mem, int size)
   521 {
   522     SDL_RWops *rwops;
   523 
   524     rwops = SDL_AllocRW();
   525     if (rwops != NULL) {
   526         rwops->seek = mem_seek;
   527         rwops->read = mem_read;
   528         rwops->write = mem_write;
   529         rwops->close = mem_close;
   530         rwops->hidden.mem.base = (Uint8 *) mem;
   531         rwops->hidden.mem.here = rwops->hidden.mem.base;
   532         rwops->hidden.mem.stop = rwops->hidden.mem.base + size;
   533     }
   534     return (rwops);
   535 }
   536 
   537 SDL_RWops *
   538 SDL_RWFromConstMem(const void *mem, int size)
   539 {
   540     SDL_RWops *rwops;
   541 
   542     rwops = SDL_AllocRW();
   543     if (rwops != NULL) {
   544         rwops->seek = mem_seek;
   545         rwops->read = mem_read;
   546         rwops->write = mem_writeconst;
   547         rwops->close = mem_close;
   548         rwops->hidden.mem.base = (Uint8 *) mem;
   549         rwops->hidden.mem.here = rwops->hidden.mem.base;
   550         rwops->hidden.mem.stop = rwops->hidden.mem.base + size;
   551     }
   552     return (rwops);
   553 }
   554 
   555 SDL_RWops *
   556 SDL_AllocRW(void)
   557 {
   558     SDL_RWops *area;
   559 
   560     area = (SDL_RWops *) SDL_malloc(sizeof *area);
   561     if (area == NULL) {
   562         SDL_OutOfMemory();
   563     }
   564     return (area);
   565 }
   566 
   567 void
   568 SDL_FreeRW(SDL_RWops * area)
   569 {
   570     SDL_free(area);
   571 }
   572 
   573 /* Functions for dynamically reading and writing endian-specific values */
   574 
   575 Uint16
   576 SDL_ReadLE16(SDL_RWops * src)
   577 {
   578     Uint16 value;
   579 
   580     SDL_RWread(src, &value, (sizeof value), 1);
   581     return (SDL_SwapLE16(value));
   582 }
   583 
   584 Uint16
   585 SDL_ReadBE16(SDL_RWops * src)
   586 {
   587     Uint16 value;
   588 
   589     SDL_RWread(src, &value, (sizeof value), 1);
   590     return (SDL_SwapBE16(value));
   591 }
   592 
   593 Uint32
   594 SDL_ReadLE32(SDL_RWops * src)
   595 {
   596     Uint32 value;
   597 
   598     SDL_RWread(src, &value, (sizeof value), 1);
   599     return (SDL_SwapLE32(value));
   600 }
   601 
   602 Uint32
   603 SDL_ReadBE32(SDL_RWops * src)
   604 {
   605     Uint32 value;
   606 
   607     SDL_RWread(src, &value, (sizeof value), 1);
   608     return (SDL_SwapBE32(value));
   609 }
   610 
   611 Uint64
   612 SDL_ReadLE64(SDL_RWops * src)
   613 {
   614     Uint64 value;
   615 
   616     SDL_RWread(src, &value, (sizeof value), 1);
   617     return (SDL_SwapLE64(value));
   618 }
   619 
   620 Uint64
   621 SDL_ReadBE64(SDL_RWops * src)
   622 {
   623     Uint64 value;
   624 
   625     SDL_RWread(src, &value, (sizeof value), 1);
   626     return (SDL_SwapBE64(value));
   627 }
   628 
   629 size_t
   630 SDL_WriteLE16(SDL_RWops * dst, Uint16 value)
   631 {
   632     value = SDL_SwapLE16(value);
   633     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   634 }
   635 
   636 size_t
   637 SDL_WriteBE16(SDL_RWops * dst, Uint16 value)
   638 {
   639     value = SDL_SwapBE16(value);
   640     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   641 }
   642 
   643 size_t
   644 SDL_WriteLE32(SDL_RWops * dst, Uint32 value)
   645 {
   646     value = SDL_SwapLE32(value);
   647     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   648 }
   649 
   650 size_t
   651 SDL_WriteBE32(SDL_RWops * dst, Uint32 value)
   652 {
   653     value = SDL_SwapBE32(value);
   654     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   655 }
   656 
   657 size_t
   658 SDL_WriteLE64(SDL_RWops * dst, Uint64 value)
   659 {
   660     value = SDL_SwapLE64(value);
   661     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   662 }
   663 
   664 size_t
   665 SDL_WriteBE64(SDL_RWops * dst, Uint64 value)
   666 {
   667     value = SDL_SwapBE64(value);
   668     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   669 }
   670 
   671 /* vi: set ts=4 sw=4 expandtab: */