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