Date: Tue, 24 May 2005 00:43:05 -0400 From: Antonio SJ Musumeci To: sdl@libsdl.org Subject: [SDL] SDL_RWops bug fixes Index: include/SDL_rwops.h =================================================================== RCS file: /home/sdlweb/libsdl.org/cvs/SDL12/include/SDL_rwops.h,v retrieving revision 1.8 diff -u -r1.8 SDL_rwops.h --- include/SDL_rwops.h 20 Aug 2004 18:57:01 -0000 1.8 +++ include/SDL_rwops.h 19 Apr 2005 17:24:43 -0000 @@ -42,6 +42,9 @@ extern "C" { #endif +#define SDL_RWOPS_EOF 1<<0 +#define SDL_RWOPS_ERROR 1<<1 + /* This is the read/write operation structure -- very basic */ typedef struct SDL_RWops { @@ -49,24 +52,33 @@ SEEK_SET, SEEK_CUR, SEEK_END Returns the final offset in the data source. */ - int (SDLCALL *seek)(struct SDL_RWops *context, int offset, int whence); + long (SDLCALL *seek)(struct SDL_RWops *context, long offset, int whence); - /* Read up to 'num' objects each of size 'objsize' from the data + /* Read up to 'num' objects each of size 'size' from the data source to the area pointed at by 'ptr'. - Returns the number of objects read, or -1 if the read failed. + Returns the number of objects successfully read, or -1 if the read failed. */ - int (SDLCALL *read)(struct SDL_RWops *context, void *ptr, int size, int maxnum); + size_t (SDLCALL *read)(struct SDL_RWops *context, void *ptr, size_t size, size_t num); - /* Write exactly 'num' objects each of size 'objsize' from the area + /* Write exactly 'num' objects each of size 'size' from the area pointed at by 'ptr' to data source. - Returns 'num', or -1 if the write failed. + Returns 'num' of objects successfully written, or -1 if the write failed. */ - int (SDLCALL *write)(struct SDL_RWops *context, const void *ptr, int size, int num); + size_t (SDLCALL *write)(struct SDL_RWops *context, const void *ptr, size_t size, size_t num); /* Close and free an allocated SDL_FSops structure */ int (SDLCALL *close)(struct SDL_RWops *context); - Uint32 type; + /* return whether we are at the end of the file/buffer */ + int (SDLCALL *eof)(struct SDL_RWops *context); + + /* return whether we received an error */ + int (SDLCALL *error)(struct SDL_RWops *context); + + /* clear eof and error */ + void (SDLCALL *clearerr)(struct SDL_RWops *context); + + Uint32 flags; union { struct { int autoclose; @@ -77,9 +89,6 @@ Uint8 *here; Uint8 *stop; } mem; - struct { - void *data1; - } unknown; } hidden; } SDL_RWops; @@ -91,8 +100,9 @@ extern DECLSPEC SDL_RWops * SDLCALL SDL_RWFromFP(FILE *fp, int autoclose); -extern DECLSPEC SDL_RWops * SDLCALL SDL_RWFromMem(void *mem, int size); -extern DECLSPEC SDL_RWops * SDLCALL SDL_RWFromConstMem(const void *mem, int size); +extern DECLSPEC SDL_RWops * SDLCALL SDL_RWFromMem(void *mem, size_t size); +extern DECLSPEC SDL_RWops * SDLCALL SDL_RWFromConstMem(const void *mem, size_t size); +extern DECLSPEC SDL_RWops * SDLCALL SDL_RWFromMallocedMem(void *mem, size_t size); extern DECLSPEC SDL_RWops * SDLCALL SDL_AllocRW(void); extern DECLSPEC void SDLCALL SDL_FreeRW(SDL_RWops *area); @@ -103,7 +113,9 @@ #define SDL_RWread(ctx, ptr, size, n) (ctx)->read(ctx, ptr, size, n) #define SDL_RWwrite(ctx, ptr, size, n) (ctx)->write(ctx, ptr, size, n) #define SDL_RWclose(ctx) (ctx)->close(ctx) - +#define SDL_RWeof(ctx) (ctx)->eof(ctx) +#define SDL_RWerror(ctx) (ctx)->error(ctx) +#define SDL_RWclearerr(ctx) (ctx)->clearerr(ctx) /* Ends C function definitions when using C++ */ #ifdef __cplusplus Index: src/file/SDL_rwops.c =================================================================== RCS file: /home/sdlweb/libsdl.org/cvs/SDL12/src/file/SDL_rwops.c,v retrieving revision 1.8 diff -u -r1.8 SDL_rwops.c --- src/file/SDL_rwops.c 4 Jan 2004 16:49:17 -0000 1.8 +++ src/file/SDL_rwops.c 19 Apr 2005 17:24:43 -0000 @@ -38,7 +38,7 @@ /* Functions to read/write stdio file pointers */ -static int stdio_seek(SDL_RWops *context, int offset, int whence) +static long stdio_seek(SDL_RWops *context, long offset, int whence) { if ( fseek(context->hidden.stdio.fp, offset, whence) == 0 ) { return(ftell(context->hidden.stdio.fp)); @@ -47,17 +47,19 @@ return(-1); } } -static int stdio_read(SDL_RWops *context, void *ptr, int size, int maxnum) + +static size_t stdio_read(SDL_RWops *context, void *ptr, size_t size, size_t num) { size_t nread; - nread = fread(ptr, size, maxnum, context->hidden.stdio.fp); + nread = fread(ptr, size, num, context->hidden.stdio.fp); if ( nread == 0 && ferror(context->hidden.stdio.fp) ) { SDL_Error(SDL_EFREAD); } return(nread); } -static int stdio_write(SDL_RWops *context, const void *ptr, int size, int num) + +static size_t stdio_write(SDL_RWops *context, const void *ptr, size_t size, size_t num) { size_t nwrote; @@ -67,21 +69,40 @@ } return(nwrote); } + static int stdio_close(SDL_RWops *context) { + int rv; + + rv = 0; if ( context ) { if ( context->hidden.stdio.autoclose ) { /* WARNING: Check the return value here! */ - fclose(context->hidden.stdio.fp); + rv = fclose(context->hidden.stdio.fp); } - free(context); + SDL_FreeRW(context); } - return(0); + return rv; +} + +static int stdio_eof(SDL_RWops *context) +{ + return feof(context->hidden.stdio.fp); +} + +static int stdio_error(SDL_RWops *context) +{ + return ferror(context->hidden.stdio.fp); +} + +static void stdio_clearerr(SDL_RWops *context) +{ + clearerr(context->hidden.stdio.fp); } /* Functions to read/write memory pointers */ -static int mem_seek(SDL_RWops *context, int offset, int whence) +static long mem_seek(SDL_RWops *context, long offset, int whence) { Uint8 *newpos; @@ -96,6 +117,7 @@ newpos = context->hidden.mem.stop+offset; break; default: + context->flags |= SDL_RWOPS_ERROR; SDL_SetError("Unknown value for 'whence'"); return(-1); } @@ -108,40 +130,209 @@ context->hidden.mem.here = newpos; return(context->hidden.mem.here-context->hidden.mem.base); } -static int mem_read(SDL_RWops *context, void *ptr, int size, int maxnum) + +static size_t mem_read(SDL_RWops *context, void *ptr, size_t size, size_t num) { - int num; + size_t total_bytes; + size_t mem_available; - num = maxnum; - if ( (context->hidden.mem.here + (num*size)) > context->hidden.mem.stop ) { - num = (context->hidden.mem.stop-context->hidden.mem.here)/size; - } - memcpy(ptr, context->hidden.mem.here, num*size); - context->hidden.mem.here += num*size; - return(num); + total_bytes = (num * size); + if((num == 0) || + (size == 0) || + ((total_bytes/num) != size)) + return 0; + + mem_available = (context->hidden.mem.stop - context->hidden.mem.here); + if(total_bytes > mem_available) + { + total_bytes = mem_available; + context->flags |= SDL_RWOPS_EOF; + } + + memcpy(ptr, context->hidden.mem.here, total_bytes); + context->hidden.mem.here += total_bytes; + + return (total_bytes / size); } -static int mem_write(SDL_RWops *context, const void *ptr, int size, int num) + +static size_t mem_write(SDL_RWops *context, const void *ptr, size_t size, size_t num) { - if ( (context->hidden.mem.here + (num*size)) > context->hidden.mem.stop ) { - num = (context->hidden.mem.stop-context->hidden.mem.here)/size; - } - memcpy(context->hidden.mem.here, ptr, num*size); - context->hidden.mem.here += num*size; - return(num); + size_t total_bytes; + size_t mem_available; + + total_bytes = (num * size); + if((num == 0) || + (size == 0) || + ((total_bytes/num) != size)) + return 0; + + mem_available = (context->hidden.mem.stop - context->hidden.mem.here); + if(total_bytes > mem_available) + { + total_bytes = mem_available; + context->flags |= SDL_RWOPS_EOF; + } + + memcpy(context->hidden.mem.here, ptr, total_bytes); + context->hidden.mem.here += total_bytes; + + return (total_bytes / size); } -static int mem_writeconst(SDL_RWops *context, const void *ptr, int size, int num) + +static size_t mem_writeconst(SDL_RWops *context, const void *ptr, size_t size, size_t num) { - SDL_SetError("Can't write to read-only memory"); - return(-1); + SDL_SetError("Can't write to read-only memory"); + context->flags |= SDL_RWOPS_ERROR; + + return 0; } + static int mem_close(SDL_RWops *context) { if ( context ) { - free(context); + SDL_FreeRW(context); } return(0); } +static int mem_eof(SDL_RWops *context) +{ + return (context->flags & SDL_RWOPS_EOF); +} + +static int mem_error(SDL_RWops *context) +{ + return (context->flags & SDL_RWOPS_ERROR); +} + +static void mem_clearerr(SDL_RWops *context) +{ + context->flags &= ~(SDL_RWOPS_EOF|SDL_RWOPS_ERROR); +} + +/* Functions to read/write malloced memory pointers */ +static long mallocmem_seek(SDL_RWops* context, long offset, int whence) +{ + Uint8* newpos; + + switch(whence) + { + case SEEK_SET: + newpos = (context->hidden.mem.base + offset); + break; + + case SEEK_CUR: + newpos = (context->hidden.mem.here + offset); + break; + + case SEEK_END: + newpos = (context->hidden.mem.stop + offset); + break; + + default: + context->flags |= SDL_RWOPS_ERROR; + SDL_SetError("Unknown value for 'whence'"); + return -1; + } + + if(newpos < context->hidden.mem.base) + { + newpos = context->hidden.mem.base; + } + + context->hidden.mem.here = newpos; + + return (context->hidden.mem.here - context->hidden.mem.base); +} + +static size_t mallocmem_read(SDL_RWops *context, void *ptr, size_t size, size_t num) +{ + size_t total_bytes;; + size_t mem_available; + + total_bytes = (num * size); + if((num == 0) || + (size == 0) || + ((total_bytes/num) != size)) + return 0; + + if(context->hidden.mem.here > context->hidden.mem.stop) + { + context->flags |= SDL_RWOPS_EOF; + total_bytes = 0; + } + else + { + mem_available = (context->hidden.mem.stop - context->hidden.mem.here); + if(total_bytes > mem_available) + { + total_bytes = mem_available; + context->flags |= SDL_RWOPS_EOF; + } + + memcpy(ptr, context->hidden.mem.here, total_bytes); + context->hidden.mem.here += total_bytes; + } + + return (total_bytes / size); +} + +static size_t mallocmem_write(SDL_RWops *context, const void *ptr, size_t size, size_t num) +{ + size_t new_buf_size; + size_t total_bytes; + size_t current_pos; + size_t amount_to_clear; + Uint8* end_pos; + Uint8* new_mem; + + total_bytes = (size * num); + if((num == 0) || + (size == 0) || + ((total_bytes/num) != size)) + return 0; + + current_pos = (context->hidden.mem.here - context->hidden.mem.base); + end_pos = (context->hidden.mem.here + total_bytes); + + if(end_pos > context->hidden.mem.stop) + { + new_buf_size = (end_pos - context->hidden.mem.base); + new_mem = realloc(context->hidden.mem.base, new_buf_size); + if(new_mem != NULL) + { + context->hidden.mem.base = new_mem; + context->hidden.mem.here = (context->hidden.mem.base + current_pos); + context->hidden.mem.stop = (context->hidden.mem.base + new_buf_size);; + + amount_to_clear = ((context->hidden.mem.stop - context->hidden.mem.here) - total_bytes); + memset(context->hidden.mem.here, '\0', amount_to_clear); + } + else + { + context->flags |= SDL_RWOPS_ERROR; + SDL_SetError("realloc of buffer failed"); + return 0; + } + } + + memcpy(context->hidden.mem.here, ptr, total_bytes); + context->hidden.mem.here += total_bytes; + + return num; +} + +static int mallocmem_close(SDL_RWops *context) +{ + if(context) + { + free(context->hidden.mem.base); + SDL_FreeRW(context); + } + + return 0; +} + /* Functions to create SDL_RWops structures from various data sources */ #ifdef WIN32 /* Aggh. You can't (apparently) open a file in an application and @@ -157,10 +348,25 @@ */ static char *unix_to_mac(const char *file) { - int flen = strlen(file); - char *path = malloc(flen + 2); + int flen; + char *dst; + char *path; const char *src = file; - char *dst = path; + + if(file == NULL) + return NULL; + + flen = strlen(file); + path = malloc(flen + 2); + + if(path == NULL) + { + SDL_OutOfMemory(); + return NULL; + } + else + dst = path; + if(*src == '/') { /* really depends on filesystem layout, hope for the best */ src++; @@ -236,50 +442,96 @@ #endif rwops = SDL_AllocRW(); if ( rwops != NULL ) { - rwops->seek = stdio_seek; - rwops->read = stdio_read; - rwops->write = stdio_write; - rwops->close = stdio_close; + rwops->seek = stdio_seek; + rwops->read = stdio_read; + rwops->write = stdio_write; + rwops->close = stdio_close; + rwops->eof = stdio_eof; + rwops->error = stdio_error; + rwops->clearerr = stdio_clearerr; + rwops->flags = 0; rwops->hidden.stdio.fp = fp; rwops->hidden.stdio.autoclose = autoclose; } return(rwops); } -SDL_RWops *SDL_RWFromMem(void *mem, int size) +SDL_RWops *SDL_RWFromMem(void *mem, size_t 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->seek = mem_seek; + rwops->read = mem_read; + rwops->write = mem_write; + rwops->close = mem_close; + rwops->eof = mem_eof; + rwops->error = mem_error; + rwops->clearerr = mem_clearerr; + rwops->flags = 0; + 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 *SDL_RWFromConstMem(const void *mem, size_t 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->seek = mem_seek; + rwops->read = mem_read; + rwops->write = mem_writeconst; + rwops->close = mem_close; + rwops->eof = mem_eof; + rwops->error = mem_error; + rwops->clearerr = mem_clearerr; + rwops->flags = 0; + 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_RWFromMallocedMem(void *mem, size_t size) +{ + SDL_RWops* rwops; + + if(mem == NULL) + mem = (Uint8*)malloc(size); + + rwops = NULL; + if(mem != NULL) + { + rwops = SDL_AllocRW(); + if(rwops != NULL) + { + rwops->seek = mallocmem_seek; + rwops->read = mallocmem_read; + rwops->write = mallocmem_write; + rwops->close = mallocmem_close; + rwops->eof = mem_eof; + rwops->error = mem_error; + rwops->clearerr = mem_clearerr; + rwops->flags = 0; + rwops->hidden.mem.base = (Uint8*)mem; + rwops->hidden.mem.here = rwops->hidden.mem.base; + rwops->hidden.mem.stop = (rwops->hidden.mem.base + size); + } + } + else + { + SDL_OutOfMemory(); + } + + return rwops; +} + SDL_RWops *SDL_AllocRW(void) { SDL_RWops *area;