]> git.sesse.net Git - vlc/blobdiff - modules/gui/skins2/src/theme_loader.cpp
* winamp2.xml: hack to support nums_ex.bmp and numbers.bmp
[vlc] / modules / gui / skins2 / src / theme_loader.cpp
index c8b89aff2990383b4b6659e84acc05a4f62ba794..b48a45338eb6aa3d5faf9f10f9c56156f4e9c5b6 100644 (file)
@@ -1,7 +1,7 @@
 /*****************************************************************************
  * theme_loader.cpp
  *****************************************************************************
- * Copyright (C) 2003 VideoLAN
+ * Copyright (C) 2003 the VideoLAN team
  * $Id$
  *
  * Authors: Cyril Deguet     <asmax@via.ecp.fr>
 #if defined( HAVE_ZLIB_H )
 #   include <zlib.h>
 #   include <errno.h>
-int gzopen_frontend( char *pathname, int oflags, int mode );
+int gzopen_frontend ( char *pathname, int oflags, int mode );
+int gzclose_frontend( int );
+int gzread_frontend ( int, void *, size_t );
+int gzwrite_frontend( int, const void *, size_t );
 #if defined( HAVE_LIBTAR_H )
 #   include <libtar.h>
 #else
@@ -58,21 +61,26 @@ typedef gzFile TAR;
 int tar_open        ( TAR **t, char *pathname, int oflags );
 int tar_extract_all ( TAR *t, char *prefix );
 int tar_close       ( TAR *t );
+int getoct( char *p, int width );
 #endif
+int makedir( const char *newdir );
 #endif
 
 #define DEFAULT_XML_FILE "theme.xml"
+#define WINAMP2_XML_FILE "winamp2.xml"
+#define ZIP_BUFFER_SIZE 4096
 
 
 bool ThemeLoader::load( const string &fileName )
 {
     // First, we try to un-targz the file, and if it fails we hope it's a XML
     // file...
+    string path = getFilePath( fileName );
 #if defined( HAVE_ZLIB_H )
-    if( ! extract( fileName ) && ! parse( fileName ) )
+    if( ! extract( fileName ) && ! parse( path, fileName ) )
         return false;
 #else
-    if( ! parse( fileName ) )
+    if( ! parse( path, fileName ) )
         return false;
 #endif
 
@@ -95,7 +103,7 @@ bool ThemeLoader::load( const string &fileName )
     {
         config_PutPsz( getIntf(), "skins2-last", fileName.c_str() );
         // Show the windows
-        pNewTheme->getWindowManager().showAll();
+        pNewTheme->getWindowManager().showAll( true );
     }
     if( skin_last ) free( skin_last );
 
@@ -111,8 +119,10 @@ bool ThemeLoader::extractTarGz( const string &tarFile, const string &rootDir )
 {
     TAR *t;
 #if defined( HAVE_LIBTAR_H )
-    tartype_t gztype = { (openfunc_t) gzopen_frontend, (closefunc_t) gzclose,
-        (readfunc_t) gzread, (writefunc_t) gzwrite };
+    tartype_t gztype = { (openfunc_t) gzopen_frontend,
+                         (closefunc_t) gzclose_frontend,
+                         (readfunc_t) gzread_frontend,
+                         (writefunc_t) gzwrite_frontend };
 
     if( tar_open( &t, (char *)tarFile.c_str(), &gztype, O_RDONLY, 0,
                   TAR_GNU ) == -1 )
@@ -138,29 +148,177 @@ bool ThemeLoader::extractTarGz( const string &tarFile, const string &rootDir )
 }
 
 
+bool ThemeLoader::extractZip( const string &zipFile, const string &rootDir )
+{
+    // Try to open the ZIP file
+    unzFile file = unzOpen( zipFile.c_str() );
+    unz_global_info info;
+
+    if( unzGetGlobalInfo( file, &info ) != UNZ_OK )
+    {
+        return false;
+    }
+    // Extract all the files in the archive
+    for( unsigned long i = 0; i < info.number_entry; i++ )
+    {
+        if( !extractFileInZip( file, rootDir ) )
+        {
+            msg_Warn( getIntf(), "Error while unzipping %s",
+                      zipFile.c_str() );
+            return false;
+        }
+
+        if( i < info.number_entry - 1 )
+        {
+            // Go the next file in the archive
+            if( unzGoToNextFile( file ) !=UNZ_OK )
+            {
+                msg_Warn( getIntf(), "Error while unzipping %s",
+                          zipFile.c_str() );
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+
+bool ThemeLoader::extractFileInZip( unzFile file, const string &rootDir )
+{
+    // Read info for the current file
+    char filenameInZip[256];
+    unz_file_info fileInfo;
+    if( unzGetCurrentFileInfo( file, &fileInfo, filenameInZip,
+                               sizeof( filenameInZip), NULL, 0, NULL, 0 )
+        != UNZ_OK )
+    {
+        return false;
+    }
+
+    // Allocate the buffer
+    void *pBuffer = malloc( ZIP_BUFFER_SIZE );
+    if( !pBuffer )
+    {
+        msg_Err( getIntf(), "Failed to allocate memory" );
+        return false;
+    }
+
+    // Get the path of the file
+    string fullPath = rootDir + "/" + filenameInZip;
+    string basePath = getFilePath( fullPath );
+
+    // Extract the file if is not a directory
+    if( basePath != fullPath )
+    {
+        if( unzOpenCurrentFile( file ) )
+        {
+            free( pBuffer );
+            return false;
+        }
+        makedir( basePath.c_str() );
+        FILE *fout = fopen( fullPath.c_str(), "wb" );
+        if( fout == NULL )
+        {
+            msg_Err( getIntf(), "Error opening %s", fullPath.c_str() );
+            free( pBuffer );
+            return false;
+        }
+
+        // Extract the current file
+        int n;
+        do
+        {
+            n = unzReadCurrentFile( file, pBuffer, ZIP_BUFFER_SIZE );
+            if( n < 0 )
+            {
+                msg_Err( getIntf(), "Error while reading zip file" );
+                free( pBuffer );
+                return false;
+            }
+            else if( n > 0 )
+            {
+                if( fwrite( pBuffer, n , 1, fout) != 1 )
+                {
+                    msg_Err( getIntf(), "Error while writing %s",
+                             fullPath.c_str() );
+                    free( pBuffer );
+                    return false;
+                }
+            }
+        } while( n > 0 );
+
+        fclose(fout);
+
+        if( unzCloseCurrentFile( file ) != UNZ_OK )
+        {
+            free( pBuffer );
+            return false;
+        }
+    }
+
+    free( pBuffer );
+    return true;
+}
+
+
 bool ThemeLoader::extract( const string &fileName )
 {
+    bool result = true;
     char *tmpdir = tempnam( NULL, "vlt" );
     string tempPath = tmpdir;
     free( tmpdir );
 
     // Extract the file in a temporary directory
-    if( ! extractTarGz( fileName, tempPath ) )
+    if( ! extractTarGz( fileName, tempPath ) &&
+        ! extractZip( fileName, tempPath ) )
         return false;
 
-    // Find the XML file and parse it
+    string path;
     string xmlFile;
-    if( ! findThemeFile( tempPath, xmlFile ) || ! parse( xmlFile ) )
+    OSFactory *pOsFactory = OSFactory::instance( getIntf() );
+    // Find the XML file in the theme
+    if( findFile( tempPath, DEFAULT_XML_FILE, xmlFile ) )
     {
-        msg_Err( getIntf(), "%s doesn't contain a " DEFAULT_XML_FILE " file",
-                 fileName.c_str() );
-        deleteTempFiles( tempPath );
-        return false;
+        path = getFilePath( xmlFile );
+    }
+    else
+    {
+        // No XML file, check if it is a winamp2 skin
+        string mainBmp;
+        if( findFile( tempPath, "main.bmp", mainBmp ) )
+        {
+            msg_Dbg( getIntf(), "Try to load a winamp2 skin" );
+            path = getFilePath( mainBmp );
+
+            // Look for winamp2.xml in the resource path
+            list<string> resPath = pOsFactory->getResourcePath();
+            list<string>::const_iterator it;
+            for( it = resPath.begin(); it != resPath.end(); it++ )
+            {
+                if( findFile( *it, WINAMP2_XML_FILE, xmlFile ) )
+                    break;
+            }
+        }
+    }
+
+    if( !xmlFile.empty() )
+    {
+        // Parse the XML file
+        if (! parse( path, xmlFile ) )
+        {
+            msg_Err( getIntf(), "Error while parsing %s", xmlFile.c_str() );
+            result = false;
+        }
+    }
+    else
+    {
+        msg_Err( getIntf(), "No XML found in theme %s", fileName.c_str() );
+        result = false;
     }
 
     // Clean-up
     deleteTempFiles( tempPath );
-    return true;
+    return result;
 }
 
 
@@ -171,24 +329,11 @@ void ThemeLoader::deleteTempFiles( const string &path )
 #endif // HAVE_ZLIB_H
 
 
-bool ThemeLoader::parse( const string &xmlFile )
+bool ThemeLoader::parse( const string &path, const string &xmlFile )
 {
     // File loaded
     msg_Dbg( getIntf(), "Using skin file: %s", xmlFile.c_str() );
 
-    // Extract the path of the XML file
-    string path;
-    const string &sep = OSFactory::instance( getIntf() )->getDirSeparator();
-    string::size_type p = xmlFile.rfind( sep, xmlFile.size() );
-    if( p != string::npos )
-    {
-        path = xmlFile.substr( 0, p + 1 );
-    }
-    else
-    {
-        path = "";
-    }
-
     // Start the parser
     SkinParser parser( getIntf(), xmlFile, path );
     if( ! parser.parse() )
@@ -205,7 +350,30 @@ bool ThemeLoader::parse( const string &xmlFile )
 }
 
 
-bool ThemeLoader::findThemeFile( const string &rootDir, string &themeFilePath )
+string ThemeLoader::getFilePath( const string &rFullPath )
+{
+    OSFactory *pOsFactory = OSFactory::instance( getIntf() );
+    const string &sep = pOsFactory->getDirSeparator();
+    // Find the last separator ('/' or '\')
+    string::size_type p = rFullPath.rfind( sep, rFullPath.size() );
+    string basePath;
+    if( p != string::npos )
+    {
+        if( p < rFullPath.size() - 1)
+        {
+            basePath = rFullPath.substr( 0, p );
+        }
+        else
+        {
+            basePath = rFullPath;
+        }
+    }
+    return basePath;
+}
+
+
+bool ThemeLoader::findFile( const string &rootDir, const string &rFileName,
+                            string &themeFilePath )
 {
     // Path separator
     const string &sep = OSFactory::instance( getIntf() )->getDirSeparator();
@@ -224,7 +392,7 @@ bool ThemeLoader::findThemeFile( const string &rootDir, string &themeFilePath )
     }
 
     // Get the first directory entry
-    pDirContent = readdir( pCurrDir );
+    pDirContent = (dirent*)readdir( pCurrDir );
 
     // While we still have entries in the directory
     while( pDirContent != NULL )
@@ -245,8 +413,8 @@ bool ThemeLoader::findThemeFile( const string &rootDir, string &themeFilePath )
             if( 0 )
 #endif
             {
-                // Can we find the theme file in this subdirectory?
-                if( findThemeFile( newURI, themeFilePath ) )
+                // Can we find the file in this subdirectory?
+                if( findFile( newURI, rFileName, themeFilePath ) )
                 {
                     closedir( pCurrDir );
                     return true;
@@ -255,8 +423,7 @@ bool ThemeLoader::findThemeFile( const string &rootDir, string &themeFilePath )
             else
             {
                 // Found the theme file?
-                if( string( DEFAULT_XML_FILE ) ==
-                    string( pDirContent->d_name ) )
+                if( rFileName == string( pDirContent->d_name ) )
                 {
                     themeFilePath = newURI;
                     closedir( pCurrDir );
@@ -265,7 +432,7 @@ bool ThemeLoader::findThemeFile( const string &rootDir, string &themeFilePath )
             }
         }
 
-        pDirContent = readdir( pCurrDir );
+        pDirContent = (dirent*)readdir( pCurrDir );
     }
 
     closedir( pCurrDir );
@@ -275,10 +442,6 @@ bool ThemeLoader::findThemeFile( const string &rootDir, string &themeFilePath )
 
 #if !defined( HAVE_LIBTAR_H ) && defined( HAVE_ZLIB_H )
 
-#ifdef WIN32
-#  define mkdir(dirname,mode) _mkdir(dirname)
-#endif
-
 /* Values used in typeflag field */
 #define REGTYPE  '0'            /* regular file */
 #define AREGTYPE '\0'           /* regular file */
