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