diff --git a/stepmania/src/RageSurface_Load.cpp b/stepmania/src/RageSurface_Load.cpp index 4667f0b795..8eed5cddbb 100644 --- a/stepmania/src/RageSurface_Load.cpp +++ b/stepmania/src/RageSurface_Load.cpp @@ -5,45 +5,139 @@ #include "RageSurface_Load_GIF.h" #include "RageUtil.h" #include "RageFile.h" +#include "RageLog.h" +#include "SDL_utils.h" +#include + +static RageSurface::OpenResult RageSurface_Load_BMP( const CString &sPath, SDL_Surface *&ret, bool bHeaderOnly, CString &error ) +{ + RageFile f; + if( !f.Open(sPath) ) + { + error = f.GetError(); + return RageSurface::OPEN_FATAL_ERROR; + } + + SDL_RWops rw; + OpenRWops( &rw, &f ); + ret = SDL_LoadBMP_RW( &rw, false ); + SDL_RWclose( &rw ); + + if( ret == NULL ) + { + error = SDL_GetError(); + return RageSurface::OPEN_UNKNOWN_FILE_FORMAT; + } + + mySDL_FixupPalettedAlpha( ret ); + return RageSurface::OPEN_OK; +} + + +static SDL_Surface *TryOpenFile( CString sPath, bool bHeaderOnly, CString &error, CString format, bool &bKeepTrying ) +{ + SDL_Surface *ret = NULL; + RageSurface::OpenResult result; + if( !format.CompareNoCase("png") ) + result = RageSurface_Load_PNG( sPath, ret, bHeaderOnly, error ); + else if( !format.CompareNoCase("gif") ) + result = RageSurface_Load_GIF( sPath, ret, bHeaderOnly, error ); + else if( !format.CompareNoCase("jpg") ) + result = RageSurface_Load_JPEG( sPath, ret, bHeaderOnly, error ); + else if( !format.CompareNoCase("bmp") ) + result = RageSurface_Load_BMP( sPath, ret, bHeaderOnly, error ); + else + { + error = "Unsupported format"; + bKeepTrying = true; + return NULL; + } + + if( result == RageSurface::OPEN_OK ) + return ret; + + LOG->Trace( "Format %s failed: %s", format.c_str(), error.c_str() ); + + /* + * The file failed to open, or failed to read. This indicates a problem that will + * affect all readers, so don't waste time trying more readers. (OPEN_IO_ERROR) + * + * Errors fall in two categories: + * OPEN_UNKNOWN_FILE_FORMAT: Data was successfully read from the file, but it's the + * wrong file format. The error message always looks like "unknown file format" or + * "Not Vorbis data"; ignore it so we always give a consistent error message, and + * continue trying other file formats. + * + * OPEN_FATAL_ERROR: Either the file was opened successfully and appears to be the + * correct format, but a fatal format-specific error was encountered that will probably + * not be fixed by using a different reader (for example, an Ogg file that doesn't + * actually contain any audio streams); or the file failed to open or read ("I/O + * error", "permission denied"), in which case all other readers will probably fail, + * too. The returned error is used, and no other formats will be tried. + */ + bKeepTrying = (result != RageSurface::OPEN_FATAL_ERROR); + switch( result ) + { + case RageSurface::OPEN_UNKNOWN_FILE_FORMAT: + bKeepTrying = true; + error = "Unknown file format"; + break; + + case RageSurface::OPEN_FATAL_ERROR: + /* The file matched, but failed to load. We know it's this type of data; + * don't bother trying the other file types. */ + bKeepTrying = false; + break; + } + + return NULL; +} SDL_Surface *RageSurface::LoadFile( const CString &sPath, bool bHeaderOnly ) { - const CString ext = GetExtension( sPath ); - - CString error; - SDL_Surface *ret = NULL; - if( !ext.CompareNoCase("png") ) - ret = RageSurface_Load_PNG( sPath, bHeaderOnly, error ); - else if( !ext.CompareNoCase("gif") ) - ret = RageSurface_Load_GIF( sPath, error ); - else if( !ext.CompareNoCase("jpg") ) - ret = RageSurface_Load_JPEG( sPath, error ); - else if( !ext.CompareNoCase("bmp") ) { - RageFile f; - if( !f.Open(sPath) ) + RageFile TestOpen; + if( !TestOpen.Open( sPath ) ) { - SDL_SetError( "%s", f.GetError().c_str() ); + SDL_SetError( "%s", TestOpen.GetError() ); return NULL; } - - SDL_RWops rw; - OpenRWops( &rw, &f ); - ret = SDL_LoadBMP_RW( &rw, false ); - SDL_RWclose( &rw ); - - if( ret ) - mySDL_FixupPalettedAlpha( ret ); } - else + + set FileTypes; + FileTypes.insert("png"); + FileTypes.insert("jpg"); + FileTypes.insert("gif"); + FileTypes.insert("bmp"); + + CString format = GetExtension(sPath); + format.MakeLower(); + + CString error = ""; + + bool bKeepTrying = true; + + /* If the extension matches a format, try that first. */ + if( FileTypes.find(format) != FileTypes.end() ) { - error = ssprintf( "Unsupported file type \"%s\"", ext.c_str() ); + SDL_Surface *ret = TryOpenFile( sPath, bHeaderOnly, error, format, bKeepTrying ); + if( ret ) + return ret; + FileTypes.erase( format ); } - if( ret == NULL ) - SDL_SetError( "%s", error.c_str() ); + for( set::iterator it = FileTypes.begin(); bKeepTrying && it != FileTypes.end(); ++it ) + { + SDL_Surface *ret = TryOpenFile( sPath, bHeaderOnly, error, *it, bKeepTrying ); + if( ret ) + { + LOG->Warn("File \"%s\" is really %s", sPath.c_str(), it->c_str()); + return ret; + } + } - return ret; + SDL_SetError( "%s", error.c_str() ); + return NULL; } /* diff --git a/stepmania/src/RageSurface_Load.h b/stepmania/src/RageSurface_Load.h index 30622e4e65..7954030ffc 100644 --- a/stepmania/src/RageSurface_Load.h +++ b/stepmania/src/RageSurface_Load.h @@ -4,6 +4,13 @@ struct SDL_Surface; namespace RageSurface { + enum OpenResult + { + OPEN_OK, + OPEN_UNKNOWN_FILE_FORMAT=1, + OPEN_FATAL_ERROR=2, + }; + /* If bHeaderOnly is true, the loader is only required to return a surface * with the width and height set (but may return a complete surface). */ SDL_Surface *LoadFile( const CString &sPath, bool bHeaderOnly=false ); diff --git a/stepmania/src/RageSurface_Load_GIF.cpp b/stepmania/src/RageSurface_Load_GIF.cpp index cc820a6ceb..cf5a7aeae1 100644 --- a/stepmania/src/RageSurface_Load_GIF.cpp +++ b/stepmania/src/RageSurface_Load_GIF.cpp @@ -3,6 +3,7 @@ #include "RageFile.h" #include "RageUtil.h" #include "RageLog.h" +#include "SDL_utils.h" #define MAXCOLORMAPSIZE 256 @@ -57,7 +58,7 @@ static int GetDataBlock( RageFile &f, unsigned char *buf ) } -SDL_Surface *RageSurface_Load_GIF( const CString &sPath, CString &error ) +RageSurface::OpenResult RageSurface_Load_GIF( const CString &sPath, SDL_Surface *&ret, bool bHeaderOnly, CString &error ) { unsigned char buf[256]; int imageCount = 0; @@ -67,18 +68,18 @@ SDL_Surface *RageSurface_Load_GIF( const CString &sPath, CString &error ) if( !f.Open( sPath ) ) { error = f.GetError(); - return NULL; + return RageSurface::OPEN_FATAL_ERROR; } if( !ReadOK(f, buf, 6) ) { error = "error reading magic number"; - return NULL; + return RageSurface::OPEN_FATAL_ERROR; } if( strncmp((char *) buf, "GIF", 3) != 0 ) { error = "not a GIF file"; - return NULL; + return RageSurface::OPEN_UNKNOWN_FILE_FORMAT; } { @@ -89,14 +90,14 @@ SDL_Surface *RageSurface_Load_GIF( const CString &sPath, CString &error ) if( (strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0) ) { error = "bad version number, not '87a' or '89a'"; - return NULL; + return RageSurface::OPEN_FATAL_ERROR; } } if( !ReadOK(f, buf, 7) ) { error = "failed to read screen descriptor"; - return NULL; + return RageSurface::OPEN_FATAL_ERROR; } SDL_Color GlobalColorMap[MAXCOLORMAPSIZE]; @@ -110,7 +111,7 @@ SDL_Surface *RageSurface_Load_GIF( const CString &sPath, CString &error ) if( !ReadPalette(f, GlobalBitPixel, GlobalColorMap ) ) { error = "error reading global colormap"; - return NULL; + return RageSurface::OPEN_FATAL_ERROR; } } @@ -122,7 +123,7 @@ SDL_Surface *RageSurface_Load_GIF( const CString &sPath, CString &error ) if( !ReadOK(f, &type, 1) ) { error = "EOF / read error on image data"; - return NULL; + return RageSurface::OPEN_FATAL_ERROR; } switch( type ) { @@ -133,7 +134,7 @@ SDL_Surface *RageSurface_Load_GIF( const CString &sPath, CString &error ) { error = ssprintf( "only %d image%s found in file", imageCount, imageCount > 1 ? "s" : ""); - return NULL; + return RageSurface::OPEN_FATAL_ERROR; } } @@ -144,7 +145,7 @@ SDL_Surface *RageSurface_Load_GIF( const CString &sPath, CString &error ) if( !ReadOK(f, &label, 1) ) { error = "EOF / read error on extention function code"; - return NULL; + return RageSurface::OPEN_FATAL_ERROR; } switch( label ) @@ -167,7 +168,7 @@ SDL_Surface *RageSurface_Load_GIF( const CString &sPath, CString &error ) if( !ReadOK(f, buf, 9) ) { error = "couldn't read left/top/width/height"; - return NULL; + return RageSurface::OPEN_FATAL_ERROR; } int bitPixel = 1 << ((buf[8] & 0x07) + 1); @@ -178,7 +179,7 @@ SDL_Surface *RageSurface_Load_GIF( const CString &sPath, CString &error ) if( !ReadPalette(f, bitPixel, LocalColorMap) ) { error = "error reading local colormap"; - return NULL; + return RageSurface::OPEN_FATAL_ERROR; } } else { bitPixel = GlobalBitPixel; @@ -195,13 +196,13 @@ SDL_Surface *RageSurface_Load_GIF( const CString &sPath, CString &error ) if( transparency != -1 ) mySDL_AddColorKey( image, transparency ); - return image; + return RageSurface::OPEN_OK; } default: continue; /* Not a valid start character */ } } - return NULL; + return RageSurface::OPEN_FATAL_ERROR; } struct LWZState diff --git a/stepmania/src/RageSurface_Load_GIF.h b/stepmania/src/RageSurface_Load_GIF.h index 3813d0961a..16db6ed2b2 100644 --- a/stepmania/src/RageSurface_Load_GIF.h +++ b/stepmania/src/RageSurface_Load_GIF.h @@ -1,9 +1,8 @@ #ifndef RAGE_SURFACE_LOAD_GIF_H #define RAGE_SURFACE_LOAD_GIF_H -#include "SDL_utils.h" - -SDL_Surface *RageSurface_Load_GIF( const CString &sPath, CString &error ); +#include "RageSurface_Load.h" +RageSurface::OpenResult RageSurface_Load_GIF( const CString &sPath, SDL_Surface *&ret, bool bHeaderOnly, CString &error ); #endif diff --git a/stepmania/src/RageSurface_Load_JPEG.cpp b/stepmania/src/RageSurface_Load_JPEG.cpp index c1d1d04e75..da1714467c 100644 --- a/stepmania/src/RageSurface_Load_JPEG.cpp +++ b/stepmania/src/RageSurface_Load_JPEG.cpp @@ -194,24 +194,24 @@ static SDL_Surface *RageSurface_Load_JPEG( RageFile *f, const char *fn, char err } -SDL_Surface *RageSurface_Load_JPEG( const CString &sPath, CString &error ) +RageSurface::OpenResult RageSurface_Load_JPEG( const CString &sPath, SDL_Surface *&ret, bool bHeaderOnly, CString &error ) { RageFile f; if( !f.Open( sPath ) ) { error = f.GetError(); - return NULL; + return RageSurface::OPEN_FATAL_ERROR; } char errorbuf[1024]; - SDL_Surface *ret = RageSurface_Load_JPEG( &f, sPath, errorbuf ); + ret = RageSurface_Load_JPEG( &f, sPath, errorbuf ); if( ret == NULL ) { error = errorbuf; - return ret; + return RageSurface::OPEN_UNKNOWN_FILE_FORMAT; // XXX } - return ret; + return RageSurface::OPEN_OK; } /* diff --git a/stepmania/src/RageSurface_Load_JPEG.h b/stepmania/src/RageSurface_Load_JPEG.h index 1eecf13256..2606bb18d3 100644 --- a/stepmania/src/RageSurface_Load_JPEG.h +++ b/stepmania/src/RageSurface_Load_JPEG.h @@ -1,8 +1,8 @@ #ifndef RAGE_SURFACE_LOAD_JPEG_H #define RAGE_SURFACE_LOAD_JPEG_H -struct SDL_Surface; -SDL_Surface *RageSurface_Load_JPEG( const CString &sPath, CString &error ); +#include "RageSurface_Load.h" +RageSurface::OpenResult RageSurface_Load_JPEG( const CString &sPath, SDL_Surface *&ret, bool bHeaderOnly, CString &error ); #endif diff --git a/stepmania/src/RageSurface_Load_PNG.cpp b/stepmania/src/RageSurface_Load_PNG.cpp index 333fa9567b..1c2187ad29 100644 --- a/stepmania/src/RageSurface_Load_PNG.cpp +++ b/stepmania/src/RageSurface_Load_PNG.cpp @@ -237,24 +237,24 @@ static SDL_Surface *RageSurface_Load_PNG( RageFile *f, const char *fn, char erro } -SDL_Surface *RageSurface_Load_PNG( const CString &sPath, bool bHeaderOnly, CString &error ) +RageSurface::OpenResult RageSurface_Load_PNG( const CString &sPath, SDL_Surface *&ret, bool bHeaderOnly, CString &error ) { RageFile f; if( !f.Open( sPath ) ) { error = f.GetError(); - return NULL; + return RageSurface::OPEN_FATAL_ERROR; } char errorbuf[1024]; - SDL_Surface *ret = RageSurface_Load_PNG( &f, sPath, errorbuf, bHeaderOnly ); + ret = RageSurface_Load_PNG( &f, sPath, errorbuf, bHeaderOnly ); if( ret == NULL ) { error = errorbuf; - return ret; + return RageSurface::OPEN_UNKNOWN_FILE_FORMAT; // XXX } - return ret; + return RageSurface::OPEN_OK; } /* diff --git a/stepmania/src/RageSurface_Load_PNG.h b/stepmania/src/RageSurface_Load_PNG.h index dc3d006771..8c18ee0ddc 100644 --- a/stepmania/src/RageSurface_Load_PNG.h +++ b/stepmania/src/RageSurface_Load_PNG.h @@ -1,9 +1,8 @@ #ifndef RAGE_SURFACE_LOAD_PNG_H #define RAGE_SURFACE_LOAD_PNG_H -#include "SDL_utils.h" - -SDL_Surface *RageSurface_Load_PNG( const CString &sPath, bool bHeaderOnly, CString &error ); +#include "RageSurface_Load.h" +RageSurface::OpenResult RageSurface_Load_PNG( const CString &sPath, SDL_Surface *&ret, bool bHeaderOnly, CString &error ); #endif