@@ -314,9 +477,6 @@ union tar_buffer {
 };
 
 
-/* helper functions */
-int getoct( char *p, int width );
-int makedir( char *newdir );
 
 int tar_open( TAR **t, char *pathname, int oflags )
 {
@@ -478,6 +638,11 @@ int getoct( char *p, int width )
     return result;
 }
 
+#endif
+
+#ifdef WIN32
+#  define mkdir(dirname,mode) _mkdir(dirname)
+#endif
 
 /* Recursive make directory
  * Abort if you get an ENOENT errno somewhere in the middle
@@ -485,7 +650,7 @@ int getoct( char *p, int width )
  *
  * return 1 if OK, 0 on error
  */
-int makedir( char *newdir )
+int makedir( const char *newdir )
 {
     char *p, *buffer = strdup( newdir );
     int  len = strlen( buffer );
@@ -527,15 +692,18 @@ int makedir( char *newdir )
     free( buffer );
     return 1;
 }
-#endif
 
 #ifdef HAVE_ZLIB_H
+
+static int currentGzFd = -1;
+static void * currentGzVp = NULL;
+
 int gzopen_frontend( char *pathname, int oflags, int mode )
 {
     char *gzflags;
     gzFile gzf;
 
-    switch( oflags & O_ACCMODE )
+    switch( oflags )
     {
         case O_WRONLY:
             gzflags = "wb";
@@ -556,6 +724,40 @@ int gzopen_frontend( char *pathname, int oflags, int mode )
         return -1;
     }
 
-    return (int)gzf;
+    /** Hum ... */
+    currentGzFd = 42;
+    currentGzVp = gzf;
+
+    return currentGzFd;
 }
+
+int gzclose_frontend( int fd )
+{
+    if( currentGzVp != NULL && fd != -1 )
+    {
+        void *toClose = currentGzVp;
+        currentGzVp = NULL;  currentGzFd = -1;
+        return gzclose( toClose );
+    }
+    return -1;
+}
+
+int gzread_frontend( int fd, void *p_buffer, size_t i_length )
+{
+    if( currentGzVp != NULL && fd != -1 )
+    {
+        return gzread( currentGzVp, p_buffer, i_length );
+    }
+    return -1;
+}
+
+int gzwrite_frontend( int fd, const void * p_buffer, size_t i_length )
+{
+    if( currentGzVp != NULL && fd != -1 )
+    {
+        return gzwrite( currentGzVp, const_cast<void*>(p_buffer), i_length );
+    }
+    return -1;
+}
+
 #endif