1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2003 the VideoLAN team
7 * Authors: Cyril Deguet <asmax@via.ecp.fr>
8 * Olivier Teulière <ipkiss@via.ecp.fr>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 #include "theme_loader.hpp"
27 #include "../parser/builder.hpp"
28 #include "../parser/skin_parser.hpp"
29 #include "../src/os_factory.hpp"
30 #include "../src/vlcproc.hpp"
31 #include "../src/window_manager.hpp"
36 #ifdef HAVE_SYS_STAT_H
37 # include <sys/stat.h>
41 #elif defined( WIN32 ) && !defined( UNDER_CE )
50 #if defined( HAVE_ZLIB_H )
53 int gzopen_frontend ( const char *pathname
, int oflags
, int mode
);
54 int gzclose_frontend( int );
55 int gzread_frontend ( int, void *, size_t );
56 int gzwrite_frontend( int, const void *, size_t );
57 #if defined( HAVE_LIBTAR_H )
61 int tar_open ( TAR
**t
, char *pathname
, int oflags
);
62 int tar_extract_all ( TAR
*t
, char *prefix
);
63 int tar_close ( TAR
*t
);
64 int getoct( char *p
, int width
);
66 int makedir( const char *newdir
);
69 #define DEFAULT_XML_FILE "theme.xml"
70 #define WINAMP2_XML_FILE "winamp2.xml"
71 #define ZIP_BUFFER_SIZE 4096
74 bool ThemeLoader::load( const string
&fileName
)
76 string path
= getFilePath( fileName
);
78 //Before all, let's see if the file is present
80 if( vlc_stat( fileName
.c_str(), &p_stat
) )
83 // First, we try to un-targz the file, and if it fails we hope it's a XML
86 #if defined( HAVE_ZLIB_H )
87 if( ! extract( sToLocale( fileName
) ) && ! parse( path
, fileName
) )
90 if( ! parse( path
, fileName
) )
94 Theme
*pNewTheme
= getIntf()->p_sys
->p_theme
;
100 // Check if the skin to load is in the config file, to load its config
101 char *skin_last
= config_GetPsz( getIntf(), "skins2-last" );
102 if( skin_last
!= NULL
&& fileName
== (string
)skin_last
)
104 // Restore the theme configuration
105 getIntf()->p_sys
->p_theme
->loadConfig();
106 // Used to anchor the windows at the beginning
107 pNewTheme
->getWindowManager().stopMove();
111 config_PutPsz( getIntf(), "skins2-last", fileName
.c_str() );
113 pNewTheme
->getWindowManager().showAll( true );
121 #if defined( HAVE_ZLIB_H )
122 bool ThemeLoader::extractTarGz( const string
&tarFile
, const string
&rootDir
)
125 #if defined( HAVE_LIBTAR_H )
126 tartype_t gztype
= { (openfunc_t
) gzopen_frontend
,
127 (closefunc_t
) gzclose_frontend
,
128 (readfunc_t
) gzread_frontend
,
129 (writefunc_t
) gzwrite_frontend
};
131 if( tar_open( &t
, (char *)tarFile
.c_str(), &gztype
, O_RDONLY
, 0,
134 if( tar_open( &t
, (char *)tarFile
.c_str(), O_RDONLY
) == -1 )
140 if( tar_extract_all( t
, (char *)rootDir
.c_str() ) != 0 )
146 if( tar_close( t
) != 0 )
155 bool ThemeLoader::extractZip( const string
&zipFile
, const string
&rootDir
)
157 // Try to open the ZIP file
158 unzFile file
= unzOpen( zipFile
.c_str() );
159 unz_global_info info
;
161 if( unzGetGlobalInfo( file
, &info
) != UNZ_OK
)
165 // Extract all the files in the archive
166 for( unsigned long i
= 0; i
< info
.number_entry
; i
++ )
168 if( !extractFileInZip( file
, rootDir
) )
170 msg_Warn( getIntf(), "error while unzipping %s",
176 if( i
< info
.number_entry
- 1 )
178 // Go the next file in the archive
179 if( unzGoToNextFile( file
) !=UNZ_OK
)
181 msg_Warn( getIntf(), "error while unzipping %s",
193 bool ThemeLoader::extractFileInZip( unzFile file
, const string
&rootDir
)
195 // Read info for the current file
196 char filenameInZip
[256];
197 unz_file_info fileInfo
;
198 if( unzGetCurrentFileInfo( file
, &fileInfo
, filenameInZip
,
199 sizeof( filenameInZip
), NULL
, 0, NULL
, 0 )
207 // Convert the file name to lower case, because some winamp skins
208 // use the wrong case...
209 for( size_t i
=0; i
< strlen( filenameInZip
); i
++)
211 filenameInZip
[i
] = tolower( filenameInZip
[i
] );
216 // Allocate the buffer
217 void *pBuffer
= malloc( ZIP_BUFFER_SIZE
);
221 // Get the path of the file
222 OSFactory
*pOsFactory
= OSFactory::instance( getIntf() );
223 string fullPath
= rootDir
224 + pOsFactory
->getDirSeparator()
225 + fixDirSeparators( filenameInZip
);
226 string basePath
= getFilePath( fullPath
);
228 // Extract the file if is not a directory
229 if( basePath
!= fullPath
)
231 if( unzOpenCurrentFile( file
) )
236 makedir( basePath
.c_str() );
237 FILE *fout
= fopen( fullPath
.c_str(), "wb" );
240 msg_Err( getIntf(), "error opening %s", fullPath
.c_str() );
245 // Extract the current file
249 n
= unzReadCurrentFile( file
, pBuffer
, ZIP_BUFFER_SIZE
);
252 msg_Err( getIntf(), "error while reading zip file" );
258 if( fwrite( pBuffer
, n
, 1, fout
) != 1 )
260 msg_Err( getIntf(), "error while writing %s",
270 if( unzCloseCurrentFile( file
) != UNZ_OK
)
282 bool ThemeLoader::extract( const string
&fileName
)
285 char *tmpdir
= tempnam( NULL
, "vlt" );
286 string tempPath
= sFromLocale( tmpdir
);
289 // Extract the file in a temporary directory
290 if( ! extractTarGz( fileName
, tempPath
) &&
291 ! extractZip( fileName
, tempPath
) )
293 deleteTempFiles( tempPath
);
299 OSFactory
*pOsFactory
= OSFactory::instance( getIntf() );
300 // Find the XML file in the theme
301 if( findFile( tempPath
, DEFAULT_XML_FILE
, xmlFile
) )
303 path
= getFilePath( xmlFile
);
307 // No XML file, check if it is a winamp2 skin
309 if( findFile( tempPath
, "main.bmp", mainBmp
) )
311 msg_Dbg( getIntf(), "trying to load a winamp2 skin" );
312 path
= getFilePath( mainBmp
);
314 // Look for winamp2.xml in the resource path
315 list
<string
> resPath
= pOsFactory
->getResourcePath();
316 list
<string
>::const_iterator it
;
317 for( it
= resPath
.begin(); it
!= resPath
.end(); it
++ )
319 if( findFile( *it
, WINAMP2_XML_FILE
, xmlFile
) )
325 if( !xmlFile
.empty() )
327 // Parse the XML file
328 if (! parse( path
, xmlFile
) )
330 msg_Err( getIntf(), "error while parsing %s", xmlFile
.c_str() );
336 msg_Err( getIntf(), "no XML found in theme %s", fileName
.c_str() );
341 deleteTempFiles( tempPath
);
346 void ThemeLoader::deleteTempFiles( const string
&path
)
348 OSFactory::instance( getIntf() )->rmDir( path
);
350 #endif // HAVE_ZLIB_H
353 bool ThemeLoader::parse( const string
&path
, const string
&xmlFile
)
356 msg_Dbg( getIntf(), "using skin file: %s", xmlFile
.c_str() );
359 SkinParser
parser( getIntf(), xmlFile
, path
);
360 if( ! parser
.parse() )
363 // Build and store the theme
364 Builder
builder( getIntf(), parser
.getData(), path
);
365 getIntf()->p_sys
->p_theme
= builder
.build();
371 string
ThemeLoader::getFilePath( const string
&rFullPath
)
373 OSFactory
*pOsFactory
= OSFactory::instance( getIntf() );
374 const string
&sep
= pOsFactory
->getDirSeparator();
375 // Find the last separator ('/' or '\')
376 string::size_type p
= rFullPath
.rfind( sep
, rFullPath
.size() );
378 if( p
!= string::npos
)
380 if( p
< rFullPath
.size() - 1)
382 basePath
= rFullPath
.substr( 0, p
);
386 basePath
= rFullPath
;
393 string
ThemeLoader::fixDirSeparators( const string
&rPath
)
395 OSFactory
*pOsFactory
= OSFactory::instance( getIntf() );
396 const string
&sep
= pOsFactory
->getDirSeparator();
397 string::size_type p
= rPath
.find( "/", 0 );
398 string newPath
= rPath
;
399 while( p
!= string::npos
)
401 newPath
= newPath
.replace( p
, 1, sep
);
402 p
= newPath
.find( "/", p
+ 1 );
408 bool ThemeLoader::findFile( const string
&rootDir
, const string
&rFileName
,
409 string
&themeFilePath
)
412 const string
&sep
= OSFactory::instance( getIntf() )->getDirSeparator();
418 pCurrDir
= vlc_opendir( rootDir
.c_str() );
420 if( pCurrDir
== NULL
)
423 msg_Dbg( getIntf(), "cannot open directory %s", rootDir
.c_str() );
427 // While we still have entries in the directory
428 while( ( pszDirContent
= vlc_readdir( pCurrDir
) ) != NULL
)
430 string newURI
= rootDir
+ sep
+ pszDirContent
;
433 if( string( pszDirContent
) != "." &&
434 string( pszDirContent
) != ".." )
436 #if defined( S_ISDIR )
437 struct stat stat_data
;
439 if( ( vlc_stat( newURI
.c_str(), &stat_data
) == 0 )
440 && S_ISDIR(stat_data
.st_mode
) )
441 #elif defined( DT_DIR )
442 if( pDirContent
->d_type
& DT_DIR
)
447 // Can we find the file in this subdirectory?
448 if( findFile( newURI
, rFileName
, themeFilePath
) )
450 free( pszDirContent
);
451 closedir( pCurrDir
);
457 // Found the theme file?
458 if( rFileName
== string( pszDirContent
) )
460 themeFilePath
= newURI
;
461 free( pszDirContent
);
462 closedir( pCurrDir
);
468 free( pszDirContent
);
471 closedir( pCurrDir
);
476 #if !defined( HAVE_LIBTAR_H ) && defined( HAVE_ZLIB_H )
478 /* Values used in typeflag field */
479 #define REGTYPE '0' /* regular file */
480 #define AREGTYPE '\0' /* regular file */
481 #define DIRTYPE '5' /* directory */
483 #define BLOCKSIZE 512
487 char name
[100]; /* 0 */
488 char mode
[8]; /* 100 */
489 char uid
[8]; /* 108 */
490 char gid
[8]; /* 116 */
491 char size
[12]; /* 124 */
492 char mtime
[12]; /* 136 */
493 char chksum
[8]; /* 148 */
494 char typeflag
; /* 156 */
495 char linkname
[100]; /* 157 */
496 char magic
[6]; /* 257 */
497 char version
[2]; /* 263 */
498 char uname
[32]; /* 265 */
499 char gname
[32]; /* 297 */
500 char devmajor
[8]; /* 329 */
501 char devminor
[8]; /* 337 */
502 char prefix
[155]; /* 345 */
508 char buffer
[BLOCKSIZE
];
509 struct tar_header header
;
514 int tar_open( TAR
**t
, char *pathname
, int oflags
)
516 gzFile f
= gzopen( pathname
, "rb" );
519 fprintf( stderr
, "Couldn't gzopen %s\n", pathname
);
523 *t
= (gzFile
*)malloc( sizeof(gzFile
) );
529 int tar_extract_all( TAR
*t
, char *prefix
)
531 union tar_buffer buffer
;
532 int len
, err
, getheader
= 1, remaining
= 0;
533 FILE *outfile
= NULL
;
534 char fname
[BLOCKSIZE
+ PATH_MAX
];
538 len
= gzread( *t
, &buffer
, BLOCKSIZE
);
541 fprintf( stderr
, "%s\n", gzerror(*t
, &err
) );
545 * Always expect complete blocks to process
546 * the tar information.
548 if( len
!= 0 && len
!= BLOCKSIZE
)
550 fprintf( stderr
, "gzread: incomplete block read\n" );
555 * If we have to get a tar header
560 * If we met the end of the tar
561 * or the end-of-tar block, we are done
563 if( (len
== 0) || (buffer
.header
.name
[0] == 0) )
568 sprintf( fname
, "%s/%s", prefix
, buffer
.header
.name
);
570 /* Check magic value in header */
571 if( strncmp( buffer
.header
.magic
, "GNUtar", 6 ) &&
572 strncmp( buffer
.header
.magic
, "ustar", 5 ) )
574 //fprintf(stderr, "not a tar file\n");
578 switch( buffer
.header
.typeflag
)
585 remaining
= getoct( buffer
.header
.size
, 12 );
586 if( !remaining
) outfile
= NULL
; else
588 outfile
= fopen( fname
, "wb" );
589 if( outfile
== NULL
)
591 /* try creating directory */
592 char *p
= strrchr( fname
, '/' );
598 outfile
= fopen( fname
, "wb" );
601 fprintf( stderr
, "tar couldn't create %s\n",
609 * could have no contents
611 getheader
= (remaining
) ? 0 : 1;
619 unsigned int bytes
= (remaining
> BLOCKSIZE
)?BLOCKSIZE
:remaining
;
621 if( outfile
!= NULL
)
623 if( fwrite( &buffer
, sizeof(char), bytes
, outfile
) != bytes
)
625 fprintf( stderr
, "error writing %s skipping...\n", fname
);
635 if( outfile
!= NULL
)
648 int tar_close( TAR
*t
)
650 if( gzclose( *t
) != Z_OK
) fprintf( stderr
, "failed gzclose\n" );
656 /* helper functions */
657 int getoct( char *p
, int width
)
669 result
= result
* 8 + (c
- '0');
677 # define mkdir(dirname,mode) _mkdir(dirname)
680 /* Recursive make directory
681 * Abort if you get an ENOENT errno somewhere in the middle
682 * e.g. ignore error "mkdir on existing directory"
684 * return 1 if OK, 0 on error
686 int makedir( const char *newdir
)
688 char *p
, *buffer
= strdup( newdir
);
689 int len
= strlen( buffer
);
697 if( buffer
[len
-1] == '/' )
699 buffer
[len
-1] = '\0';
702 if( mkdir( buffer
, 0775 ) == 0 )
713 while( *p
&& *p
!= '\\' && *p
!= '/' ) p
++;
716 if( ( mkdir( buffer
, 0775 ) == -1 ) && ( errno
== ENOENT
) )
718 fprintf( stderr
, "couldn't create directory %s\n", buffer
);
722 if( hold
== 0 ) break;
731 static int currentGzFd
= -1;
732 static void * currentGzVp
= NULL
;
734 int gzopen_frontend( const char *pathname
, int oflags
, int mode
)
753 gzf
= gzopen( pathname
, gzflags
);
767 int gzclose_frontend( int fd
)
769 if( currentGzVp
!= NULL
&& fd
!= -1 )
771 void *toClose
= currentGzVp
;
772 currentGzVp
= NULL
; currentGzFd
= -1;
773 return gzclose( toClose
);
778 int gzread_frontend( int fd
, void *p_buffer
, size_t i_length
)
780 if( currentGzVp
!= NULL
&& fd
!= -1 )
782 return gzread( currentGzVp
, p_buffer
, i_length
);
787 int gzwrite_frontend( int fd
, const void * p_buffer
, size_t i_length
)
789 if( currentGzVp
!= NULL
&& fd
!= -1 )
791 return gzwrite( currentGzVp
, const_cast<void*>(p_buffer
), i_length
);