/* SDL - Simple DirectMedia Layer Copyright (C) 1997-2006 Sam Lantinga This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Sam Lantinga slouken@libsdl.org */ #include "SDL_config.h" /* This file provides a general interface for SDL to read and write data sources. It can easily be extended to files, memory, etc. */ #include "SDL_endian.h" #include "SDL_rwops.h" #if defined(__WIN32__) /* Functions to read/write Win32 API file pointers */ /* Will not use it on WinCE because stdio is buffered, it means faster, and all stdio functions anyway are embedded in coredll.dll - the main wince dll*/ #define WINDOWS_LEAN_AND_MEAN #include #ifndef INVALID_SET_FILE_POINTER #define INVALID_SET_FILE_POINTER 0xFFFFFFFF #endif static int SDLCALL win32_file_open(SDL_RWops * context, const char *filename, const char *mode) { #ifndef _WIN32_WCE UINT old_error_mode; #endif HANDLE h; DWORD r_right, w_right; DWORD must_exist, truncate; int a_mode; if (!context) return -1; context->hidden.win32io.h = INVALID_HANDLE_VALUE; /* mark this as unusable */ /* "r" = reading, file must exist */ /* "w" = writing, truncate existing, file may not exist */ /* "r+"= reading or writing, file must exist */ /* "a" = writing, append file may not exist */ /* "a+"= append + read, file may not exist */ /* "w+" = read, write, truncate. file may not exist */ must_exist = (SDL_strchr(mode, 'r') != NULL) ? OPEN_EXISTING : 0; truncate = (SDL_strchr(mode, 'w') != NULL) ? CREATE_ALWAYS : 0; r_right = (SDL_strchr(mode, '+') != NULL || must_exist) ? GENERIC_READ : 0; a_mode = (SDL_strchr(mode, 'a') != NULL) ? OPEN_ALWAYS : 0; w_right = (a_mode || SDL_strchr(mode, '+') || truncate) ? GENERIC_WRITE : 0; if (!r_right && !w_right) /* inconsistent mode */ return -1; /* failed (invalid call) */ #ifdef _WIN32_WCE { size_t size = SDL_strlen(filename) + 1; wchar_t *filenameW = SDL_stack_alloc(wchar_t, size); if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, size) == 0) { SDL_SetError("Unable to convert filename to Unicode"); SDL_stack_free(filenameW); return -1; } h = CreateFile(filenameW, (w_right | r_right), (w_right) ? 0 : FILE_SHARE_READ, NULL, (must_exist | truncate | a_mode), FILE_ATTRIBUTE_NORMAL, NULL); SDL_stack_free(filenameW); } #else /* Do not open a dialog box if failure */ old_error_mode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); h = CreateFile(filename, (w_right | r_right), (w_right) ? 0 : FILE_SHARE_READ, NULL, (must_exist | truncate | a_mode), FILE_ATTRIBUTE_NORMAL, NULL); /* restore old behaviour */ SetErrorMode(old_error_mode); #endif /* _WIN32_WCE */ if (h == INVALID_HANDLE_VALUE) { SDL_SetError("Couldn't open %s", filename); return -2; /* failed (CreateFile) */ } context->hidden.win32io.h = h; context->hidden.win32io.append = a_mode; return 0; /* ok */ } static int SDLCALL win32_file_seek(SDL_RWops * context, int offset, int whence) { DWORD win32whence; int file_pos; if (!context || context->hidden.win32io.h == INVALID_HANDLE_VALUE) { SDL_SetError("win32_file_seek: invalid context/file not opened"); return -1; } switch (whence) { case RW_SEEK_SET: win32whence = FILE_BEGIN; break; case RW_SEEK_CUR: win32whence = FILE_CURRENT; break; case RW_SEEK_END: win32whence = FILE_END; break; default: SDL_SetError("win32_file_seek: Unknown value for 'whence'"); return -1; } file_pos = SetFilePointer(context->hidden.win32io.h, offset, NULL, win32whence); if (file_pos != INVALID_SET_FILE_POINTER) return file_pos; /* success */ SDL_Error(SDL_EFSEEK); return -1; /* error */ } static int SDLCALL win32_file_read(SDL_RWops * context, void *ptr, int size, int maxnum) { int total_bytes; DWORD byte_read, nread; total_bytes = size * maxnum; if (!context || context->hidden.win32io.h == INVALID_HANDLE_VALUE || total_bytes <= 0 || !size) return 0; if (!ReadFile (context->hidden.win32io.h, ptr, total_bytes, &byte_read, NULL)) { SDL_Error(SDL_EFREAD); return 0; } nread = byte_read / size; return nread; } static int SDLCALL win32_file_write(SDL_RWops * context, const void *ptr, int size, int num) { int total_bytes; DWORD byte_written, nwritten; total_bytes = size * num; if (!context || context->hidden.win32io.h == INVALID_HANDLE_VALUE || total_bytes <= 0 || !size) return 0; /* if in append mode, we must go to the EOF before write */ if (context->hidden.win32io.append) { if (SetFilePointer(context->hidden.win32io.h, 0L, NULL, FILE_END) == INVALID_SET_FILE_POINTER) { SDL_Error(SDL_EFWRITE); return 0; } } if (!WriteFile (context->hidden.win32io.h, ptr, total_bytes, &byte_written, NULL)) { SDL_Error(SDL_EFWRITE); return 0; } nwritten = byte_written / size; return nwritten; } static int SDLCALL win32_file_close(SDL_RWops * context) { if (context) { if (context->hidden.win32io.h != INVALID_HANDLE_VALUE) { CloseHandle(context->hidden.win32io.h); context->hidden.win32io.h = INVALID_HANDLE_VALUE; /* to be sure */ } SDL_FreeRW(context); } return (0); } #endif /* __WIN32__ */ #ifdef HAVE_STDIO_H /* Functions to read/write stdio file pointers */ static int SDLCALL stdio_seek(SDL_RWops * context, int offset, int whence) { if (fseek(context->hidden.stdio.fp, offset, whence) == 0) { return (ftell(context->hidden.stdio.fp)); } else { SDL_Error(SDL_EFSEEK); return (-1); } } static int SDLCALL stdio_read(SDL_RWops * context, void *ptr, int size, int maxnum) { size_t nread; nread = fread(ptr, size, maxnum, context->hidden.stdio.fp); if (nread == 0 && ferror(context->hidden.stdio.fp)) { SDL_Error(SDL_EFREAD); } return (nread); } static int SDLCALL stdio_write(SDL_RWops * context, const void *ptr, int size, int num) { size_t nwrote; nwrote = fwrite(ptr, size, num, context->hidden.stdio.fp); if (nwrote == 0 && ferror(context->hidden.stdio.fp)) { SDL_Error(SDL_EFWRITE); } return (nwrote); } static int SDLCALL stdio_close(SDL_RWops * context) { if (context) { if (context->hidden.stdio.autoclose) { /* WARNING: Check the return value here! */ fclose(context->hidden.stdio.fp); } SDL_FreeRW(context); } return (0); } #endif /* !HAVE_STDIO_H */ /* Functions to read/write memory pointers */ static int SDLCALL mem_seek(SDL_RWops * context, int offset, int whence) { Uint8 *newpos; switch (whence) { case RW_SEEK_SET: newpos = context->hidden.mem.base + offset; break; case RW_SEEK_CUR: newpos = context->hidden.mem.here + offset; break; case RW_SEEK_END: newpos = context->hidden.mem.stop + offset; break; default: SDL_SetError("Unknown value for 'whence'"); return (-1); } if (newpos < context->hidden.mem.base) { newpos = context->hidden.mem.base; } if (newpos > context->hidden.mem.stop) { newpos = context->hidden.mem.stop; } context->hidden.mem.here = newpos; return (context->hidden.mem.here - context->hidden.mem.base); } static int SDLCALL mem_read(SDL_RWops * context, void *ptr, int size, int maxnum) { size_t total_bytes; size_t mem_available; total_bytes = (maxnum * size); if ((maxnum <= 0) || (size <= 0) || ((total_bytes / maxnum) != (size_t) size)) { return 0; } mem_available = (context->hidden.mem.stop - context->hidden.mem.here); if (total_bytes > mem_available) { total_bytes = mem_available; } SDL_memcpy(ptr, context->hidden.mem.here, total_bytes); context->hidden.mem.here += total_bytes; return (total_bytes / size); } static int SDLCALL mem_write(SDL_RWops * context, const void *ptr, int size, int num) { if ((context->hidden.mem.here + (num * size)) > context->hidden.mem.stop) { num = (context->hidden.mem.stop - context->hidden.mem.here) / size; } SDL_memcpy(context->hidden.mem.here, ptr, num * size); context->hidden.mem.here += num * size; return (num); } static int SDLCALL mem_writeconst(SDL_RWops * context, const void *ptr, int size, int num) { SDL_SetError("Can't write to read-only memory"); return (-1); } static int SDLCALL mem_close(SDL_RWops * context) { if (context) { SDL_FreeRW(context); } return (0); } /* Functions to create SDL_RWops structures from various data sources */ #ifdef __MACOS__ /* * translate unix-style slash-separated filename to mac-style colon-separated * name; return malloced string */ static char * unix_to_mac(const char *file) { int flen = SDL_strlen(file); char *path = SDL_malloc(flen + 2); const char *src = file; char *dst = path; if (*src == '/') { /* really depends on filesystem layout, hope for the best */ src++; } else { /* Check if this is a MacOS path to begin with */ if (*src != ':') *dst++ = ':'; /* relative paths begin with ':' */ } while (src < file + flen) { const char *end = SDL_strchr(src, '/'); int len; if (!end) end = file + flen; /* last component */ len = end - src; if (len == 0 || (len == 1 && src[0] == '.')) { /* remove repeated slashes and . */ } else { if (len == 2 && src[0] == '.' && src[1] == '.') { /* replace .. with the empty string */ } else { SDL_memcpy(dst, src, len); dst += len; } if (end < file + flen) *dst++ = ':'; } src = end + 1; } *dst++ = '\0'; return path; } #endif /* __MACOS__ */ SDL_RWops * SDL_RWFromFile(const char *file, const char *mode) { SDL_RWops *rwops = NULL; #ifdef HAVE_STDIO_H FILE *fp = NULL; #endif if (!file || !*file || !mode || !*mode) { SDL_SetError("SDL_RWFromFile(): No file or no mode specified"); return NULL; } #if defined(__WIN32__) rwops = SDL_AllocRW(); if (!rwops) return NULL; /* SDL_SetError already setup by SDL_AllocRW() */ rwops->hidden.win32io.h = INVALID_HANDLE_VALUE; if (win32_file_open(rwops, file, mode)) { SDL_FreeRW(rwops); return NULL; } rwops->seek = win32_file_seek; rwops->read = win32_file_read; rwops->write = win32_file_write; rwops->close = win32_file_close; #elif HAVE_STDIO_H #ifdef __MACOS__ { char *mpath = unix_to_mac(file); fp = fopen(mpath, mode); SDL_free(mpath); } #else fp = fopen(file, mode); #endif if (fp == NULL) { SDL_SetError("Couldn't open %s", file); } else { rwops = SDL_RWFromFP(fp, 1); } #else SDL_SetError("SDL not compiled with stdio support"); #endif /* !HAVE_STDIO_H */ return (rwops); } #ifdef HAVE_STDIO_H SDL_RWops * SDL_RWFromFP(FILE * fp, int autoclose) { SDL_RWops *rwops = NULL; rwops = SDL_AllocRW(); if (rwops != NULL) { rwops->seek = stdio_seek; rwops->read = stdio_read; rwops->write = stdio_write; rwops->close = stdio_close; rwops->hidden.stdio.fp = fp; rwops->hidden.stdio.autoclose = autoclose; } return (rwops); } #endif /* HAVE_STDIO_H */ SDL_RWops * SDL_RWFromMem(void *mem, int size) { SDL_RWops *rwops; rwops = SDL_AllocRW(); if (rwops != NULL) { rwops->seek = mem_seek; rwops->read = mem_read; rwops->write = mem_write; rwops->close = mem_close; rwops->hidden.mem.base = (Uint8 *) mem; rwops->hidden.mem.here = rwops->hidden.mem.base; rwops->hidden.mem.stop = rwops->hidden.mem.base + size; } return (rwops); } SDL_RWops * SDL_RWFromConstMem(const void *mem, int size) { SDL_RWops *rwops; rwops = SDL_AllocRW(); if (rwops != NULL) { rwops->seek = mem_seek; rwops->read = mem_read; rwops->write = mem_writeconst; rwops->close = mem_close; rwops->hidden.mem.base = (Uint8 *) mem; rwops->hidden.mem.here = rwops->hidden.mem.base; rwops->hidden.mem.stop = rwops->hidden.mem.base + size; } return (rwops); } SDL_RWops * SDL_AllocRW(void) { SDL_RWops *area; area = (SDL_RWops *) SDL_malloc(sizeof *area); if (area == NULL) { SDL_OutOfMemory(); } return (area); } void SDL_FreeRW(SDL_RWops * area) { SDL_free(area); } /* Functions for dynamically reading and writing endian-specific values */ Uint16 SDL_ReadLE16(SDL_RWops * src) { Uint16 value; SDL_RWread(src, &value, (sizeof value), 1); return (SDL_SwapLE16(value)); } Uint16 SDL_ReadBE16(SDL_RWops * src) { Uint16 value; SDL_RWread(src, &value, (sizeof value), 1); return (SDL_SwapBE16(value)); } Uint32 SDL_ReadLE32(SDL_RWops * src) { Uint32 value; SDL_RWread(src, &value, (sizeof value), 1); return (SDL_SwapLE32(value)); } Uint32 SDL_ReadBE32(SDL_RWops * src) { Uint32 value; SDL_RWread(src, &value, (sizeof value), 1); return (SDL_SwapBE32(value)); } Uint64 SDL_ReadLE64(SDL_RWops * src) { Uint64 value; SDL_RWread(src, &value, (sizeof value), 1); return (SDL_SwapLE64(value)); } Uint64 SDL_ReadBE64(SDL_RWops * src) { Uint64 value; SDL_RWread(src, &value, (sizeof value), 1); return (SDL_SwapBE64(value)); } int SDL_WriteLE16(SDL_RWops * dst, Uint16 value) { value = SDL_SwapLE16(value); return (SDL_RWwrite(dst, &value, (sizeof value), 1)); } int SDL_WriteBE16(SDL_RWops * dst, Uint16 value) { value = SDL_SwapBE16(value); return (SDL_RWwrite(dst, &value, (sizeof value), 1)); } int SDL_WriteLE32(SDL_RWops * dst, Uint32 value) { value = SDL_SwapLE32(value); return (SDL_RWwrite(dst, &value, (sizeof value), 1)); } int SDL_WriteBE32(SDL_RWops * dst, Uint32 value) { value = SDL_SwapBE32(value); return (SDL_RWwrite(dst, &value, (sizeof value), 1)); } int SDL_WriteLE64(SDL_RWops * dst, Uint64 value) { value = SDL_SwapLE64(value); return (SDL_RWwrite(dst, &value, (sizeof value), 1)); } int SDL_WriteBE64(SDL_RWops * dst, Uint64 value) { value = SDL_SwapBE64(value); return (SDL_RWwrite(dst, &value, (sizeof value), 1)); } /* vi: set ts=4 sw=4 expandtab: */