src/file/SDL_rwops.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 10 Jul 2007 05:01:22 +0000
changeset 2159 dd4753e47ed4
parent 1978 542c78b6fb12
child 2160 00adbaed3910
permissions -rw-r--r--
Merged read-ahead support for Win32 file I/O from SDL 1.2 revision 3183
     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 int SDLCALL
   132 win32_file_seek(SDL_RWops * context, int offset, int whence)
   133 {
   134     DWORD win32whence;
   135     int 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 int SDLCALL
   173 win32_file_read(SDL_RWops * context, void *ptr, int size, int maxnum)
   174 {
   175     int total_need;
   176     int total_read = 0;
   177     int 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 <= 0 || !size)
   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 int SDLCALL
   225 win32_file_write(SDL_RWops * context, const void *ptr, int size, int num)
   226 {
   227 
   228     int 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 int SDLCALL
   286 stdio_seek(SDL_RWops * context, int 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 int SDLCALL
   296 stdio_read(SDL_RWops * context, void *ptr, int size, int 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 int SDLCALL
   307 stdio_write(SDL_RWops * context, const void *ptr, int size, int 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     if (context) {
   321         if (context->hidden.stdio.autoclose) {
   322             /* WARNING:  Check the return value here! */
   323             fclose(context->hidden.stdio.fp);
   324         }
   325         SDL_FreeRW(context);
   326     }
   327     return (0);
   328 }
   329 #endif /* !HAVE_STDIO_H */
   330 
   331 /* Functions to read/write memory pointers */
   332 
   333 static int SDLCALL
   334 mem_seek(SDL_RWops * context, int offset, int whence)
   335 {
   336     Uint8 *newpos;
   337 
   338     switch (whence) {
   339     case RW_SEEK_SET:
   340         newpos = context->hidden.mem.base + offset;
   341         break;
   342     case RW_SEEK_CUR:
   343         newpos = context->hidden.mem.here + offset;
   344         break;
   345     case RW_SEEK_END:
   346         newpos = context->hidden.mem.stop + offset;
   347         break;
   348     default:
   349         SDL_SetError("Unknown value for 'whence'");
   350         return (-1);
   351     }
   352     if (newpos < context->hidden.mem.base) {
   353         newpos = context->hidden.mem.base;
   354     }
   355     if (newpos > context->hidden.mem.stop) {
   356         newpos = context->hidden.mem.stop;
   357     }
   358     context->hidden.mem.here = newpos;
   359     return (context->hidden.mem.here - context->hidden.mem.base);
   360 }
   361 static int SDLCALL
   362 mem_read(SDL_RWops * context, void *ptr, int size, int maxnum)
   363 {
   364     size_t total_bytes;
   365     size_t mem_available;
   366 
   367     total_bytes = (maxnum * size);
   368     if ((maxnum <= 0) || (size <= 0)
   369         || ((total_bytes / maxnum) != (size_t) size)) {
   370         return 0;
   371     }
   372 
   373     mem_available = (context->hidden.mem.stop - context->hidden.mem.here);
   374     if (total_bytes > mem_available) {
   375         total_bytes = mem_available;
   376     }
   377 
   378     SDL_memcpy(ptr, context->hidden.mem.here, total_bytes);
   379     context->hidden.mem.here += total_bytes;
   380 
   381     return (total_bytes / size);
   382 }
   383 static int SDLCALL
   384 mem_write(SDL_RWops * context, const void *ptr, int size, int num)
   385 {
   386     if ((context->hidden.mem.here + (num * size)) > context->hidden.mem.stop) {
   387         num = (context->hidden.mem.stop - context->hidden.mem.here) / size;
   388     }
   389     SDL_memcpy(context->hidden.mem.here, ptr, num * size);
   390     context->hidden.mem.here += num * size;
   391     return (num);
   392 }
   393 static int SDLCALL
   394 mem_writeconst(SDL_RWops * context, const void *ptr, int size, int num)
   395 {
   396     SDL_SetError("Can't write to read-only memory");
   397     return (-1);
   398 }
   399 static int SDLCALL
   400 mem_close(SDL_RWops * context)
   401 {
   402     if (context) {
   403         SDL_FreeRW(context);
   404     }
   405     return (0);
   406 }
   407 
   408 
   409 /* Functions to create SDL_RWops structures from various data sources */
   410 
   411 SDL_RWops *
   412 SDL_RWFromFile(const char *file, const char *mode)
   413 {
   414     SDL_RWops *rwops = NULL;
   415 #ifdef HAVE_STDIO_H
   416     FILE *fp = NULL;
   417 #endif
   418     if (!file || !*file || !mode || !*mode) {
   419         SDL_SetError("SDL_RWFromFile(): No file or no mode specified");
   420         return NULL;
   421     }
   422 #if defined(__WIN32__)
   423     rwops = SDL_AllocRW();
   424     if (!rwops)
   425         return NULL;            /* SDL_SetError already setup by SDL_AllocRW() */
   426     rwops->hidden.win32io.h = INVALID_HANDLE_VALUE;
   427     if (win32_file_open(rwops, file, mode)) {
   428         SDL_FreeRW(rwops);
   429         return NULL;
   430     }
   431     rwops->seek = win32_file_seek;
   432     rwops->read = win32_file_read;
   433     rwops->write = win32_file_write;
   434     rwops->close = win32_file_close;
   435 
   436 #elif HAVE_STDIO_H
   437 
   438     fp = fopen(file, mode);
   439     if (fp == NULL) {
   440         SDL_SetError("Couldn't open %s", file);
   441     } else {
   442         rwops = SDL_RWFromFP(fp, 1);
   443     }
   444 #else
   445     SDL_SetError("SDL not compiled with stdio support");
   446 #endif /* !HAVE_STDIO_H */
   447 
   448     return (rwops);
   449 }
   450 
   451 #ifdef HAVE_STDIO_H
   452 SDL_RWops *
   453 SDL_RWFromFP(FILE * fp, int autoclose)
   454 {
   455     SDL_RWops *rwops = NULL;
   456 
   457     rwops = SDL_AllocRW();
   458     if (rwops != NULL) {
   459         rwops->seek = stdio_seek;
   460         rwops->read = stdio_read;
   461         rwops->write = stdio_write;
   462         rwops->close = stdio_close;
   463         rwops->hidden.stdio.fp = fp;
   464         rwops->hidden.stdio.autoclose = autoclose;
   465     }
   466     return (rwops);
   467 }
   468 #endif /* HAVE_STDIO_H */
   469 
   470 SDL_RWops *
   471 SDL_RWFromMem(void *mem, int size)
   472 {
   473     SDL_RWops *rwops;
   474 
   475     rwops = SDL_AllocRW();
   476     if (rwops != NULL) {
   477         rwops->seek = mem_seek;
   478         rwops->read = mem_read;
   479         rwops->write = mem_write;
   480         rwops->close = mem_close;
   481         rwops->hidden.mem.base = (Uint8 *) mem;
   482         rwops->hidden.mem.here = rwops->hidden.mem.base;
   483         rwops->hidden.mem.stop = rwops->hidden.mem.base + size;
   484     }
   485     return (rwops);
   486 }
   487 
   488 SDL_RWops *
   489 SDL_RWFromConstMem(const void *mem, int size)
   490 {
   491     SDL_RWops *rwops;
   492 
   493     rwops = SDL_AllocRW();
   494     if (rwops != NULL) {
   495         rwops->seek = mem_seek;
   496         rwops->read = mem_read;
   497         rwops->write = mem_writeconst;
   498         rwops->close = mem_close;
   499         rwops->hidden.mem.base = (Uint8 *) mem;
   500         rwops->hidden.mem.here = rwops->hidden.mem.base;
   501         rwops->hidden.mem.stop = rwops->hidden.mem.base + size;
   502     }
   503     return (rwops);
   504 }
   505 
   506 SDL_RWops *
   507 SDL_AllocRW(void)
   508 {
   509     SDL_RWops *area;
   510 
   511     area = (SDL_RWops *) SDL_malloc(sizeof *area);
   512     if (area == NULL) {
   513         SDL_OutOfMemory();
   514     }
   515     return (area);
   516 }
   517 
   518 void
   519 SDL_FreeRW(SDL_RWops * area)
   520 {
   521     SDL_free(area);
   522 }
   523 
   524 /* Functions for dynamically reading and writing endian-specific values */
   525 
   526 Uint16
   527 SDL_ReadLE16(SDL_RWops * src)
   528 {
   529     Uint16 value;
   530 
   531     SDL_RWread(src, &value, (sizeof value), 1);
   532     return (SDL_SwapLE16(value));
   533 }
   534 
   535 Uint16
   536 SDL_ReadBE16(SDL_RWops * src)
   537 {
   538     Uint16 value;
   539 
   540     SDL_RWread(src, &value, (sizeof value), 1);
   541     return (SDL_SwapBE16(value));
   542 }
   543 
   544 Uint32
   545 SDL_ReadLE32(SDL_RWops * src)
   546 {
   547     Uint32 value;
   548 
   549     SDL_RWread(src, &value, (sizeof value), 1);
   550     return (SDL_SwapLE32(value));
   551 }
   552 
   553 Uint32
   554 SDL_ReadBE32(SDL_RWops * src)
   555 {
   556     Uint32 value;
   557 
   558     SDL_RWread(src, &value, (sizeof value), 1);
   559     return (SDL_SwapBE32(value));
   560 }
   561 
   562 Uint64
   563 SDL_ReadLE64(SDL_RWops * src)
   564 {
   565     Uint64 value;
   566 
   567     SDL_RWread(src, &value, (sizeof value), 1);
   568     return (SDL_SwapLE64(value));
   569 }
   570 
   571 Uint64
   572 SDL_ReadBE64(SDL_RWops * src)
   573 {
   574     Uint64 value;
   575 
   576     SDL_RWread(src, &value, (sizeof value), 1);
   577     return (SDL_SwapBE64(value));
   578 }
   579 
   580 int
   581 SDL_WriteLE16(SDL_RWops * dst, Uint16 value)
   582 {
   583     value = SDL_SwapLE16(value);
   584     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   585 }
   586 
   587 int
   588 SDL_WriteBE16(SDL_RWops * dst, Uint16 value)
   589 {
   590     value = SDL_SwapBE16(value);
   591     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   592 }
   593 
   594 int
   595 SDL_WriteLE32(SDL_RWops * dst, Uint32 value)
   596 {
   597     value = SDL_SwapLE32(value);
   598     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   599 }
   600 
   601 int
   602 SDL_WriteBE32(SDL_RWops * dst, Uint32 value)
   603 {
   604     value = SDL_SwapBE32(value);
   605     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   606 }
   607 
   608 int
   609 SDL_WriteLE64(SDL_RWops * dst, Uint64 value)
   610 {
   611     value = SDL_SwapLE64(value);
   612     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   613 }
   614 
   615 int
   616 SDL_WriteBE64(SDL_RWops * dst, Uint64 value)
   617 {
   618     value = SDL_SwapBE64(value);
   619     return (SDL_RWwrite(dst, &value, (sizeof value), 1));
   620 }
   621 
   622 /* vi: set ts=4 sw=4 expandtab: */