src/file/SDL_rwops.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 27 Aug 2008 15:10:03 +0000
changeset 2735 204be4fc2726
parent 2185 2032348afed1
child 2859 99210400e8b9
permissions -rw-r--r--
Final merge of Google Summer of Code 2008 work...

Port SDL 1.3 to the Nintendo DS
by Darren Alton, mentored by Sam Lantinga
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2006 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 -= 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, (size_t) 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, 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, nwritten;
   243 
   244     total_bytes = size * num;
   245 
   246     if (!context || context->hidden.win32io.h == INVALID_HANDLE_VALUE
   247         || total_bytes <= 0 || !size)
   248         return 0;
   249 
   250     if (context->hidden.win32io.buffer.left) {
   251         SetFilePointer(context->hidden.win32io.h,
   252                        -context->hidden.win32io.buffer.left, NULL,
   253                        FILE_CURRENT);
   254         context->hidden.win32io.buffer.left = 0;
   255     }
   256 
   257     /* if in append mode, we must go to the EOF before write */
   258     if (context->hidden.win32io.append) {
   259         if (SetFilePointer(context->hidden.win32io.h, 0L, NULL, FILE_END) ==
   260             INVALID_SET_FILE_POINTER) {
   261             SDL_Error(SDL_EFWRITE);
   262             return 0;
   263         }
   264     }
   265 
   266     if (!WriteFile
   267         (context->hidden.win32io.h, ptr, total_bytes, &byte_written, NULL)) {
   268         SDL_Error(SDL_EFWRITE);
   269         return 0;
   270     }
   271 
   272     nwritten = byte_written / size;
   273     return nwritten;
   274 }
   275 
   276 static int SDLCALL
   277 win32_file_close(SDL_RWops * context)
   278 {
   279 
   280     if (context) {
   281         if (context->hidden.win32io.h != INVALID_HANDLE_VALUE) {
   282             CloseHandle(context->hidden.win32io.h);
   283             context->hidden.win32io.h = INVALID_HANDLE_VALUE;   /* to be sure */
   284         }
   285         if (context->hidden.win32io.buffer.data) {
   286             SDL_free(context->hidden.win32io.buffer.data);
   287             context->hidden.win32io.buffer.data = NULL;
   288         }
   289         SDL_FreeRW(context);
   290     }
   291     return (0);
   292 }
   293 #endif /* __WIN32__ */
   294 
   295 #ifdef HAVE_STDIO_H
   296 
   297 /* Functions to read/write stdio file pointers */
   298 
   299 static long SDLCALL
   300 stdio_seek(SDL_RWops * context, long offset, int whence)
   301 {
   302     if (fseek(context->hidden.stdio.fp, offset, whence) == 0) {
   303         return (ftell(context->hidden.stdio.fp));
   304     } else {
   305         SDL_Error(SDL_EFSEEK);
   306         return (-1);
   307     }
   308 }
   309 
   310 static size_t SDLCALL
   311 stdio_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
   312 {
   313     size_t nread;
   314 
   315     nread = fread(ptr, size, maxnum, context->hidden.stdio.fp);
   316     if (nread == 0 && ferror(context->hidden.stdio.fp)) {
   317         SDL_Error(SDL_EFREAD);
   318     }
   319     return (nread);
   320 }
   321 
   322 static size_t SDLCALL
   323 stdio_write(SDL_RWops * context, const void *ptr, size_t size, size_t num)
   324 {
   325     size_t nwrote;
   326 
   327     nwrote = fwrite(ptr, size, num, context->hidden.stdio.fp);
   328     if (nwrote == 0 && ferror(context->hidden.stdio.fp)) {
   329         SDL_Error(SDL_EFWRITE);
   330     }
   331     return (nwrote);
   332 }
   333 
   334 static int SDLCALL
   335 stdio_close(SDL_RWops * context)
   336 {
   337     int status = 0;
   338     if (context) {
   339         if (context->hidden.stdio.autoclose) {
   340             /* WARNING:  Check the return value here! */
   341             if (fclose(context->hidden.stdio.fp) != 0) {
   342                 SDL_Error(SDL_EFWRITE);
   343                 status = -1;
   344             }
   345         }
   346         SDL_FreeRW(context);
   347     }
   348     return status;
   349 }
   350 #endif /* !HAVE_STDIO_H */
   351 
   352 /* Functions to read/write memory pointers */
   353 
   354 static long SDLCALL
   355 mem_seek(SDL_RWops * context, long offset, int whence)
   356 {
   357     Uint8 *newpos;
   358 
   359     switch (whence) {
   360     case RW_SEEK_SET:
   361         newpos = context->hidden.mem.base + offset;
   362         break;
   363     case RW_SEEK_CUR:
   364         newpos = context->hidden.mem.here + offset;
   365         break;
   366     case RW_SEEK_END:
   367         newpos = context->hidden.mem.stop + offset;
   368         break;
   369     default:
   370         SDL_SetError("Unknown value for 'whence'");
   371         return (-1);
   372     }
   373     if (newpos < context->hidden.mem.base) {
   374         newpos = context->hidden.mem.base;
   375     }
   376     if (newpos > context->hidden.mem.stop) {
   377         newpos = context->hidden.mem.stop;
   378     }
   379     context->hidden.mem.here = newpos;
   380     return (context->hidden.mem.here - context->hidden.mem.base);
   381 }
   382 
   383 static size_t SDLCALL
   384 mem_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
   385 {
   386     size_t total_bytes;
   387     size_t mem_available;
   388 
   389     total_bytes = (maxnum * size);
   390     if ((maxnum <= 0) || (size <= 0)
   391         || ((total_bytes / maxnum) != (size_t) size)) {
   392         return 0;
   393     }
   394 
   395     mem_available = (context->hidden.mem.stop - context->hidden.mem.here);
   396     if (total_bytes > mem_available) {
   397         total_bytes = mem_available;
   398     }
   399 
   400     SDL_memcpy(ptr, context->hidden.mem.here, total_bytes);
   401     context->hidden.mem.here += total_bytes;
   402 
   403     return (total_bytes / size);
   404 }
   405 
   406 static size_t SDLCALL
   407 mem_write(SDL_RWops * context, const void *ptr, size_t size, size_t num)
   408 {
   409     if ((context->hidden.mem.here + (num * size)) > context->hidden.mem.stop) {
   410         num = (context->hidden.mem.stop - context->hidden.mem.here) / size;
   411     }
   412     SDL_memcpy(context->hidden.mem.here, ptr, num * size);
   413     context->hidden.mem.here += num * size;
   414     return (num);
   415 }
   416 
   417 static size_t SDLCALL
   418 mem_writeconst(SDL_RWops * context, const void *ptr, size_t size, size_t num)
   419 {
   420     SDL_SetError("Can't write to read-only memory");
   421     return (-1);
   422 }
   423 
   424 static int SDLCALL
   425 mem_close(SDL_RWops * context)
   426 {
   427     if (context) {
   428         SDL_FreeRW(context);
   429     }
   430     return (0);
   431 }
   432 
   433 
   434 /* Functions to create SDL_RWops structures from various data sources */
   435 
   436 SDL_RWops *
   437 SDL_RWFromFile(const char *file, const char *mode)
   438 {
   439     SDL_RWops *rwops = NULL;
   440 #ifdef HAVE_STDIO_H
   441     FILE *fp = NULL;
   442 #endif
   443     if (!file || !*file || !mode || !*mode) {
   444         SDL_SetError("SDL_RWFromFile(): No file or no mode specified");
   445         return NULL;
   446     }
   447 #if defined(__WIN32__)
   448     rwops = SDL_AllocRW();
   449     if (!rwops)
   450         return NULL;            /* SDL_SetError already setup by SDL_AllocRW() */
   451     if (win32_file_open(rwops, file, mode) < 0) {
   452         SDL_FreeRW(rwops);
   453         return NULL;
   454     }
   455     rwops->seek = win32_file_seek;
   456     rwops->read = win32_file_read;
   457     rwops->write = win32_file_write;
   458     rwops->close = win32_file_close;
   459 
   460 #elif HAVE_STDIO_H
   461 
   462     fp = fopen(file, mode);
   463     if (fp == NULL) {
   464         SDL_SetError("Couldn't open %s", file);
   465     } else {
   466         rwops = SDL_RWFromFP(fp, 1);
   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 #endif /* HAVE_STDIO_H */
   500 
   501 SDL_RWops *
   502 SDL_RWFromMem(void *mem, int size)
   503 {
   504     SDL_RWops *rwops;
   505 
   506     rwops = SDL_AllocRW();
   507     if (rwops != NULL) {
   508         rwops->seek = mem_seek;
   509         rwops->read = mem_read;
   510         rwops->write = mem_write;
   511         rwops->close = mem_close;
   512         rwops->hidden.mem.base = (Uint8 *) mem;
   513         rwops->hidden.mem.here = rwops->hidden.mem.base;
   514         rwops->hidden.mem.stop = rwops->hidden.mem.base + size;
   515     }
   516     return (rwops);
   517 }
   518 
   519 SDL_RWops *
   520 SDL_RWFromConstMem(const 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_writeconst;
   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_AllocRW(void)
   539 {
   540     SDL_RWops *area;
   541 
   542     area = (SDL_RWops *) SDL_malloc(sizeof *area);
   543     if (area == NULL) {
   544         SDL_OutOfMemory();
   545     }
   546     return (area);
   547 }
   548 
   549 void
   550 SDL_FreeRW(SDL_RWops * area)
   551 {
   552     SDL_free(area);
   553 }
   554 
   555 /* Functions for dynamically reading and writing endian-specific values */
   556 
   557 Uint16
   558 SDL_ReadLE16(SDL_RWops * src)
   559 {
   560     Uint16 value;
   561 
   562     SDL_RWread(src, &value, (sizeof value), 1);
   563     return (SDL_SwapLE16(value));
   564 }
   565 
   566 Uint16
   567 SDL_ReadBE16(SDL_RWops * src)
   568 {
   569     Uint16 value;
   570 
   571     SDL_RWread(src, &value, (sizeof value), 1);
   572     return (SDL_SwapBE16(value));
   573 }
   574 
   575 Uint32
   576 SDL_ReadLE32(SDL_RWops * src)
   577 {
   578     Uint32 value;
   579 
   580     SDL_RWread(src, &value, (sizeof value), 1);
   581     return (SDL_SwapLE32(value));
   582 }
   583 
   584 Uint32
   585 SDL_ReadBE32(SDL_RWops * src)
   586 {
   587     Uint32 value;
   588 
   589     SDL_RWread(src, &value, (sizeof value), 1);
   590     return (SDL_SwapBE32(value));
   591 }
   592 
   593 Uint64
   594 SDL_ReadLE64(SDL_RWops * src)
   595 {
   596     Uint64 value;
   597 
   598     SDL_RWread(src, &value, (sizeof value), 1);
   599     return (SDL_SwapLE64(value));
   600 }
   601 
   602 Uint64
   603 SDL_ReadBE64(SDL_RWops * src)
   604 {
   605     Uint64 value;
   606 
   607     SDL_RWread(src, &value, (sizeof value), 1);
   608     return (SDL_SwapBE64(value));
   609 }
   610 
   611 int
   612 SDL_WriteLE16(SDL_RWops * dst, Uint16 value)
   613 {
   614     value = SDL_SwapLE16(value);
   615     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   616 }
   617 
   618 int
   619 SDL_WriteBE16(SDL_RWops * dst, Uint16 value)
   620 {
   621     value = SDL_SwapBE16(value);
   622     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   623 }
   624 
   625 int
   626 SDL_WriteLE32(SDL_RWops * dst, Uint32 value)
   627 {
   628     value = SDL_SwapLE32(value);
   629     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   630 }
   631 
   632 int
   633 SDL_WriteBE32(SDL_RWops * dst, Uint32 value)
   634 {
   635     value = SDL_SwapBE32(value);
   636     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   637 }
   638 
   639 int
   640 SDL_WriteLE64(SDL_RWops * dst, Uint64 value)
   641 {
   642     value = SDL_SwapLE64(value);
   643     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   644 }
   645 
   646 int
   647 SDL_WriteBE64(SDL_RWops * dst, Uint64 value)
   648 {
   649     value = SDL_SwapBE64(value);
   650     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   651 }
   652 
   653 /* vi: set ts=4 sw=4 expandtab: */