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