diff --git a/Makefile b/Makefile index a59b955f..9490a6d7 100644 --- a/Makefile +++ b/Makefile @@ -50,9 +50,9 @@ ${MAINDIR}/hq2x${OE} ${MAINDIR}/GBA-thumb${OE} ${MAINDIR}/GBA-arm${OE} ${MAINDIR ${MAINDIR}/Mode1${OE} ${MAINDIR}/Mode2${OE} ${MAINDIR}/Mode3${OE} ${MAINDIR}/Mode4${OE} \ ${MAINDIR}/Mode5${OE} ${MAINDIR}/motionblur${OE} ${MAINDIR}/pixel${OE} ${MAINDIR}/portable${OE} \ ${MAINDIR}/remote${OE} ${MAINDIR}/RTC${OE} ${MAINDIR}/scanline${OE} ${MAINDIR}/simpleFilter${OE} \ -${MAINDIR}/snd_interp${OE} ${MAINDIR}/Sound${OE} ${MAINDIR}/Sram${OE} ${MAINDIR}/Text${OE} \ +${MAINDIR}/fex_mini${OE} ${MAINDIR}/Sound${OE} ${MAINDIR}/Sram${OE} ${MAINDIR}/Text${OE} \ ${MAINDIR}/unzip${OE} ${MAINDIR}/Util${OE} ${MAINDIR}/exprNode${OE} ${MAINDIR}/getopt${OE} \ -${MAINDIR}/getopt1${OE} ${MAINDIR}/memgzio${OE} ${MAINDIR}/expr-lex${OE} ${MAINDIR}/expr${OE} +${MAINDIR}/getopt1${OE} ${MAINDIR}/memgzio${OE} ${MAINDIR}/expr-lex${OE} ${MAINDIR}/expr${OE} \ DMGOBJ=${DMGDIR}/GB${OE} ${DMGDIR}/gbCheats${OE} ${DMGDIR}/gbDis${OE} ${DMGDIR}/gbGfx${OE} \ ${DMGDIR}/gbGlobals${OE} ${DMGDIR}/gbMemory${OE} ${DMGDIR}/gbPrinter${OE} ${DMGDIR}/gbSGB${OE} \ diff --git a/src/fex.h b/src/fex.h index 1727f7b9..e2bf69b4 100644 --- a/src/fex.h +++ b/src/fex.h @@ -1,140 +1,140 @@ -/* Compressed file archive C interface (also usable from C++) */ - -/* File_Extractor 0.4.3 */ -#ifndef FEX_H -#define FEX_H - -#ifdef __cplusplus - extern "C" { -#endif - -/* Error string returned by library functions, or NULL if no error (success). -If function takes fex_err_t* err_out, it sets *err_out to NULL on success, -otherwise error string, or you can pass NULL if you don't care about exact -cause of error (these functions still report error by returning NULL). */ -typedef const char* fex_err_t; - -/* First parameter of most extractor_ functions is a pointer to the -File_Extractor being acted on. */ -typedef struct File_Extractor File_Extractor; - - -/**** Basics ****/ - -/* Opens archive and returns pointer to it, or NULL if error. */ -File_Extractor* fex_open( const char* path, fex_err_t* err_out ); - -/* True if at end of archive. */ -int fex_done( File_Extractor const* ); - -/* Name of current file. */ -const char* fex_name( File_Extractor const* ); - -/* Size of current file. */ -long fex_size( File_Extractor const* ); - -/* Extracts n bytes and writes them to *out. Returns error if all n -bytes couldn't be extracted (due to end of file or read error). */ -fex_err_t fex_read( File_Extractor*, void* out, long n ); - -/* Goes to next file in archive (skips directories). */ -fex_err_t fex_next( File_Extractor* ); - -/* Closes archive and frees memory. */ -void fex_close( File_Extractor* ); - - -/**** Advanced ****/ - -/* Goes back to first file in archive. */ -fex_err_t fex_rewind( File_Extractor* ); - -/* Hints to fex_next() that no file extraction will occur, speeding scanning -of some archive types. */ -void fex_scan_only( File_Extractor* ); - -/* Modification date of current file (MS-DOS format). */ -unsigned long fex_dos_date( File_Extractor const* ); - -/* Number of bytes remaining to be read from current file. */ -long fex_remain( File_Extractor const* ); - -/* Reads at most n bytes and returns number actually read, or negative if error. */ -long fex_read_avail( File_Extractor*, void* out, long n ); - -/* Extracts first n bytes and ignores rest. Faster than a normal read since it -doesn't need to read any more data. Must not be called twice in a row. */ -fex_err_t fex_read_once( File_Extractor*, void* out, long n ); - -/* Loads file data into memory (if not already) and returns pointer to it, or -NULL if error. Pointer is valid until fex_next(), fex_rewind(), or fex_close() are -called. Will return same pointer if called more than once. */ -const unsigned char* fex_data( File_Extractor*, fex_err_t* err_out ); - - -/**** Archive types ****/ - -/* fex_type_t is a pointer to this structure. For example, fex_zip_type->extension is -"ZIP" and ex_zip_type->new_fex() is equilvant to 'new Zip_Extractor' (in C++). */ -struct fex_type_t_ -{ - const char* extension; /* file extension/type */ - File_Extractor* (*new_fex)(); -}; - -/* Archive type constants for each supported file type */ -extern struct fex_type_t_ const - fex_7z_type [1], /* .7z (7-zip) */ - fex_gz_type [1], /* .gz (gzip) */ - /*fex_rar_type [1],*/ /* .rar */ - fex_zip_type [1], /* .zip */ - fex_bin_type [1]; /* binary file, possibly gzipped */ -typedef struct fex_type_t_ const* fex_type_t; - -/* Array of supported archive types, with NULL entry at end. */ -fex_type_t const* fex_type_list(); - -/* Type of archive this extractor handles. */ -fex_type_t fex_type( File_Extractor const* ); - - -/******** Advanced opening ********/ - -/* Error returned if file is wrong type */ -extern const char fex_wrong_file_type [29]; - -/* Determines likely archive type based on first four bytes of file. Returns string -containing proper file suffix (i.e. "ZIP", "GZ", etc.) or "" (empty string) if file -header is not recognized. */ -const char* fex_identify_header( void const* header ); - -/* Gets corresponding archive type for file path or extension passed in. Returns NULL -if type isn't recognized. */ -fex_type_t fex_identify_extension( const char* path_or_extension ); - -/* Determines file type based on filename extension, or file header (if extension -isn't recognized). Returns NULL if unrecognized or error. */ -fex_type_t fex_identify_file( const char* path, fex_err_t* err_out ); - -/* Opens archive of specific type and returns pointer to it, or NULL if error. */ -File_Extractor* fex_open_type( fex_type_t, const char* path, fex_err_t* err_out ); - - -/******** User data ********/ - -/* Sets/gets pointer to data you want to associate with this extractor. -You can use this for whatever you want. */ -void fex_set_user_data( File_Extractor*, void* new_user_data ); -void* fex_user_data( File_Extractor const* ); - -/* Registers cleanup function to be called when closing extractor, or NULL to -clear it. Passes user_data (see above) to cleanup function. */ -typedef void (*fex_user_cleanup_t)( void* user_data ); -void fex_set_user_cleanup( File_Extractor*, fex_user_cleanup_t func ); - - -#ifdef __cplusplus - } -#endif - -#endif +/* Compressed file archive C interface (also usable from C++) */ + +/* File_Extractor 0.4.3 */ +#ifndef FEX_H +#define FEX_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Error string returned by library functions, or NULL if no error (success). +If function takes fex_err_t* err_out, it sets *err_out to NULL on success, +otherwise error string, or you can pass NULL if you don't care about exact +cause of error (these functions still report error by returning NULL). */ +typedef const char* fex_err_t; + +/* First parameter of most extractor_ functions is a pointer to the +File_Extractor being acted on. */ +typedef struct File_Extractor File_Extractor; + + +/**** Basics ****/ + +/* Opens archive and returns pointer to it, or NULL if error. */ +File_Extractor* fex_open( const char* path, fex_err_t* err_out ); + +/* True if at end of archive. */ +int fex_done( File_Extractor const* ); + +/* Name of current file. */ +const char* fex_name( File_Extractor* ); + +/* Size of current file. */ +long fex_size( File_Extractor const* ); + +/* Extracts n bytes and writes them to *out. Returns error if all n +bytes couldn't be extracted (due to end of file or read error). */ +fex_err_t fex_read( File_Extractor*, void* out, long n ); + +/* Goes to next file in archive (skips directories). */ +fex_err_t fex_next( File_Extractor* ); + +/* Closes archive and frees memory. */ +void fex_close( File_Extractor* ); + + +/**** Advanced ****/ + +/* Goes back to first file in archive. */ +fex_err_t fex_rewind( File_Extractor* ); + +/* Hints to fex_next() that no file extraction will occur, speeding scanning +of some archive types. */ +void fex_scan_only( File_Extractor* ); + +/* Modification date of current file (MS-DOS format). */ +unsigned long fex_dos_date( File_Extractor const* ); + +/* Number of bytes remaining to be read from current file. */ +long fex_remain( File_Extractor const* ); + +/* Reads at most n bytes and returns number actually read, or negative if error. */ +long fex_read_avail( File_Extractor*, void* out, long n ); + +/* Extracts first n bytes and ignores rest. Faster than a normal read since it +doesn't need to read any more data. Must not be called twice in a row. */ +fex_err_t fex_read_once( File_Extractor*, void* out, long n ); + +/* Loads file data into memory (if not already) and returns pointer to it, or +NULL if error. Pointer is valid until fex_next(), fex_rewind(), or fex_close() are +called. Will return same pointer if called more than once. */ +const unsigned char* fex_data( File_Extractor*, fex_err_t* err_out ); + + +/**** Archive types ****/ + +/* fex_type_t is a pointer to this structure. For example, fex_zip_type->extension is +"ZIP" and ex_zip_type->new_fex() is equilvant to 'new Zip_Extractor' (in C++). */ +struct fex_type_t_ +{ + const char* extension; /* file extension/type */ + File_Extractor* (*new_fex)(); +}; + +/* Archive type constants for each supported file type */ +extern struct fex_type_t_ const + fex_7z_type [1], /* .7z (7-zip) */ + fex_gz_type [1], /* .gz (gzip) */ + /*fex_rar_type [1],*/ /* .rar */ + fex_zip_type [1], /* .zip */ + fex_bin_type [1]; /* binary file, possibly gzipped */ +typedef struct fex_type_t_ const* fex_type_t; + +/* Array of supported archive types, with NULL entry at end. */ +fex_type_t const* fex_type_list(); + +/* Type of archive this extractor handles. */ +fex_type_t fex_type( File_Extractor const* ); + + +/******** Advanced opening ********/ + +/* Error returned if file is wrong type */ +extern const char fex_wrong_file_type [29]; + +/* Determines likely archive type based on first four bytes of file. Returns string +containing proper file suffix (i.e. "ZIP", "GZ", etc.) or "" (empty string) if file +header is not recognized. */ +const char* fex_identify_header( void const* header ); + +/* Gets corresponding archive type for file path or extension passed in. Returns NULL +if type isn't recognized. */ +fex_type_t fex_identify_extension( const char* path_or_extension ); + +/* Determines file type based on filename extension, or file header (if extension +isn't recognized). Returns NULL if unrecognized or error. */ +fex_type_t fex_identify_file( const char* path, fex_err_t* err_out ); + +/* Opens archive of specific type and returns pointer to it, or NULL if error. */ +File_Extractor* fex_open_type( fex_type_t, const char* path, fex_err_t* err_out ); + + +/******** User data ********/ + +/* Sets/gets pointer to data you want to associate with this extractor. +You can use this for whatever you want. */ +void fex_set_user_data( File_Extractor*, void* new_user_data ); +void* fex_user_data( File_Extractor const* ); + +/* Registers cleanup function to be called when closing extractor, or NULL to +clear it. Passes user_data (see above) to cleanup function. */ +typedef void (*fex_user_cleanup_t)( void* user_data ); +void fex_set_user_cleanup( File_Extractor*, fex_user_cleanup_t func ); + + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/src/fex_mini.cpp b/src/fex_mini.cpp index df694fe8..f1514c6f 100644 --- a/src/fex_mini.cpp +++ b/src/fex_mini.cpp @@ -1,222 +1,222 @@ -// Minimal implementation of fex.h. Supports gzipped files if you have zlib -// available and HAVE_ZLIB_H is defined. - -// File_Extractor 0.4.3. http://www.slack.net/~ant/ - -#include "fex.h" - -#include -#include -#include - -#ifdef HAVE_CONFIG_H - #include "config.h" -#endif - -/* Copyright (C) 2007 Shay Green. This module 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 -module 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 module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#ifndef HAVE_ZLIB_H - #define FILE_GZ( norm, gz ) norm - #define FILE_READ( ptr, size, file ) fread( ptr, 1, size, file ) -#else - #define FILE_GZ( norm, gz ) gz - #define FILE_READ( ptr, size, file ) gzread( file, ptr, size ) - -#include "zlib.h" - -static const char* get_gzip_size( const char* path, long* eof ) -{ - FILE* file = fopen( path, "rb" ); - if ( !file ) - return "Couldn't open file"; - - unsigned char buf [4]; - if ( fread( buf, 2, 1, file ) > 0 && buf [0] == 0x1F && buf [1] == 0x8B ) - { - fseek( file, -4, SEEK_END ); - fread( buf, 4, 1, file ); - *eof = buf [3] * 0x1000000 + buf [2] * 0x10000 + buf [1] * 0x100 + buf [0]; - } - else - { - fseek( file, 0, SEEK_END ); - *eof = ftell( file ); - } - const char* err = (ferror( file ) || feof( file )) ? "Couldn't get file size" : 0; - fclose( file ); - return err; -} -#endif - -const char fex_wrong_file_type [] = "Archive format not supported"; - -struct File_Extractor -{ - FILE_GZ(FILE*,gzFile) file; - int done; - long size; - void* data; // file data read into memory, ot 0 if not read - void* user_data; - fex_user_cleanup_t user_cleanup; - - char* name() { return (char*) (this + 1); } -}; - -// Always identify as single file extractor -fex_type_t_ const fex_bin_type [1] = {{ "" , 0 }}; -const char* fex_identify_header ( void const* ) { return ""; } -fex_type_t fex_identify_extension( const char* ) { return fex_bin_type; } -fex_type_t fex_identify_file ( const char*, fex_err_t* e ) { if ( e ) *e = 0; return fex_bin_type; } - -static fex_err_t fex_open_( const char* path, File_Extractor** fe_out ) -{ - *fe_out = 0; - - // name - const char* name = strrchr( path, '\\' ); // DOS - if ( !name ) - name = strrchr( path, '/' ); // UNIX - if ( !name ) - name = strrchr( path, ':' ); // Mac - if ( !name ) - name = path; - - // allocate space for struct and name - long name_size = strlen( name ) + 1; - File_Extractor* fe = (File_Extractor*) malloc( sizeof (File_Extractor) + name_size ); - if ( !fe ) return "Out of memory"; - - fe->done = 0; - fe->data = 0; - fe->user_data = 0; - fe->user_cleanup = 0; - memcpy( fe->name(), name, name_size ); - - #ifdef HAVE_ZLIB_H - // get gzip size BEFORE opening file - const char* err = get_gzip_size( path, &fe->size ); - if ( err ) - { - free( fe ); - return err; - } - #endif - - // open file - fe->file = FILE_GZ(fopen,gzopen)( path, "rb" ); - if ( !fe->file ) - { - free( fe ); - return "Couldn't open file"; - } - - // get normal size - #ifndef HAVE_ZLIB_H - fseek( fe->file, 0, SEEK_END ); - fe->size = ftell( fe->file ); - rewind( fe->file ); - #endif - - *fe_out = fe; - return 0; -} - -File_Extractor* fex_open( const char* path, fex_err_t* err_out ) -{ - File_Extractor* fe; - fex_err_t err = fex_open_( path, &fe ); - if ( err_out ) - *err_out = err; - return fe; -} - -File_Extractor* fex_open_type( fex_type_t, const char* path, fex_err_t* err_out ) -{ - return fex_open( path, err_out ); -} - -void* fex_user_data ( File_Extractor const* fe ) { return fe->user_data; } -void fex_set_user_data ( File_Extractor* fe, void* new_user_data ) { fe->user_data = new_user_data; } -void fex_set_user_cleanup ( File_Extractor* fe, fex_user_cleanup_t func ) { fe->user_cleanup = func; } - -fex_type_t fex_type ( File_Extractor const* ) { return fex_bin_type; } -int fex_done ( File_Extractor const* fe ) { return fe->done; } -const char* fex_name ( File_Extractor const* fe ) { return fe->name(); } -unsigned long fex_dos_date ( File_Extractor const* ) { return 0; } -long fex_size ( File_Extractor const* fe ) { return fe->size; } -long fex_remain ( File_Extractor const* fe ) { return fe->size - FILE_GZ(ftell,gztell)( fe->file ); } -void fex_scan_only ( File_Extractor* ) { } -fex_err_t fex_read_once ( File_Extractor* fe, void* out, long count ) { return fex_read( fe, out, count ); } -long fex_read_avail ( File_Extractor* fe, void* out, long count ) { return FILE_READ( out, count, fe->file ); } - -fex_err_t fex_read( File_Extractor* fe, void* out, long count ) -{ - if ( count == (long) FILE_READ( out, count, fe->file ) ) - return 0; - - if ( FILE_GZ(feof,gzeof)( fe->file ) ) - return "Unexpected end of file"; - - return "Couldn't read from file"; -} - -fex_err_t fex_next( File_Extractor* fe ) -{ - fe->done = 1; - return 0; -} - -fex_err_t fex_rewind( File_Extractor* fe ) -{ - fe->done = 0; - FILE_GZ(rewind,gzrewind)( fe->file ); - return 0; -} - -static fex_err_t fex_data_( File_Extractor* fe ) -{ - if ( !fe->data ) - { - fe->data = malloc( fe->size ); - if ( !fe->data ) return "Out of memory"; - - fex_err_t err = fex_read( fe, fe->data, fe->size ); - if ( err ) - { - free( fe->data ); - return err; - } - } - return 0; -} - -const unsigned char* fex_data( File_Extractor* fe, fex_err_t* err_out ) -{ - fex_err_t err = fex_data_( fe ); - if ( err_out ) - *err_out = err; - return (const unsigned char*) fe->data; -} - -void fex_close( File_Extractor* fe ) -{ - if ( fe ) - { - free( fe->data ); - FILE_GZ(fclose,gzclose)( fe->file ); - - if ( fe->user_cleanup ) - fe->user_cleanup( fe->user_data ); - - free( fe ); - } -} +// Minimal implementation of fex.h. Supports gzipped files if you have zlib +// available and HAVE_ZLIB_H is defined. + +// File_Extractor 0.4.3. http://www.slack.net/~ant/ + +#include "fex.h" + +#include +#include +#include + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +/* Copyright (C) 2007 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef HAVE_ZLIB_H + #define FILE_GZ( norm, gz ) norm + #define FILE_READ( ptr, size, file ) fread( ptr, 1, size, file ) +#else + #define FILE_GZ( norm, gz ) gz + #define FILE_READ( ptr, size, file ) gzread( file, ptr, size ) + +#include "zlib.h" + +static const char* get_gzip_size( const char* path, long* eof ) +{ + FILE* file = fopen( path, "rb" ); + if ( !file ) + return "Couldn't open file"; + + unsigned char buf [4]; + if ( fread( buf, 2, 1, file ) > 0 && buf [0] == 0x1F && buf [1] == 0x8B ) + { + fseek( file, -4, SEEK_END ); + fread( buf, 4, 1, file ); + *eof = buf [3] * 0x1000000 + buf [2] * 0x10000 + buf [1] * 0x100 + buf [0]; + } + else + { + fseek( file, 0, SEEK_END ); + *eof = ftell( file ); + } + const char* err = (ferror( file ) || feof( file )) ? "Couldn't get file size" : 0; + fclose( file ); + return err; +} +#endif + +const char fex_wrong_file_type [] = "Archive format not supported"; + +struct File_Extractor +{ + FILE_GZ(FILE*,gzFile) file; + int done; + long size; + void* data; // file data read into memory, ot 0 if not read + void* user_data; + fex_user_cleanup_t user_cleanup; + + char* name() { return (char*) (this + 1); } +}; + +// Always identify as single file extractor +fex_type_t_ const fex_bin_type [1] = {{ "" , 0 }}; +const char* fex_identify_header ( void const* ) { return ""; } +fex_type_t fex_identify_extension( const char* ) { return fex_bin_type; } +fex_type_t fex_identify_file ( const char*, fex_err_t* e ) { if ( e ) *e = 0; return fex_bin_type; } + +static fex_err_t fex_open_( const char* path, File_Extractor** fe_out ) +{ + *fe_out = 0; + + // name + const char* name = strrchr( path, '\\' ); // DOS + if ( !name ) + name = strrchr( path, '/' ); // UNIX + if ( !name ) + name = strrchr( path, ':' ); // Mac + if ( !name ) + name = path; + + // allocate space for struct and name + long name_size = strlen( name ) + 1; + File_Extractor* fe = (File_Extractor*) malloc( sizeof (File_Extractor) + name_size ); + if ( !fe ) return "Out of memory"; + + fe->done = 0; + fe->data = 0; + fe->user_data = 0; + fe->user_cleanup = 0; + memcpy( fe->name(), name, name_size ); + + #ifdef HAVE_ZLIB_H + // get gzip size BEFORE opening file + const char* err = get_gzip_size( path, &fe->size ); + if ( err ) + { + free( fe ); + return err; + } + #endif + + // open file + fe->file = FILE_GZ(fopen,gzopen)( path, "rb" ); + if ( !fe->file ) + { + free( fe ); + return "Couldn't open file"; + } + + // get normal size + #ifndef HAVE_ZLIB_H + fseek( fe->file, 0, SEEK_END ); + fe->size = ftell( fe->file ); + rewind( fe->file ); + #endif + + *fe_out = fe; + return 0; +} + +File_Extractor* fex_open( const char* path, fex_err_t* err_out ) +{ + File_Extractor* fe; + fex_err_t err = fex_open_( path, &fe ); + if ( err_out ) + *err_out = err; + return fe; +} + +File_Extractor* fex_open_type( fex_type_t, const char* path, fex_err_t* err_out ) +{ + return fex_open( path, err_out ); +} + +void* fex_user_data ( File_Extractor const* fe ) { return fe->user_data; } +void fex_set_user_data ( File_Extractor* fe, void* new_user_data ) { fe->user_data = new_user_data; } +void fex_set_user_cleanup ( File_Extractor* fe, fex_user_cleanup_t func ) { fe->user_cleanup = func; } + +fex_type_t fex_type ( File_Extractor const* ) { return fex_bin_type; } +int fex_done ( File_Extractor const* fe ) { return fe->done; } +const char* fex_name ( File_Extractor* fe ) { return fe->name(); } +unsigned long fex_dos_date ( File_Extractor const* ) { return 0; } +long fex_size ( File_Extractor const* fe ) { return fe->size; } +long fex_remain ( File_Extractor const* fe ) { return fe->size - FILE_GZ(ftell,gztell)( fe->file ); } +void fex_scan_only ( File_Extractor* ) { } +fex_err_t fex_read_once ( File_Extractor* fe, void* out, long count ) { return fex_read( fe, out, count ); } +long fex_read_avail ( File_Extractor* fe, void* out, long count ) { return FILE_READ( out, count, fe->file ); } + +fex_err_t fex_read( File_Extractor* fe, void* out, long count ) +{ + if ( count == (long) FILE_READ( out, count, fe->file ) ) + return 0; + + if ( FILE_GZ(feof,gzeof)( fe->file ) ) + return "Unexpected end of file"; + + return "Couldn't read from file"; +} + +fex_err_t fex_next( File_Extractor* fe ) +{ + fe->done = 1; + return 0; +} + +fex_err_t fex_rewind( File_Extractor* fe ) +{ + fe->done = 0; + FILE_GZ(rewind,gzrewind)( fe->file ); + return 0; +} + +static fex_err_t fex_data_( File_Extractor* fe ) +{ + if ( !fe->data ) + { + fe->data = malloc( fe->size ); + if ( !fe->data ) return "Out of memory"; + + fex_err_t err = fex_read( fe, fe->data, fe->size ); + if ( err ) + { + free( fe->data ); + return err; + } + } + return 0; +} + +const unsigned char* fex_data( File_Extractor* fe, fex_err_t* err_out ) +{ + fex_err_t err = fex_data_( fe ); + if ( err_out ) + *err_out = err; + return (const unsigned char*) fe->data; +} + +void fex_close( File_Extractor* fe ) +{ + if ( fe ) + { + free( fe->data ); + FILE_GZ(fclose,gzclose)( fe->file ); + + if ( fe->user_cleanup ) + fe->user_cleanup( fe->user_data ); + + free( fe ); + } +} diff --git a/src/sdl/SDL.cpp b/src/sdl/SDL.cpp index 8be8ece4..b8cd5ec1 100644 --- a/src/sdl/SDL.cpp +++ b/src/sdl/SDL.cpp @@ -285,11 +285,32 @@ char screenMessageBuffer[21]; u32 screenMessageTime = 0; // Patch #1382692 by deathpudding. -SDL_sem *sdlBufferLock = NULL; -SDL_sem *sdlBufferFull = NULL; -SDL_sem *sdlBufferEmpty = NULL; -u8 sdlBuffer[4096]; -int sdlSoundLen = 0; +const int sdlSoundSamples = 2048; +const int sdlSoundAlign = 4; +const int sdlSoundCapacity = sdlSoundSamples * 2; +const int sdlSoundTotalLen = sdlSoundCapacity + sdlSoundAlign; +static u8 sdlSoundBuffer[sdlSoundTotalLen]; +static int sdlSoundRPos; +static int sdlSoundWPos; +static SDL_cond *sdlSoundCond; +static SDL_mutex *sdlSoundMutex; + +static inline int soundBufferFree() +{ + int ret = sdlSoundRPos - sdlSoundWPos - sdlSoundAlign; + if (ret < 0) + ret += sdlSoundTotalLen; + return ret; +} + +static inline int soundBufferUsed() +{ + int ret = sdlSoundWPos - sdlSoundRPos; + if (ret < 0) + ret += sdlSoundTotalLen; + return ret; +} + char *arg0; @@ -2078,6 +2099,8 @@ int main(int argc, char **argv) "FNO:T:Y:G:D:b:c:df:hi:p::s:t:v:1234", sdlOptions, NULL)) != -1) { + if (optarg) + optarg++; switch(op) { case 0: // long option already processed by getopt_long @@ -3139,73 +3162,71 @@ void systemScreenCapture(int a) systemScreenMessage("Screen capture"); } -void soundCallback(void *,u8 *stream,int len) +static void soundCallback(void *,u8 *stream,int len) { - if (!emulating) - return; + if (len <= 0 || !emulating) + return; - // Patch #1382692 by deathpudding. - /* since this is running in a different thread, speedup and - * throttle can change at any time; save the value so locks - * stay in sync */ - bool lock = (!speedup && !throttle) ? true : false; - - if (lock) - SDL_SemWait (sdlBufferFull); - - SDL_SemWait (sdlBufferLock); - memcpy (stream, sdlBuffer, len); - sdlSoundLen = 0; - SDL_SemPost (sdlBufferLock); - - if (lock) - SDL_SemPost (sdlBufferEmpty); +SDL_mutexP(sdlSoundMutex); + const int nAvail = soundBufferUsed(); + if (len > nAvail) + len = nAvail; + const int nAvail2 = ((sdlSoundTotalLen - sdlSoundRPos) + sdlSoundTotalLen) % sdlSoundTotalLen; + if (len >= nAvail2) { + memcpy(stream, &sdlSoundBuffer[sdlSoundRPos], nAvail2); + sdlSoundRPos = 0; + stream += nAvail2; + len -= nAvail2; + } + if (len > 0) { + memcpy(stream, &sdlSoundBuffer[sdlSoundRPos], len); + sdlSoundRPos = (sdlSoundRPos + len) % sdlSoundTotalLen; + stream += len; + } + SDL_CondSignal(sdlSoundCond); + SDL_mutexV(sdlSoundMutex); } - void systemWriteDataToSoundBuffer() { - // Patch #1382692 by deathpudding. - if (SDL_GetAudioStatus () != SDL_AUDIO_PLAYING) - SDL_PauseAudio (0); - - if ((sdlSoundLen + soundBufferLen) >= 2048*2) { - bool lock = (!speedup && !throttle) ? true : false; - - if (lock) - SDL_SemWait (sdlBufferEmpty); - - SDL_SemWait (sdlBufferLock); - int copied = 2048*2 - sdlSoundLen; - memcpy (sdlBuffer + sdlSoundLen, soundFinalWave, copied); - sdlSoundLen = 2048*2; - SDL_SemPost (sdlBufferLock); - - if (lock) { - SDL_SemPost (sdlBufferFull); - - /* wait for buffer to be dumped by soundCallback() */ - SDL_SemWait (sdlBufferEmpty); - SDL_SemPost (sdlBufferEmpty); - - SDL_SemWait (sdlBufferLock); - memcpy (sdlBuffer, ((u8 *)soundFinalWave) + copied, - soundBufferLen - copied); - sdlSoundLen = soundBufferLen - copied; - SDL_SemPost (sdlBufferLock); - } - else { - SDL_SemWait (sdlBufferLock); - memcpy (sdlBuffer, ((u8 *) soundFinalWave) + copied, soundBufferLen); - SDL_SemPost (sdlBufferLock); - } + if (SDL_GetAudioStatus() != SDL_AUDIO_PLAYING) + { + SDL_PauseAudio(0); } - else { - SDL_SemWait (sdlBufferLock); - memcpy (sdlBuffer + sdlSoundLen, soundFinalWave, soundBufferLen); - sdlSoundLen += soundBufferLen; - SDL_SemPost (sdlBufferLock); + int remain = soundBufferLen; + const u8 *wave = reinterpret_cast(soundFinalWave); + if (remain <= 0) + return; + SDL_mutexP(sdlSoundMutex); + int n; + while (remain >= (n = soundBufferFree())) { + const int nAvail = ((sdlSoundTotalLen - sdlSoundWPos) + sdlSoundTotalLen) % sdlSoundTotalLen; + if (n >= nAvail) { + memcpy(&sdlSoundBuffer[sdlSoundWPos], wave, nAvail); + sdlSoundWPos = 0; + wave += nAvail; + remain -= nAvail; + n -= nAvail; + } + if (!emulating || speedup || throttle) { + SDL_mutexV(sdlSoundMutex); + return; + } + SDL_CondWait(sdlSoundCond, sdlSoundMutex); + } + const int nAvail = ((sdlSoundTotalLen - sdlSoundWPos) + sdlSoundTotalLen) % sdlSoundTotalLen; + if (remain >= nAvail) { + memcpy(&sdlSoundBuffer[sdlSoundWPos], wave, nAvail); + sdlSoundWPos = 0; + wave += nAvail; + remain -= nAvail; + } + if (remain > 0) { + memcpy(&sdlSoundBuffer[sdlSoundWPos], wave, remain); + sdlSoundWPos = (sdlSoundWPos + remain) % sdlSoundTotalLen; } -} + SDL_mutexV(sdlSoundMutex); + } + bool systemSoundInit() { @@ -3234,26 +3255,34 @@ bool systemSoundInit() fprintf(stderr,"Failed to open audio: %s\n", SDL_GetError()); return false; } - soundBufferTotalLen = soundBufferLen*10; - // Patch #1382692 by deathpudding. - sdlBufferLock = SDL_CreateSemaphore (1); - sdlBufferFull = SDL_CreateSemaphore (0); - sdlBufferEmpty = SDL_CreateSemaphore (1); - sdlSoundLen = 0; + + sdlSoundCond = SDL_CreateCond(); + sdlSoundMutex = SDL_CreateMutex(); + + sdlSoundRPos = sdlSoundWPos = 0; systemSoundOn = true; return true; + } void systemSoundShutdown() { - // Patch #1382692 by deathpudding. - SDL_CloseAudio (); //TODO: fix freeze - SDL_DestroySemaphore (sdlBufferLock); - SDL_DestroySemaphore (sdlBufferFull); - SDL_DestroySemaphore (sdlBufferEmpty); - sdlBufferLock = NULL; - sdlBufferFull = NULL; - sdlBufferEmpty = NULL; + SDL_mutexP(sdlSoundMutex); + int iSave = emulating; + emulating = 0; + SDL_CondSignal(sdlSoundCond); + SDL_mutexV(sdlSoundMutex); + + SDL_DestroyCond(sdlSoundCond); + sdlSoundCond = NULL; + + SDL_DestroyMutex(sdlSoundMutex); + sdlSoundMutex = NULL; + + SDL_CloseAudio(); + + emulating = iSave; + systemSoundOn = false; } void systemSoundPause() diff --git a/src/win32/MainWnd.cpp b/src/win32/MainWnd.cpp index e4518130..8a757695 100644 --- a/src/win32/MainWnd.cpp +++ b/src/win32/MainWnd.cpp @@ -180,6 +180,8 @@ BEGIN_MESSAGE_MAP(MainWnd, CWnd) ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_RENDEROPTIONS_GLQUADS, OnUpdateOptionsVideoRenderoptionsGlquads) ON_COMMAND(ID_OPTIONS_VIDEO_RENDEROPTIONS_GLPOLYGONS, OnOptionsVideoRenderoptionsGlpolygons) ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_RENDEROPTIONS_GLPOLYGONS, OnUpdateOptionsVideoRenderoptionsGlpolygons) + ON_COMMAND(ID_OPTIONS_VIDEO_RENDEROPTIONS_GLSLSHADERS, OnOptionsVideoRenderingoptionsGLSLShaders) + ON_UPDATE_COMMAND_UI(ID_OPTIONS_VIDEO_RENDEROPTIONS_GLSLSHADERS, OnUpdateOptionsVideoRenderingoptionsGLSLShaders) ON_WM_CONTEXTMENU() ON_COMMAND(ID_OPTIONS_EMULATOR_ASSOCIATE, OnOptionsEmulatorAssociate) diff --git a/src/win32/MainWnd.h b/src/win32/MainWnd.h index f8e12357..5495e178 100644 --- a/src/win32/MainWnd.h +++ b/src/win32/MainWnd.h @@ -200,8 +200,8 @@ class MainWnd : public CWnd afx_msg void OnUpdateOptionsVideoRenderoptionsGlnearest(CCmdUI* pCmdUI); afx_msg void OnOptionsVideoRenderoptionsGlbilinear(); afx_msg void OnUpdateOptionsVideoRenderoptionsGlbilinear(CCmdUI* pCmdUI); - afx_msg void OnUpdateOptionsVideoRenderoptionsGlanisotropic(CCmdUI* pCmdUI); - afx_msg void OnOptionsVideoRenderoptionsGlanisotropic(); + afx_msg void OnOptionsVideoRenderingoptionsGLSLShaders(); + afx_msg void OnUpdateOptionsVideoRenderingoptionsGLSLShaders(CCmdUI* pCmdUI); afx_msg void OnOptionsVideoRenderoptionsGltriangle(); afx_msg void OnUpdateOptionsVideoRenderoptionsGltriangle(CCmdUI* pCmdUI); diff --git a/src/win32/MainWndOptions.cpp b/src/win32/MainWndOptions.cpp index 29be9a64..1f9176b5 100644 --- a/src/win32/MainWndOptions.cpp +++ b/src/win32/MainWndOptions.cpp @@ -625,6 +625,19 @@ void MainWnd::OnUpdateOptionsVideoRenderoptionsGlpolygons(CCmdUI* pCmdUI) pCmdUI->SetCheck(theApp.glType == 2); } +void MainWnd::OnUpdateOptionsVideoRenderingoptionsGLSLShaders(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(theApp.GLSLShaders); +} + +void MainWnd::OnOptionsVideoRenderingoptionsGLSLShaders() +{ + theApp.GLSLShaders = !theApp.GLSLShaders; + if( theApp.GLSLShaders ) { + theApp.display->setOption( _T("GLSLShaders"), theApp.GLSLShaders ); + } +} + void MainWnd::OnOptionsEmulatorAssociate() { theApp.winCheckFullscreen(); diff --git a/src/win32/OpenGL.cpp b/src/win32/OpenGL.cpp index f18a4e99..4480d03a 100644 --- a/src/win32/OpenGL.cpp +++ b/src/win32/OpenGL.cpp @@ -32,6 +32,8 @@ #include #include "glFont.h" +#pragma comment(lib,"glew32.lib") +#include // OpenGL #include // main include file #ifdef HAS_GLEXT @@ -68,6 +70,9 @@ private: RECT destRect; bool failed; GLFONT font; + char *VertexShaderSource,*FragmentShaderSource; + int VertexShader,FragmentShader; + int ShaderProgram; void initializeMatrices( int w, int h ); bool initializeTexture( int w, int h ); @@ -75,6 +80,10 @@ private: void setVSync( int interval = 1 ); void calculateDestRect( int w, int h ); void initializeFont(); + void InitShader(); + void DeInitShader(); + void rasterise(); + public: OpenGLDisplay(); @@ -93,8 +102,10 @@ public: virtual int selectFullScreenMode( GUID ** ); }; -#include "gzglfont.h" + +#include "gzglfont.h" +char *readShaderFile(char *FileName); void OpenGLDisplay::initializeFont() { int ret; @@ -126,7 +137,61 @@ void OpenGLDisplay::initializeFont() (void)inflateEnd(&strm); } +char *readShaderFile(char *FileName) { + FILE *fp; + char *DATA = NULL; + int flength = 0; + + fp = fopen(FileName,"rt"); + + fseek(fp, 0, SEEK_END); + flength = ftell(fp); + rewind(fp); + + DATA = (char *)malloc(sizeof(char) * (flength+1)); + flength = fread(DATA, sizeof(char), flength, fp); + DATA[flength] = '\0'; + + fclose(fp); + + return DATA; +} + +void OpenGLDisplay::DeInitShader () { + glDetachObjectARB(ShaderProgram,VertexShader); + glDetachObjectARB(ShaderProgram,FragmentShader); + glDeleteObjectARB(ShaderProgram); +} + +void OpenGLDisplay::InitShader () { + GLEW_ARB_vertex_shader; + GLEW_ARB_fragment_shader; + + VertexShader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); + FragmentShader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); + + VertexShaderSource = readShaderFile("vertex_shader.vert"); + FragmentShaderSource = readShaderFile("fragment_shader.frag"); + + const char * VS = VertexShaderSource; + const char * FS = FragmentShaderSource; + + glShaderSourceARB(VertexShader, 1, &VS,NULL); + glShaderSourceARB(FragmentShader, 1, &FS,NULL); + + free(VertexShaderSource);free(FragmentShaderSource); + + glCompileShaderARB(VertexShader); + glCompileShaderARB(FragmentShader); + + ShaderProgram = glCreateProgramObjectARB(); + + glAttachObjectARB(ShaderProgram,VertexShader); + glAttachObjectARB(ShaderProgram,FragmentShader); + + glLinkProgramARB(ShaderProgram); +} OpenGLDisplay::OpenGLDisplay() { @@ -143,6 +208,7 @@ OpenGLDisplay::OpenGLDisplay() OpenGLDisplay::~OpenGLDisplay() { + DeInitShader(); cleanup(); } @@ -205,8 +271,8 @@ bool OpenGLDisplay::initialize() glEnable( GL_TEXTURE_2D ); glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - - + glewInit(); + initializeMatrices( theApp.surfaceSizeX, theApp.surfaceSizeY ); setVSync( theApp.vsync ); @@ -243,12 +309,8 @@ void OpenGLDisplay::clear() glClear( GL_COLOR_BUFFER_BIT ); } - -void OpenGLDisplay::render() +void OpenGLDisplay::rasterise() { - clear(); - - int pitch = theApp.filterWidth * (systemColorDepth>>3) + 4; u8 *data = pix + ( theApp.sizeX + 1 ) * 4; @@ -273,17 +335,7 @@ void OpenGLDisplay::render() } else { glPixelStorei( GL_UNPACK_ROW_LENGTH, theApp.sizeX + 1 ); } - - glTexSubImage2D( - GL_TEXTURE_2D, - 0, - 0, - 0, - width, - height, - GL_RGBA, - GL_UNSIGNED_BYTE, - data ); + glTexSubImage2D(GL_TEXTURE_2D,0,0,0,width,height,GL_RGBA,GL_UNSIGNED_BYTE,data ); if( theApp.glType == 0 ) { glBegin( GL_TRIANGLE_STRIP ); @@ -377,12 +429,30 @@ void OpenGLDisplay::render() } } +} +void OpenGLDisplay::render() +{ + clear(); + if (theApp.GLSLShaders){ + InitShader(); + glUseProgramObjectARB(ShaderProgram); + int texture_location = glGetUniformLocationARB(ShaderProgram, "ShaderTexture"); + glUniform1iARB(texture_location, 0); + } + else{ + glUseProgramObjectARB(NULL); + DeInitShader(); + } + rasterise(); + glFlush(); + + SwapBuffers( hDC ); // since OpenGL draws on the back buffer, // we have to swap it to the front buffer to see it - + // draw informations with GDI on the front buffer } diff --git a/src/win32/VBA.cpp b/src/win32/VBA.cpp index 81a5f66b..8c077336 100644 --- a/src/win32/VBA.cpp +++ b/src/win32/VBA.cpp @@ -288,6 +288,7 @@ VBA::VBA() ddrawUseVideoMemory = false; d3dFilter = 0; glFilter = 0; + GLSLShaders = 0; glType = 0; skin = NULL; skinName = ""; @@ -1489,6 +1490,10 @@ void VBA::loadSettings() if(glFilter < 0 || glFilter > 1) glFilter = 1; + GLSLShaders = regQueryDwordValue("GLSLShaders", 0); + if(GLSLShaders < 0 || GLSLShaders > 1) + GLSLShaders = 0; + glType = regQueryDwordValue("glType", 0); if(glType < 0 || glType > 2) glType = 0; @@ -2571,6 +2576,7 @@ void VBA::saveSettings() regSetDwordValue("d3dFilter", d3dFilter); regSetDwordValue("glFilter", glFilter); + regSetDwordValue("GLSLShaders", GLSLShaders); regSetDwordValue("glType", glType); regSetDwordValue("filter", filterType); diff --git a/src/win32/VBA.h b/src/win32/VBA.h index a80c53d0..b02e58de 100644 --- a/src/win32/VBA.h +++ b/src/win32/VBA.h @@ -163,6 +163,7 @@ class VBA : public CWinApp bool ddrawUseVideoMemory; int d3dFilter; int glFilter; + int GLSLShaders; int glType; bool dinputKeyFocus; CSkin *skin; diff --git a/src/win32/VBA.rc b/src/win32/VBA.rc index 28c82bbf..2b532829 100644 --- a/src/win32/VBA.rc +++ b/src/win32/VBA.rc @@ -1595,6 +1595,7 @@ BEGIN MENUITEM " Vertex: Triangle", ID_OPTIONS_VIDEO_RENDEROPTIONS_GLTRIANGLE MENUITEM " Vertex: Quads", ID_OPTIONS_VIDEO_RENDEROPTIONS_GLQUADS MENUITEM " Vertex: Polygons", ID_OPTIONS_VIDEO_RENDEROPTIONS_GLPOLYGONS + MENUITEM " GLSL Shaders", ID_OPTIONS_VIDEO_RENDEROPTIONS_GLSLSHADERS MENUITEM SEPARATOR MENUITEM "&VSync", ID_OPTIONS_VIDEO_VSYNC MENUITEM "Triple Buffering", ID_OPTIONS_VIDEO_TRIPLEBUFFERING diff --git a/src/win32/resource.h b/src/win32/resource.h index cfcdd6e1..9f72277c 100644 --- a/src/win32/resource.h +++ b/src/win32/resource.h @@ -813,13 +813,15 @@ #define ID_OUTPUTAPI_SOFTWAREMIXING 40348 #define ID_OUTPUTAPI_CONFIGURATION 40349 #define ID_OUTPUTAPI_OALCONFIGURATION 40350 +#define ID_RENDERAPI_FILTER 40351 +#define ID_OPTIONS_VIDEO_RENDEROPTIONS_GLSLSHADERS 40352 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 161 -#define _APS_NEXT_COMMAND_VALUE 40351 +#define _APS_NEXT_COMMAND_VALUE 40353 #define _APS_NEXT_CONTROL_VALUE 1270 #define _APS_NEXT_SYMED_VALUE 103 #endif