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