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