skins2: fix RadialSlider
[vlc/asuraparaju-public.git] / modules / gui / skins2 / src / theme_loader.cpp
blobd9ae6a44a11dd44d6b04893c57698df9ee3ffcb4
1 /*****************************************************************************
2 * theme_loader.cpp
3 *****************************************************************************
4 * Copyright (C) 2003 the VideoLAN team
5 * $Id$
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"
26 #include "theme.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"
33 #ifdef HAVE_FCNTL_H
34 # include <fcntl.h>
35 #endif
36 #ifdef HAVE_SYS_STAT_H
37 # include <sys/stat.h>
38 #endif
39 #ifdef HAVE_UNISTD_H
40 # include <unistd.h>
41 #elif defined( WIN32 ) && !defined( UNDER_CE )
42 # include <direct.h>
43 #endif
45 #ifdef HAVE_DIRENT_H
46 # include <dirent.h>
47 #endif
50 #if defined( HAVE_ZLIB_H )
51 # include <zlib.h>
52 # include <errno.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 )
58 # include <libtar.h>
59 #else
60 typedef gzFile TAR;
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 );
65 #endif
66 int makedir( const char *newdir );
67 #endif
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
79 struct stat p_stat;
80 if( vlc_stat( fileName.c_str(), &p_stat ) )
81 return false;
83 // First, we try to un-targz the file, and if it fails we hope it's a XML
84 // file...
86 #if defined( HAVE_ZLIB_H )
87 if( ! extract( sToLocale( fileName ) ) && ! parse( path, fileName ) )
88 return false;
89 #else
90 if( ! parse( path, fileName ) )
91 return false;
92 #endif
94 Theme *pNewTheme = getIntf()->p_sys->p_theme;
95 if( !pNewTheme )
97 return false;
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();
109 else
111 config_PutPsz( getIntf(), "skins2-last", fileName.c_str() );
112 // Show the windows
113 pNewTheme->getWindowManager().showAll( true );
115 free( skin_last );
117 return true;
121 #if defined( HAVE_ZLIB_H )
122 bool ThemeLoader::extractTarGz( const string &tarFile, const string &rootDir )
124 TAR *t;
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,
132 TAR_GNU ) == -1 )
133 #else
134 if( tar_open( &t, (char *)tarFile.c_str(), O_RDONLY ) == -1 )
135 #endif
137 return false;
140 if( tar_extract_all( t, (char *)rootDir.c_str() ) != 0 )
142 tar_close( t );
143 return false;
146 if( tar_close( t ) != 0 )
148 return false;
151 return true;
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 )
163 return false;
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",
171 zipFile.c_str() );
172 unzClose( file );
173 return false;
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",
182 zipFile.c_str() );
183 unzClose( file );
184 return false;
188 unzClose( file );
189 return true;
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 )
200 != UNZ_OK )
202 return false;
205 #ifdef WIN32
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] );
214 #endif
216 // Allocate the buffer
217 void *pBuffer = malloc( ZIP_BUFFER_SIZE );
218 if( !pBuffer )
219 return false;
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 ) )
233 free( pBuffer );
234 return false;
236 makedir( basePath.c_str() );
237 FILE *fout = fopen( fullPath.c_str(), "wb" );
238 if( fout == NULL )
240 msg_Err( getIntf(), "error opening %s", fullPath.c_str() );
241 free( pBuffer );
242 return false;
245 // Extract the current file
246 int n;
249 n = unzReadCurrentFile( file, pBuffer, ZIP_BUFFER_SIZE );
250 if( n < 0 )
252 msg_Err( getIntf(), "error while reading zip file" );
253 free( pBuffer );
254 return false;
256 else if( n > 0 )
258 if( fwrite( pBuffer, n , 1, fout) != 1 )
260 msg_Err( getIntf(), "error while writing %s",
261 fullPath.c_str() );
262 free( pBuffer );
263 return false;
266 } while( n > 0 );
268 fclose(fout);
270 if( unzCloseCurrentFile( file ) != UNZ_OK )
272 free( pBuffer );
273 return false;
277 free( pBuffer );
278 return true;
282 bool ThemeLoader::extract( const string &fileName )
284 bool result = true;
285 char *tmpdir = tempnam( NULL, "vlt" );
286 string tempPath = sFromLocale( tmpdir );
287 free( tmpdir );
289 // Extract the file in a temporary directory
290 if( ! extractTarGz( fileName, tempPath ) &&
291 ! extractZip( fileName, tempPath ) )
293 deleteTempFiles( tempPath );
294 return false;
297 string path;
298 string xmlFile;
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 );
305 else
307 // No XML file, check if it is a winamp2 skin
308 string mainBmp;
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 ) )
320 break;
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() );
331 result = false;
334 else
336 msg_Err( getIntf(), "no XML found in theme %s", fileName.c_str() );
337 result = false;
340 // Clean-up
341 deleteTempFiles( tempPath );
342 return result;
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 )
355 // File loaded
356 msg_Dbg( getIntf(), "using skin file: %s", xmlFile.c_str() );
358 // Start the parser
359 SkinParser parser( getIntf(), xmlFile, path );
360 if( ! parser.parse() )
361 return false;
363 // Build and store the theme
364 Builder builder( getIntf(), parser.getData(), path );
365 getIntf()->p_sys->p_theme = builder.build();
367 return true;
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() );
377 string basePath;
378 if( p != string::npos )
380 if( p < rFullPath.size() - 1)
382 basePath = rFullPath.substr( 0, p );
384 else
386 basePath = rFullPath;
389 return basePath;
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 );
404 return newPath;
408 bool ThemeLoader::findFile( const string &rootDir, const string &rFileName,
409 string &themeFilePath )
411 // Path separator
412 const string &sep = OSFactory::instance( getIntf() )->getDirSeparator();
414 DIR *pCurrDir;
415 char *pszDirContent;
417 // Open the dir
418 pCurrDir = vlc_opendir( rootDir.c_str() );
420 if( pCurrDir == NULL )
422 // An error occurred
423 msg_Dbg( getIntf(), "cannot open directory %s", rootDir.c_str() );
424 return false;
427 // While we still have entries in the directory
428 while( ( pszDirContent = vlc_readdir( pCurrDir ) ) != NULL )
430 string newURI = rootDir + sep + pszDirContent;
432 // Skip . and ..
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 )
443 #else
444 if( 0 )
445 #endif
447 // Can we find the file in this subdirectory?
448 if( findFile( newURI, rFileName, themeFilePath ) )
450 free( pszDirContent );
451 closedir( pCurrDir );
452 return true;
455 else
457 // Found the theme file?
458 if( rFileName == string( pszDirContent ) )
460 themeFilePath = newURI;
461 free( pszDirContent );
462 closedir( pCurrDir );
463 return true;
468 free( pszDirContent );
471 closedir( pCurrDir );
472 return false;
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
485 struct tar_header
486 { /* byte offset */
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 */
503 /* 500 */
507 union tar_buffer {
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" );
517 if( f == NULL )
519 fprintf( stderr, "Couldn't gzopen %s\n", pathname );
520 return -1;
523 *t = (gzFile *)malloc( sizeof(gzFile) );
524 **t = f;
525 return 0;
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];
536 while( 1 )
538 len = gzread( *t, &buffer, BLOCKSIZE );
539 if( len < 0 )
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" );
551 return -1;
555 * If we have to get a tar header
557 if( getheader == 1 )
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) )
565 break;
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");
575 return -1;
578 switch( buffer.header.typeflag )
580 case DIRTYPE:
581 makedir( fname );
582 break;
583 case REGTYPE:
584 case AREGTYPE:
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, '/' );
593 if( p != NULL )
595 *p = '\0';
596 makedir( fname );
597 *p = '/';
598 outfile = fopen( fname, "wb" );
599 if( !outfile )
601 fprintf( stderr, "tar couldn't create %s\n",
602 fname );
609 * could have no contents
611 getheader = (remaining) ? 0 : 1;
612 break;
613 default:
614 break;
617 else
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 );
626 fclose( outfile );
627 outfile = NULL;
628 unlink( fname );
631 remaining -= bytes;
632 if( remaining == 0 )
634 getheader = 1;
635 if( outfile != NULL )
637 fclose(outfile);
638 outfile = NULL;
644 return 0;
648 int tar_close( TAR *t )
650 if( gzclose( *t ) != Z_OK ) fprintf( stderr, "failed gzclose\n" );
651 free( t );
652 return 0;
656 /* helper functions */
657 int getoct( char *p, int width )
659 int result = 0;
660 char c;
662 while( width-- )
664 c = *p++;
665 if( c == ' ' )
666 continue;
667 if( c == 0 )
668 break;
669 result = result * 8 + (c - '0');
671 return result;
674 #endif
676 #ifdef WIN32
677 # define mkdir(dirname,mode) _mkdir(dirname)
678 #endif
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 );
691 if( len <= 0 )
693 free( buffer );
694 return 0;
697 if( buffer[len-1] == '/' )
699 buffer[len-1] = '\0';
702 if( mkdir( buffer, 0775 ) == 0 )
704 free( buffer );
705 return 1;
708 p = buffer + 1;
709 while( 1 )
711 char hold;
713 while( *p && *p != '\\' && *p != '/' ) p++;
714 hold = *p;
715 *p = 0;
716 if( ( mkdir( buffer, 0775 ) == -1 ) && ( errno == ENOENT ) )
718 fprintf( stderr, "couldn't create directory %s\n", buffer );
719 free( buffer );
720 return 0;
722 if( hold == 0 ) break;
723 *p++ = hold;
725 free( buffer );
726 return 1;
729 #ifdef HAVE_ZLIB_H
731 static int currentGzFd = -1;
732 static void * currentGzVp = NULL;
734 int gzopen_frontend( const char *pathname, int oflags, int mode )
736 const char *gzflags;
737 gzFile gzf;
739 switch( oflags )
741 case O_WRONLY:
742 gzflags = "wb";
743 break;
744 case O_RDONLY:
745 gzflags = "rb";
746 break;
747 case O_RDWR:
748 default:
749 errno = EINVAL;
750 return -1;
753 gzf = gzopen( pathname, gzflags );
754 if( !gzf )
756 errno = ENOMEM;
757 return -1;
760 /** Hum ... */
761 currentGzFd = 42;
762 currentGzVp = gzf;
764 return currentGzFd;
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 );
775 return -1;
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 );
784 return -1;
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 );
793 return -1;
796 #endif