]> git.sesse.net Git - vlc/commitdiff
* include/*,src/*: New plugins cache feature for faster load times.
authorGildas Bazin <gbazin@videolan.org>
Sat, 29 May 2004 21:41:57 +0000 (21:41 +0000)
committerGildas Bazin <gbazin@videolan.org>
Sat, 29 May 2004 21:41:57 +0000 (21:41 +0000)
   This is still experimental so isn't enabled by default (use --plugins-cache).
   There are also a few issues that aren't dealt with yet:
     - translation of module strings.
     - config options callbacks and actions.
     - a few memory leaks.
     - and likely other things ;)
   You can easily reset the plugins cache with --reset-plugins-cache.

include/modules.h
include/vlc_common.h
include/vlc_config.h
src/libvlc.c
src/libvlc.h
src/misc/configuration.c
src/misc/modules.c

index 749343873349e5c3e2661794976fc2d9f89eaaa4..e07f0cf9e779177e825173b3d41dc1c13538ee0f 100644 (file)
@@ -55,6 +55,17 @@ struct module_bank_t
     VLC_COMMON_MEMBERS
 
     module_symbols_t symbols;
+
+    /* Plugins cache */
+    vlc_bool_t     b_cache;
+    vlc_bool_t     b_cache_dirty;
+    vlc_bool_t     b_cache_delete;
+
+    int            i_cache;
+    module_cache_t **pp_cache;
+
+    int            i_loaded_cache;
+    module_cache_t **pp_loaded_cache;
 };
 
 /*****************************************************************************
@@ -104,6 +115,7 @@ struct module_t
     char *              psz_filename;                     /* Module filename */
 
     vlc_bool_t          b_builtin;  /* Set to true if the module is built in */
+    vlc_bool_t          b_loaded;        /* Set to true if the dll is loaded */
 
     /*
      * Symbol table we send to the module so that it can access vlc symbols
@@ -111,6 +123,21 @@ struct module_t
     module_symbols_t *p_symbols;
 };
 
+/*****************************************************************************
+ * Module cache description structure
+ *****************************************************************************/
+struct module_cache_t
+{
+    /* Mandatory cache entry header */
+    char       *psz_file;
+    int64_t    i_time;
+    int64_t    i_size;
+    vlc_bool_t b_junk;
+
+    /* Optional extra data */
+    module_t *p_module;
+};
+
 /*****************************************************************************
  * Exported functions.
  *****************************************************************************/
index 9702491b78a4cb222f44cccb52a905e3765ca792..e5cd07deb6ea9f2f51792b563785d06f184a947d 100644 (file)
@@ -199,6 +199,7 @@ typedef struct module_bank_t module_bank_t;
 typedef struct module_t module_t;
 typedef struct module_config_t module_config_t;
 typedef struct module_symbols_t module_symbols_t;
+typedef struct module_cache_t module_cache_t;
 
 /* Interface */
 typedef struct intf_thread_t intf_thread_t;
index 62785cbd72614fc02fd255cd3c667c887aecfd51..ecce507a2f28ec4d98cd36b9065d8be175a2d694 100644 (file)
@@ -58,6 +58,7 @@
 #  define CONFIG_DIR                    ".vlc"
 #endif
 #define CONFIG_FILE                     "vlcrc"
+#define PLUGINSCACHE_FILE               "vlcplugins"
 
 /*****************************************************************************
  * Interface configuration
index f2425b8e88716dc27c9935bc03c8335b64682622..cdcfd52c2e03a489129047005afe99fea6378bb5 100644 (file)
@@ -340,6 +340,12 @@ int VLC_Init( int i_object, int i_argc, char *ppsz_argv[] )
     p_vlc->psz_homedir = config_GetHomeDir();
     p_vlc->psz_configfile = config_GetPsz( p_vlc, "config" );
 
+    /* Check for plugins cache options */
+    if( config_GetInt( p_vlc, "reset-plugins-cache" ) )
+    {
+        libvlc.p_module_bank->b_cache_delete = VLC_TRUE;
+    }
+
     /* Hack: remove the help module here */
     vlc_object_detach( p_help_module );
     /* End hack */
@@ -729,6 +735,11 @@ int VLC_Destroy( int i_object )
         p_vlc->p_memcpy_module = NULL;
     }
 
+    /*
+     * XXX: Free module bank !
+     */
+    module_EndBank( p_vlc );
+
     if( p_vlc->psz_homedir )
     {
         free( p_vlc->psz_homedir );
@@ -747,11 +758,6 @@ int VLC_Destroy( int i_object )
         p_vlc->p_hotkeys = NULL;
     }
 
-    /*
-     * XXX: Free module bank !
-     */
-    /*module_EndBank( p_vlc );*/
-
     /*
      * System specific cleaning code
      */
index ab895c9c80d34ac9549c97fa22cb1c0f88c13533..67f497388e1cdc739058f409d9be81a29a68ace9 100644 (file)
@@ -86,16 +86,6 @@ static char *ppsz_language_text[] =
     "show all the available options, including those that most users should " \
     "never touch.")
 
-#define INTF_PATH_TEXT N_("Interface default search path")
-#define INTF_PATH_LONGTEXT N_( \
-    "This option allows you to set the default path that the interface will " \
-    "open when looking for a file.")
-
-#define PLUGIN_PATH_TEXT N_("Modules search path")
-#define PLUGIN_PATH_LONGTEXT N_( \
-    "This option allows you to specify an additional path for VLC to look " \
-    "for its modules.")
-
 #define AOUT_CAT_LONGTEXT N_( \
     "These options allow you to modify the behavior of the audio " \
     "subsystem, and to add audio filters which can be used for " \
@@ -586,10 +576,20 @@ static char *ppsz_align_descriptions[] =
     "priorities. You can use it to tune VLC priority against other " \
     "programs, or against other VLC instances.")
 
-#define MINIMIZE_THREADS_TXT N_("Minimize number of threads")
-#define MINIMIZE_THREADS_LONGTXT N_( \
+#define MINIMIZE_THREADS_TEXT N_("Minimize number of threads")
+#define MINIMIZE_THREADS_LONGTEXT N_( \
      "This option minimizes the number of threads needed to run VLC")
 
+#define PLUGIN_PATH_TEXT N_("Modules search path")
+#define PLUGIN_PATH_LONGTEXT N_( \
+    "This option allows you to specify an additional path for VLC to look " \
+    "for its modules.")
+
+#define PLUGINS_CACHE_TEXT N_("Use a plugins cache")
+#define PLUGINS_CACHE_LONGTEXT N_( \
+    "This option allows you to use a plugins cache which will greatly " \
+    "improve the start time of VLC.")
+
 #define ONEINSTANCE_TEXT N_("Allow only one running instance")
 #define ONEINSTANCE_LONGTEXT N_( \
     "Allowing only one running instance of VLC can sometimes be useful, " \
@@ -767,10 +767,6 @@ vlc_module_begin();
     add_bool( "color", 0, NULL, COLOR_TEXT, COLOR_LONGTEXT, VLC_TRUE );
     add_bool( "advanced", 0, NULL, ADVANCED_TEXT,
                             ADVANCED_LONGTEXT, VLC_FALSE );
-    add_directory( "search-path", NULL, NULL, INTF_PATH_TEXT,
-                            INTF_PATH_LONGTEXT, VLC_TRUE );
-    add_directory( "plugin-path", NULL, NULL,
-                PLUGIN_PATH_TEXT, PLUGIN_PATH_LONGTEXT, VLC_TRUE );
 
     /* Audio options */
     add_category_hint( N_("Audio"), AOUT_CAT_LONGTEXT , VLC_FALSE );
@@ -956,25 +952,38 @@ vlc_module_begin();
 
     /* Misc options */
     add_category_hint( N_("Miscellaneous"), MISC_CAT_LONGTEXT, VLC_TRUE );
-    add_module( "memcpy", "memcpy", NULL, NULL, MEMCPY_TEXT, MEMCPY_LONGTEXT, VLC_TRUE );
-    add_module( "access", "access", NULL, NULL, ACCESS_TEXT, ACCESS_LONGTEXT, VLC_TRUE );
-    add_module( "demux", "demux", NULL, NULL, DEMUX_TEXT, DEMUX_LONGTEXT, VLC_TRUE );
-
-    add_bool( "minimize-threads", 0, NULL, MINIMIZE_THREADS_TXT, MINIMIZE_THREADS_LONGTXT, VLC_TRUE );
+    add_module( "memcpy", "memcpy", NULL, NULL, MEMCPY_TEXT,
+                MEMCPY_LONGTEXT, VLC_TRUE );
+    add_module( "access", "access", NULL, NULL, ACCESS_TEXT,
+                ACCESS_LONGTEXT, VLC_TRUE );
+    add_module( "demux", "demux", NULL, NULL, DEMUX_TEXT,
+                DEMUX_LONGTEXT, VLC_TRUE );
+    add_bool( "minimize-threads", 0, NULL, MINIMIZE_THREADS_TEXT,
+              MINIMIZE_THREADS_LONGTEXT, VLC_TRUE );
+    add_directory( "plugin-path", NULL, NULL, PLUGIN_PATH_TEXT,
+                   PLUGIN_PATH_LONGTEXT, VLC_TRUE );
+    add_bool( "plugins-cache", 0, NULL, PLUGINS_CACHE_TEXT,
+              PLUGINS_CACHE_LONGTEXT, VLC_TRUE );
 
 #if !defined(SYS_DARWIN) && !defined(SYS_BEOS) && defined(PTHREAD_COND_T_IN_PTHREAD_H)
-    add_bool( "rt-priority", 0, NULL, RT_PRIORITY_TEXT, RT_PRIORITY_LONGTEXT, VLC_TRUE );
+    add_bool( "rt-priority", 0, NULL, RT_PRIORITY_TEXT,
+              RT_PRIORITY_LONGTEXT, VLC_TRUE );
 #endif
 
 #if !defined(SYS_BEOS) && defined(PTHREAD_COND_T_IN_PTHREAD_H)
-    add_integer( "rt-offset", 0, NULL, RT_OFFSET_TEXT, RT_OFFSET_LONGTEXT, VLC_TRUE );
+    add_integer( "rt-offset", 0, NULL, RT_OFFSET_TEXT,
+                 RT_OFFSET_LONGTEXT, VLC_TRUE );
 #endif
 
 #if defined(WIN32)
-    add_bool( "one-instance", 0, NULL, ONEINSTANCE_TEXT, ONEINSTANCE_LONGTEXT, VLC_TRUE );
-    add_bool( "high-priority", 0, NULL, HPRIORITY_TEXT, HPRIORITY_LONGTEXT, VLC_TRUE );
-    add_bool( "fast-mutex", 0, NULL, FAST_MUTEX_TEXT, FAST_MUTEX_LONGTEXT, VLC_TRUE );
-    add_integer( "win9x-cv-method", 1, NULL, WIN9X_CV_TEXT, WIN9X_CV_LONGTEXT, VLC_TRUE );
+    add_bool( "one-instance", 0, NULL, ONEINSTANCE_TEXT,
+              ONEINSTANCE_LONGTEXT, VLC_TRUE );
+    add_bool( "high-priority", 0, NULL, HPRIORITY_TEXT,
+              HPRIORITY_LONGTEXT, VLC_TRUE );
+    add_bool( "fast-mutex", 0, NULL, FAST_MUTEX_TEXT,
+              FAST_MUTEX_LONGTEXT, VLC_TRUE );
+    add_integer( "win9x-cv-method", 1, NULL, WIN9X_CV_TEXT,
+                  WIN9X_CV_LONGTEXT, VLC_TRUE );
 #endif
 
     /* Hotkey options*/
@@ -1108,6 +1117,8 @@ static module_config_t p_help_config[] =
       N_("reset the current config to the default values") },
     { CONFIG_ITEM_STRING, NULL, "config", '\0',
       N_("use alternate config file") },
+    { CONFIG_ITEM_BOOL, NULL, "reset-plugins-cache", '\0',
+      N_("resets the current plugins cache") },
     { CONFIG_ITEM_BOOL, NULL, "version", '\0',
       N_("print version information") },
     { CONFIG_HINT_END, NULL, NULL, '\0', NULL }
index 6e62f1fa33c539fea5678e684614a1608aaf7e48..d06b7ac7b1f15665e8bb2788dce94daa4d86f129 100644 (file)
@@ -2,9 +2,9 @@
  * configuration.c management of the modules configuration
  *****************************************************************************
  * Copyright (C) 2001-2004 VideoLAN
- * $Id: configuration.c,v 1.76 2004/01/29 17:04:01 gbazin Exp $
+ * $Id$
  *
- * Authors: Gildas Bazin <gbazin@netcourrier.com>
+ * Authors: Gildas Bazin <gbazin@videolan.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -909,6 +909,45 @@ int __config_LoadConfigFile( vlc_object_t *p_this, const char *psz_module_name )
     return 0;
 }
 
+/*****************************************************************************
+ * config_CreateConfigDir: Create configuration directory if it doesn't exist.
+ *****************************************************************************/
+int config_CreateDir( vlc_object_t *p_this, char *psz_dirname )
+{
+    if( !psz_dirname && !*psz_dirname ) return -1;
+
+#if defined( UNDER_CE )
+    {
+        wchar_t psz_new[ MAX_PATH ];
+        MultiByteToWideChar( CP_ACP, 0, psz_dirname, -1, psz_new, MAX_PATH );
+        if( CreateDirectory( psz_new, NULL ) )
+        {
+            msg_Err( p_this, "could not create %s", psz_filename );
+        }
+    }
+
+#elif defined( HAVE_ERRNO_H )
+#   if defined( WIN32 )
+    if( mkdir( psz_dirname ) && errno != EEXIST )
+#   else
+    if( mkdir( psz_dirname, 0755 ) && errno != EEXIST )
+#   endif
+    {
+        msg_Err( p_this, "could not create %s (%s)",
+                 psz_dirname, strerror(errno) );
+    }
+
+#else
+    if( mkdir( psz_dirname ) )
+    {
+        msg_Err( p_this, "could not create %s", psz_dirname );
+    }
+
+#endif
+
+    return 0;
+}
+
 /*****************************************************************************
  * config_SaveConfigFile: Save a module's config options.
  *****************************************************************************
@@ -967,35 +1006,7 @@ int __config_SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name )
             return -1;
         }
 
-#if defined( UNDER_CE )
-        {
-            wchar_t psz_new[ MAX_PATH ];
-            MultiByteToWideChar( CP_ACP, 0, psz_filename, -1, psz_new,
-                                 MAX_PATH );
-            if( CreateDirectory( psz_new, NULL ) )
-            {
-                msg_Err( p_this, "could not create %s", psz_filename );
-            }
-        }
-
-#elif defined( HAVE_ERRNO_H )
-#   if defined( WIN32 )
-        if( mkdir( psz_filename ) && errno != EEXIST )
-#   else
-        if( mkdir( psz_filename, 0755 ) && errno != EEXIST )
-#   endif
-        {
-            msg_Err( p_this, "could not create %s (%s)",
-                             psz_filename, strerror(errno) );
-        }
-
-#else
-        if( mkdir( psz_filename ) )
-        {
-            msg_Err( p_this, "could not create %s", psz_filename );
-        }
-
-#endif
+        config_CreateDir( p_this, psz_filename );
 
         strcat( psz_filename, "/" CONFIG_FILE );
     }
index 0dd7c3d9374b603f675489fcc8b8e102d9a84c9b..80875b984bb811bf7a7e2ff2d74c5ae15a5f72d9 100644 (file)
@@ -7,6 +7,7 @@
  * Authors: Sam Hocevar <sam@zoy.org>
  *          Ethan C. Baldridge <BaldridgeE@cadmus.com>
  *          Hans-Peter Jansen <hpj@urpla.net>
+ *          Gildas Bazin <gbazin@videolan.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * Local prototypes
  *****************************************************************************/
 #ifdef HAVE_DYNAMIC_PLUGINS
-static void AllocateAllPlugins   ( vlc_object_t * );
-static void AllocatePluginDir    ( vlc_object_t *, const MYCHAR *, int );
-static int  AllocatePluginFile   ( vlc_object_t *, MYCHAR * );
+static void AllocateAllPlugins  ( vlc_object_t * );
+static void AllocatePluginDir   ( vlc_object_t *, const char *, int );
+static int  AllocatePluginFile  ( vlc_object_t *, char *, int64_t, int64_t );
+static module_t * AllocatePlugin( vlc_object_t *, char * );
 #endif
 static int  AllocateBuiltinModule( vlc_object_t *, int ( * ) ( module_t * ) );
 static int  DeleteModule ( module_t * );
@@ -143,8 +145,16 @@ static int  DeleteModule ( module_t * );
 static void   DupModule        ( module_t * );
 static void   UndupModule      ( module_t * );
 static int    CallEntry        ( module_t * );
+static int    LoadModule       ( vlc_object_t *, char *, module_handle_t * );
 static void   CloseModule      ( module_handle_t );
 static void * GetSymbol        ( module_handle_t, const char * );
+static void   CacheLoad        ( vlc_object_t * );
+static void   CacheLoadConfig  ( module_t *, FILE * );
+static void   CacheSave        ( vlc_object_t * );
+static void   CacheSaveConfig  ( module_t *, FILE * );
+static void   CacheMerge       ( vlc_object_t *, module_t *, module_t * );
+static module_cache_t * CacheFind( vlc_object_t *, char *, int64_t, int64_t );
+
 #if defined(HAVE_DL_WINDOWS)
 static char * GetWindowsError  ( void );
 #endif
@@ -162,6 +172,10 @@ void __module_InitBank( vlc_object_t *p_this )
 
     p_bank = vlc_object_create( p_this, sizeof(module_bank_t) );
     p_bank->psz_object_name = "module bank";
+    p_bank->i_cache = p_bank->i_loaded_cache = 0;
+    p_bank->pp_cache = p_bank->pp_loaded_cache = 0;
+    p_bank->b_cache = p_bank->b_cache_dirty =
+        p_bank->b_cache_delete = VLC_FALSE;
 
     /*
      * Store the symbols to be exported
@@ -199,6 +213,10 @@ void __module_EndBank( vlc_object_t *p_this )
 {
     module_t * p_next;
 
+#ifdef HAVE_DYNAMIC_PLUGINS
+    if( p_this->p_libvlc->p_module_bank->b_cache ) CacheSave( p_this );
+#endif
+
     vlc_object_detach( p_this->p_libvlc->p_module_bank );
 
     while( p_this->p_libvlc->p_module_bank->i_children )
@@ -255,6 +273,13 @@ void __module_LoadPlugins( vlc_object_t * p_this )
 {
 #ifdef HAVE_DYNAMIC_PLUGINS
     msg_Dbg( p_this, "checking plugin modules" );
+
+    if( config_GetInt( p_this, "plugins-cache" ) )
+        p_this->p_libvlc->p_module_bank->b_cache = VLC_TRUE;
+
+    if( p_this->p_libvlc->p_module_bank->b_cache ||
+        p_this->p_libvlc->p_module_bank->b_cache_delete ) CacheLoad( p_this );
+
     AllocateAllPlugins( p_this );
 #endif
 }
@@ -516,6 +541,23 @@ module_t * __module_Need( vlc_object_t *p_this, const char *psz_capability,
     p_tmp = p_first;
     while( p_tmp != NULL )
     {
+#ifdef HAVE_DYNAMIC_PLUGINS
+        /* Make sure the module is loaded in mem */
+        module_t *p_module = p_tmp->p_module->b_submodule ?
+            (module_t *)p_tmp->p_module->p_parent : p_tmp->p_module;
+        if( !p_module->b_builtin && !p_module->b_loaded )
+        {
+            module_t *p_new_module =
+                AllocatePlugin( p_this, p_module->psz_filename );
+            if( p_new_module )
+            {
+                CacheMerge( p_this, p_module, p_new_module );
+                vlc_object_attach( p_new_module, p_module );
+                DeleteModule( p_new_module );
+            }
+        }
+#endif
+
         if( p_tmp->p_module->pf_activate
              && p_tmp->p_module->pf_activate( p_this ) == VLC_SUCCESS )
         {
@@ -614,13 +656,9 @@ static void AllocateAllPlugins( vlc_object_t *p_this )
     char **         ppsz_path = path;
     char *          psz_fullpath;
 
-#if defined( UNDER_CE )
-    wchar_t         psz_dir[MAX_PATH];
-#endif
-
     /* If the user provided a plugin path, we add it to the list */
-    path[ sizeof(path)/sizeof(char*) - 2 ] = config_GetPsz( p_this,
-                                                            "plugin-path" );
+    path[ sizeof(path)/sizeof(char*) - 2 ] =
+        config_GetPsz( p_this, "plugin-path" );
 
     for( ; *ppsz_path != NULL ; ppsz_path++ )
     {
@@ -659,12 +697,7 @@ static void AllocateAllPlugins( vlc_object_t *p_this )
         msg_Dbg( p_this, "recursively browsing `%s'", psz_fullpath );
 
         /* Don't go deeper than 5 subdirectories */
-#if defined( UNDER_CE )
-        MultiByteToWideChar( CP_ACP, 0, psz_fullpath, -1, psz_dir, MAX_PATH );
-        AllocatePluginDir( p_this, psz_dir, 5 );
-#else
         AllocatePluginDir( p_this, psz_fullpath, 5 );
-#endif
 
         free( psz_fullpath );
     }
@@ -678,12 +711,13 @@ static void AllocateAllPlugins( vlc_object_t *p_this )
 /*****************************************************************************
  * AllocatePluginDir: recursively parse a directory to look for plugins
  *****************************************************************************/
-static void AllocatePluginDir( vlc_object_t *p_this, const MYCHAR *psz_dir,
+static void AllocatePluginDir( vlc_object_t *p_this, const char *psz_dir,
                                int i_maxdepth )
 {
 #if defined( UNDER_CE ) || defined( _MSC_VER )
 #ifdef UNDER_CE
     MYCHAR psz_path[MAX_PATH + 256];
+    wchar_t psz_wdir[MAX_PATH];
 #else
     char psz_path[MAX_PATH + 256];
 #endif
@@ -693,9 +727,9 @@ static void AllocatePluginDir( vlc_object_t *p_this, const MYCHAR *psz_dir,
 #else
     int    i_dirlen;
     DIR *  dir;
-    char * psz_file;
     struct dirent * file;
 #endif
+    char * psz_file;
 
     if( p_this->p_vlc->b_die || i_maxdepth < 0 )
     {
@@ -703,7 +737,9 @@ static void AllocatePluginDir( vlc_object_t *p_this, const MYCHAR *psz_dir,
     }
 
 #if defined( UNDER_CE ) || defined( _MSC_VER )
-    rc = GetFileAttributes( psz_dir );
+    MultiByteToWideChar( CP_ACP, 0, psz_dir, -1, psz_wdir, MAX_PATH );
+
+    rc = GetFileAttributes( psz_wdir );
     if( !(rc & FILE_ATTRIBUTE_DIRECTORY) )
     {
         /* Not a directory */
@@ -754,7 +790,16 @@ static void AllocatePluginDir( vlc_object_t *p_this, const MYCHAR *psz_dir,
                                    LIBEXT, strlen( LIBEXT ) ) )
 #endif
         {
-            AllocatePluginFile( p_this, psz_path );
+#ifdef UNDER_CE
+            char psz_filename[MAX_PATH];
+            WideCharToMultiByte( CP_ACP, WC_DEFAULTCHAR, psz_path, -1,
+                                 psz_filename, MAX_PATH, NULL, NULL );
+            psz_file = psz_filename;
+#else
+            psz_file = psz_path;
+#endif
+
+            AllocatePluginFile( p_this, psz_file, 0, 0 );
         }
     }
     while( !p_this->p_vlc->b_die && FindNextFile( handle, &finddata ) );
@@ -776,6 +821,7 @@ static void AllocatePluginDir( vlc_object_t *p_this, const MYCHAR *psz_dir,
     {
         struct stat statbuf;
         unsigned int i_len;
+        int i_stat;
 
         /* Skip ".", ".." and anything starting with "." */
         if( !*file->d_name || *file->d_name == '.' )
@@ -791,7 +837,8 @@ static void AllocatePluginDir( vlc_object_t *p_this, const MYCHAR *psz_dir,
         sprintf( psz_file, "%s/%s", psz_dir, file->d_name );
 #endif
 
-        if( !stat( psz_file, &statbuf ) && statbuf.st_mode & S_IFDIR )
+        i_stat = stat( psz_file, &statbuf );
+        if( !i_stat && statbuf.st_mode & S_IFDIR )
         {
             AllocatePluginDir( p_this, psz_file, i_maxdepth - 1 );
         }
@@ -800,7 +847,15 @@ static void AllocatePluginDir( vlc_object_t *p_this, const MYCHAR *psz_dir,
                   && !strncasecmp( file->d_name + i_len - strlen( LIBEXT ),
                                    LIBEXT, strlen( LIBEXT ) ) )
         {
-            AllocatePluginFile( p_this, psz_file );
+            int64_t i_time = 0, i_size = 0;
+
+            if( !i_stat )
+            {
+                i_time = statbuf.st_mtime;
+                i_size = statbuf.st_size;
+            }
+
+            AllocatePluginFile( p_this, psz_file, i_time, i_size );
         }
 
         free( psz_file );
@@ -819,143 +874,78 @@ static void AllocatePluginDir( vlc_object_t *p_this, const MYCHAR *psz_dir,
  * for its information data. The module can then be handled by module_Need
  * and module_Unneed. It can be removed by DeleteModule.
  *****************************************************************************/
-static int AllocatePluginFile( vlc_object_t * p_this, MYCHAR * psz_file )
+static int AllocatePluginFile( vlc_object_t * p_this, char * psz_file,
+                               int64_t i_file_time, int64_t i_file_size )
 {
     module_t * p_module;
-    module_handle_t handle;
+    module_cache_t *p_cache_entry = NULL;
 
     /*
-     * Try to dynamically load the module. This part is very platform-
-     * specific, and error messages should be as verbose as possible.
+     * Check our plugins cache first then load plugin if needed
      */
+    p_cache_entry =
+        CacheFind( p_this, psz_file, i_file_time, i_file_size );
 
-#if defined(HAVE_DL_DYLD)
-    NSObjectFileImage image;
-    NSObjectFileImageReturnCode ret;
-
-    ret = NSCreateObjectFileImageFromFile( psz_file, &image );
-
-    if( ret != NSObjectFileImageSuccess )
+    if( !p_cache_entry )
     {
-        msg_Warn( p_this, "cannot create image from `%s'", psz_file );
-        return -1;
+        p_module = AllocatePlugin( p_this, psz_file );
     }
-
-    /* Open the dynamic module */
-    handle = NSLinkModule( image, psz_file,
-                           NSLINKMODULE_OPTION_RETURN_ON_ERROR );
-
-    if( !handle )
-    {
-        NSLinkEditErrors errors;
-        const char *psz_file, *psz_err;
-        int i_errnum;
-        NSLinkEditError( &errors, &i_errnum, &psz_file, &psz_err );
-        msg_Warn( p_this, "cannot link module `%s' (%s)", psz_file, psz_err );
-        NSDestroyObjectFileImage( image );
-        return -1;
-    }
-
-    /* Destroy our image, we won't need it */
-    NSDestroyObjectFileImage( image );
-
-#elif defined(HAVE_DL_BEOS)
-    handle = load_add_on( psz_file );
-    if( handle < 0 )
-    {
-        msg_Warn( p_this, "cannot load module `%s'", psz_file );
-        return -1;
-    }
-
-#elif defined(HAVE_DL_WINDOWS) && defined(UNDER_CE)
-    char psz_filename[MAX_PATH];
-    WideCharToMultiByte( CP_ACP, WC_DEFAULTCHAR, psz_file, -1,
-                         psz_filename, MAX_PATH, NULL, NULL );
-    handle = LoadLibrary( psz_filename );
-    if( handle == NULL )
-    {
-        char *psz_error = GetWindowsError();
-        msg_Warn( p_this, "cannot load module `%s' (%s)",
-                          psz_filename, psz_error );
-        free( psz_error );
-    }
-
-#elif defined(HAVE_DL_WINDOWS) && defined(WIN32)
-    handle = LoadLibrary( psz_file );
-    if( handle == NULL )
-    {
-        char *psz_error = GetWindowsError();
-        msg_Warn( p_this, "cannot load module `%s' (%s)",
-                          psz_file, psz_error );
-        free( psz_error );
-    }
-
-#elif defined(HAVE_DL_DLOPEN) && defined(RTLD_NOW)
-    /* static is OK, we are called atomically */
-    static vlc_bool_t b_kde = VLC_FALSE;
-
-#   if defined(SYS_LINUX)
-    /* XXX HACK #1 - we should NOT open modules with RTLD_GLOBAL, or we
-     * are going to get namespace collisions when two modules have common
-     * public symbols, but ALSA is being a pest here. */
-    if( strstr( psz_file, "alsa_plugin" ) )
+    else
     {
-        handle = dlopen( psz_file, RTLD_NOW | RTLD_GLOBAL );
-        if( handle == NULL )
+        /* If junk dll, don't try to load it */
+        if( p_cache_entry->b_junk )
         {
-            msg_Warn( p_this, "cannot load module `%s' (%s)",
-                              psz_file, dlerror() );
-            return -1;
+            p_module = NULL;
+        }
+        else
+        {
+            p_module = p_cache_entry->p_module;
+            p_module->b_loaded = VLC_FALSE;
         }
-    }
-#   endif
-    /* XXX HACK #2 - the ugly KDE workaround. It seems that libkdewhatever
-     * causes dlopen() to segfault if libstdc++ is not loaded in the caller,
-     * so we just load libstdc++. Bwahahaha! ph34r! -- Sam. */
-    /* Update: FYI, this is Debian bug #180505, and seems to be fixed. */
-    if( !b_kde && !strstr( psz_file, "kde" ) )
-    {
-        dlopen( "libstdc++.so.6", RTLD_NOW )
-         || dlopen( "libstdc++.so.5", RTLD_NOW )
-         || dlopen( "libstdc++.so.4", RTLD_NOW )
-         || dlopen( "libstdc++.so.3", RTLD_NOW );
-        b_kde = VLC_TRUE;
     }
 
-    handle = dlopen( psz_file, RTLD_NOW );
-    if( handle == NULL )
+    if( p_module )
     {
-        msg_Warn( p_this, "cannot load module `%s' (%s)",
-                          psz_file, dlerror() );
-        return -1;
-    }
+        /* Everything worked fine !
+         * The module is ready to be added to the list. */
+        p_module->b_builtin = VLC_FALSE;
 
-#elif defined(HAVE_DL_DLOPEN)
-#   if defined(DL_LAZY)
-    handle = dlopen( psz_file, DL_LAZY );
-#   else
-    handle = dlopen( psz_file, 0 );
-#   endif
-    if( handle == NULL )
-    {
-        msg_Warn( p_this, "cannot load module `%s' (%s)",
-                          psz_file, dlerror() );
-        return -1;
-    }
+        /* msg_Dbg( p_this, "plugin \"%s\", %s",
+                    p_module->psz_object_name, p_module->psz_longname ); */
 
-#elif defined(HAVE_DL_SHL_LOAD)
-    handle = shl_load( psz_file, BIND_IMMEDIATE | BIND_NONFATAL, NULL );
-    if( handle == NULL )
-    {
-        msg_Warn( p_this, "cannot load module `%s' (%s)",
-                          psz_file, strerror(errno) );
-        return -1;
+        vlc_object_attach( p_module, p_this->p_libvlc->p_module_bank );
     }
 
-#else
-#   error "Something is wrong in modules.c"
+    if( !p_this->p_libvlc->p_module_bank->b_cache ) return 0;
+
+    /* Add entry to cache */
+#define p_bank p_this->p_libvlc->p_module_bank
+    p_bank->pp_cache =
+        realloc( p_bank->pp_cache, (p_bank->i_cache + 1) * sizeof(void *) );
+    p_bank->pp_cache[p_bank->i_cache] = malloc( sizeof(module_cache_t) );
+    p_bank->pp_cache[p_bank->i_cache]->psz_file = strdup( psz_file );
+    p_bank->pp_cache[p_bank->i_cache]->i_time = i_file_time;
+    p_bank->pp_cache[p_bank->i_cache]->i_size = i_file_size;
+    p_bank->pp_cache[p_bank->i_cache]->b_junk = p_module ? 0 : 1;
+    p_bank->pp_cache[p_bank->i_cache]->p_module = p_module;
+    p_bank->i_cache++;
+
+    return p_module ? 0 : -1;
+}
 
-#endif
+/*****************************************************************************
+ * AllocatePlugin: load a module into memory and initialize it.
+ *****************************************************************************
+ * This function loads a dynamically loadable module and allocates a structure
+ * for its information data. The module can then be handled by module_Need
+ * and module_Unneed. It can be removed by DeleteModule.
+ *****************************************************************************/
+static module_t * AllocatePlugin( vlc_object_t * p_this, char * psz_file )
+{
+    module_t * p_module;
+    module_handle_t handle;
+
+    if( LoadModule( p_this, psz_file, &handle ) ) return NULL;
 
     /* Now that we have successfully loaded the module, we can
      * allocate a structure for it */
@@ -964,25 +954,22 @@ static int AllocatePluginFile( vlc_object_t * p_this, MYCHAR * psz_file )
     {
         msg_Err( p_this, "out of memory" );
         CloseModule( handle );
-        return -1;
+        return NULL;
     }
 
     /* We need to fill these since they may be needed by CallEntry() */
-#ifdef UNDER_CE
-    p_module->psz_filename = psz_filename;
-#else
     p_module->psz_filename = psz_file;
-#endif
     p_module->handle = handle;
     p_module->p_symbols = &p_this->p_libvlc->p_module_bank->symbols;
+    p_module->b_loaded = VLC_TRUE;
 
-    /* Initialize the module: fill p_module->psz_object_name, default config */
+    /* Initialize the module: fill p_module, default config */
     if( CallEntry( p_module ) != 0 )
     {
         /* We couldn't call module_init() */
         vlc_object_destroy( p_module );
         CloseModule( handle );
-        return -1;
+        return NULL;
     }
 
     DupModule( p_module );
@@ -992,12 +979,7 @@ static int AllocatePluginFile( vlc_object_t * p_this, MYCHAR * psz_file )
     /* Everything worked fine ! The module is ready to be added to the list. */
     p_module->b_builtin = VLC_FALSE;
 
-    /* msg_Dbg( p_this, "plugin \"%s\", %s",
-                p_module->psz_object_name, p_module->psz_longname ); */
-
-    vlc_object_attach( p_module, p_this->p_libvlc->p_module_bank );
-
-    return 0;
+    return p_module;
 }
 
 /*****************************************************************************
@@ -1118,7 +1100,7 @@ static int DeleteModule( module_t * p_module )
 #ifdef HAVE_DYNAMIC_PLUGINS
     if( !p_module->b_builtin )
     {
-        if( p_module->b_unloadable )
+        if( p_module->b_loaded && p_module->b_unloadable )
         {
             CloseModule( p_module->handle );
         }
@@ -1194,6 +1176,135 @@ static int CallEntry( module_t * p_module )
     return 0;
 }
 
+/*****************************************************************************
+ * LoadModule: loads a dynamic library
+ *****************************************************************************
+ * This function loads a dynamically linked library using a system dependant
+ * method. Will return 0 on success as well as the module handle.
+ *****************************************************************************/
+static int LoadModule( vlc_object_t *p_this, char *psz_file,
+                       module_handle_t *p_handle )
+{
+    module_handle_t handle;
+
+#if defined(HAVE_DL_DYLD)
+    NSObjectFileImage image;
+    NSObjectFileImageReturnCode ret;
+
+    ret = NSCreateObjectFileImageFromFile( psz_file, &image );
+
+    if( ret != NSObjectFileImageSuccess )
+    {
+        msg_Warn( p_this, "cannot create image from `%s'", psz_file );
+        return -1;
+    }
+
+    /* Open the dynamic module */
+    handle = NSLinkModule( image, psz_file,
+                           NSLINKMODULE_OPTION_RETURN_ON_ERROR );
+
+    if( !handle )
+    {
+        NSLinkEditErrors errors;
+        const char *psz_file, *psz_err;
+        int i_errnum;
+        NSLinkEditError( &errors, &i_errnum, &psz_file, &psz_err );
+        msg_Warn( p_this, "cannot link module `%s' (%s)", psz_file, psz_err );
+        NSDestroyObjectFileImage( image );
+        return -1;
+    }
+
+    /* Destroy our image, we won't need it */
+    NSDestroyObjectFileImage( image );
+
+#elif defined(HAVE_DL_BEOS)
+    handle = load_add_on( psz_file );
+    if( handle < 0 )
+    {
+        msg_Warn( p_this, "cannot load module `%s'", psz_file );
+        return -1;
+    }
+
+#elif defined(HAVE_DL_WINDOWS)
+    handle = LoadLibrary( psz_file );
+    if( handle == NULL )
+    {
+        char *psz_err = GetWindowsError();
+        msg_Warn( p_this, "cannot load module `%s' (%s)", psz_file, psz_err );
+        free( psz_err );
+    }
+
+#elif defined(HAVE_DL_DLOPEN) && defined(RTLD_NOW)
+    /* static is OK, we are called atomically */
+    static vlc_bool_t b_kde = VLC_FALSE;
+
+#   if defined(SYS_LINUX)
+    /* XXX HACK #1 - we should NOT open modules with RTLD_GLOBAL, or we
+     * are going to get namespace collisions when two modules have common
+     * public symbols, but ALSA is being a pest here. */
+    if( strstr( psz_file, "alsa_plugin" ) )
+    {
+        handle = dlopen( psz_file, RTLD_NOW | RTLD_GLOBAL );
+        if( handle == NULL )
+        {
+            msg_Warn( p_this, "cannot load module `%s' (%s)",
+                              psz_file, dlerror() );
+            return -1;
+        }
+    }
+#   endif
+    /* XXX HACK #2 - the ugly KDE workaround. It seems that libkdewhatever
+     * causes dlopen() to segfault if libstdc++ is not loaded in the caller,
+     * so we just load libstdc++. Bwahahaha! ph34r! -- Sam. */
+    /* Update: FYI, this is Debian bug #180505, and seems to be fixed. */
+    if( !b_kde && !strstr( psz_file, "kde" ) )
+    {
+        dlopen( "libstdc++.so.6", RTLD_NOW )
+         || dlopen( "libstdc++.so.5", RTLD_NOW )
+         || dlopen( "libstdc++.so.4", RTLD_NOW )
+         || dlopen( "libstdc++.so.3", RTLD_NOW );
+        b_kde = VLC_TRUE;
+    }
+
+    handle = dlopen( psz_file, RTLD_NOW );
+    if( handle == NULL )
+    {
+        msg_Warn( p_this, "cannot load module `%s' (%s)",
+                          psz_file, dlerror() );
+        return -1;
+    }
+
+#elif defined(HAVE_DL_DLOPEN)
+#   if defined(DL_LAZY)
+    handle = dlopen( psz_file, DL_LAZY );
+#   else
+    handle = dlopen( psz_file, 0 );
+#   endif
+    if( handle == NULL )
+    {
+        msg_Warn( p_this, "cannot load module `%s' (%s)",
+                          psz_file, dlerror() );
+        return -1;
+    }
+
+#elif defined(HAVE_DL_SHL_LOAD)
+    handle = shl_load( psz_file, BIND_IMMEDIATE | BIND_NONFATAL, NULL );
+    if( handle == NULL )
+    {
+        msg_Warn( p_this, "cannot load module `%s' (%s)",
+                          psz_file, strerror(errno) );
+        return -1;
+    }
+
+#else
+#   error "Something is wrong in modules.c"
+
+#endif
+
+    *p_handle = handle;
+    return 0;
+}
+
 /*****************************************************************************
  * CloseModule: unload a dynamic library
  *****************************************************************************
@@ -1336,4 +1447,492 @@ static char * GetWindowsError( void )
 }
 #endif /* HAVE_DL_WINDOWS */
 
+/*****************************************************************************
+ * LoadPluginsCache: loads the plugins cache file
+ *****************************************************************************
+ * This function will load the plugin cache if present and valid. This cache
+ * will in turn be queried by AllocateAllPlugins() to see if it needs to
+ * actually load the dynamically loadable module.
+ * This allows us to only fully load plugins when they are actually used.
+ *****************************************************************************/
+static void CacheLoad( vlc_object_t *p_this )
+{
+    char *psz_filename, *psz_homedir, *psz_cachefile;
+    FILE *file;
+    int i, j, i_size, i_read;
+    char p_cachestring[sizeof(PLUGINSCACHE_FILE COPYRIGHT_MESSAGE)];
+    int *pi_cache;
+    module_cache_t **pp_cache = 0;
+
+    psz_cachefile = 0; // FIXME
+    if( !psz_cachefile || !psz_cachefile )
+    {
+        psz_homedir = p_this->p_vlc->psz_homedir;
+        if( !psz_homedir )
+        {
+            msg_Err( p_this, "psz_homedir is null" );
+            return;
+        }
+        psz_filename =
+            (char *)malloc( sizeof("/" CONFIG_DIR "/" PLUGINSCACHE_FILE) +
+                            strlen(psz_homedir) );
+
+        if( psz_filename )
+            sprintf( psz_filename, "%s/" CONFIG_DIR "/" PLUGINSCACHE_FILE,
+                     psz_homedir );
+
+        if( !psz_filename )
+        {
+            msg_Err( p_this, "out of memory" );
+            return;
+        }
+    }
+    else
+    {
+        psz_filename = strdup( psz_cachefile );
+        if( !psz_filename )
+        {
+            msg_Err( p_this, "out of memory" );
+            return;
+        }
+    }
+
+    if( p_this->p_libvlc->p_module_bank->b_cache_delete )
+    {
+        msg_Dbg( p_this, "removing plugins cache file %s", psz_filename );
+        unlink( psz_filename );
+        return;
+    }
+
+    msg_Dbg( p_this, "loading plugins cache file %s", psz_filename );
+
+    file = fopen( psz_filename, "r" );
+    if( !file )
+    {
+        msg_Warn( p_this, "could not open plugins cache file %s for reading",
+                  psz_filename );
+        free( psz_filename );
+        return;
+    }
+    free( psz_filename );
+
+    /* Check the file is a plugins cache */
+    i_size = sizeof(PLUGINSCACHE_FILE COPYRIGHT_MESSAGE) - 1;
+    i_read = fread( p_cachestring, sizeof(char), i_size, file );
+    if( i_read != i_size ||
+        memcmp( p_cachestring, PLUGINSCACHE_FILE COPYRIGHT_MESSAGE, i_size ) )
+    {
+        msg_Warn( p_this, "This doesn't look like a valid plugins cache" );
+    }
+
+    pi_cache = &p_this->p_libvlc->p_module_bank->i_loaded_cache;
+    fread( pi_cache, sizeof(char), sizeof(*pi_cache), file );
+    pp_cache = p_this->p_libvlc->p_module_bank->pp_loaded_cache =
+        malloc( *pi_cache * sizeof(void *) );
+
+    for( i = 0; i < *pi_cache; i++ )
+    {
+        int32_t i_size;
+
+#define LOAD_IMMEDIATE(a) \
+        fread( &a, sizeof(char), sizeof(a), file )
+#define LOAD_STRING(a) \
+        { fread( &i_size, sizeof(char), sizeof(int32_t), file ); \
+          if( i_size ) { \
+            a = malloc( i_size ); \
+            fread( a, sizeof(char), i_size, file ); \
+          } else a = 0; \
+        } while(0)
+
+        pp_cache[i] = malloc( sizeof(module_cache_t) );
+
+        /* Save common info */
+        LOAD_STRING( pp_cache[i]->psz_file );
+        LOAD_IMMEDIATE( pp_cache[i]->i_time );
+        LOAD_IMMEDIATE( pp_cache[i]->i_size );
+        LOAD_IMMEDIATE( pp_cache[i]->b_junk );
+
+        if( pp_cache[i]->b_junk ) continue;
+
+        pp_cache[i]->p_module = vlc_object_create( p_this, VLC_OBJECT_MODULE );
+
+        /* Save additional infos */
+        LOAD_STRING( pp_cache[i]->p_module->psz_object_name );
+        LOAD_STRING( pp_cache[i]->p_module->psz_shortname );
+        LOAD_STRING( pp_cache[i]->p_module->psz_longname );
+        LOAD_STRING( pp_cache[i]->p_module->psz_program );
+        for( j = 0; j < MODULE_SHORTCUT_MAX; j++ )
+        {
+            LOAD_STRING( pp_cache[i]->p_module->pp_shortcuts[j] ); // FIX
+        }
+        LOAD_STRING( pp_cache[i]->p_module->psz_capability );
+        LOAD_IMMEDIATE( pp_cache[i]->p_module->i_score );
+        LOAD_IMMEDIATE( pp_cache[i]->p_module->i_cpu );
+        LOAD_IMMEDIATE( pp_cache[i]->p_module->b_unloadable );
+        LOAD_IMMEDIATE( pp_cache[i]->p_module->b_reentrant );
+        LOAD_IMMEDIATE( pp_cache[i]->p_module->b_submodule );
+
+        /* Config stuff */
+        CacheLoadConfig( pp_cache[i]->p_module, file );
+
+        LOAD_STRING( pp_cache[i]->p_module->psz_filename );
+
+        int i_submodules;
+        LOAD_IMMEDIATE( i_submodules );
+
+        while( i_submodules-- )
+        {
+            module_t *p_module = vlc_object_create( p_this, VLC_OBJECT_MODULE);
+            vlc_object_attach( p_module, pp_cache[i]->p_module );
+            p_module->b_submodule = VLC_TRUE; 
+
+            LOAD_STRING( p_module->psz_object_name );
+            LOAD_STRING( p_module->psz_shortname );
+            LOAD_STRING( p_module->psz_longname );
+            LOAD_STRING( p_module->psz_program );
+            for( j = 0; j < MODULE_SHORTCUT_MAX; j++ )
+            {
+                LOAD_STRING( p_module->pp_shortcuts[j] ); // FIX
+            }
+            LOAD_STRING( p_module->psz_capability );
+            LOAD_IMMEDIATE( p_module->i_score );
+            LOAD_IMMEDIATE( p_module->i_cpu );
+            LOAD_IMMEDIATE( p_module->b_unloadable );
+            LOAD_IMMEDIATE( p_module->b_reentrant );
+        }
+    }
+
+    fclose( file );
+
+    return;
+}
+
+void CacheLoadConfig( module_t *p_module, FILE *file )
+{
+    int i, j, i_lines;
+    int32_t i_size;
+
+    /* Calculate the structure length */
+    LOAD_IMMEDIATE( p_module->i_config_items );
+    LOAD_IMMEDIATE( p_module->i_bool_items );
+
+    LOAD_IMMEDIATE( i_lines );
+
+    /* Allocate memory */
+    p_module->p_config =
+        (module_config_t *)malloc( sizeof(module_config_t) * (i_lines + 1));
+    if( p_module->p_config == NULL )
+    {
+        msg_Err( p_module, "config error: can't duplicate p_config" );
+        return;
+    }
+
+    /* Do the duplication job */
+    for( i = 0; i < i_lines ; i++ )
+    {
+        LOAD_IMMEDIATE( p_module->p_config[i] );
+
+        LOAD_STRING( p_module->p_config[i].psz_type );
+        LOAD_STRING( p_module->p_config[i].psz_name );
+        LOAD_STRING( p_module->p_config[i].psz_text );
+        LOAD_STRING( p_module->p_config[i].psz_longtext );
+        LOAD_STRING( p_module->p_config[i].psz_value_orig );
+
+        p_module->p_config[i].psz_value =
+            p_module->p_config[i].psz_value_orig ?
+                strdup( p_module->p_config[i].psz_value_orig ) : 0;
+        p_module->p_config[i].i_value = p_module->p_config[i].i_value_orig;
+        p_module->p_config[i].f_value = p_module->p_config[i].f_value_orig;
+
+        p_module->p_config[i].p_lock = &p_module->object_lock;
+
+        if( p_module->p_config[i].i_list )
+        {
+            if( p_module->p_config[i].ppsz_list )
+            {
+                p_module->p_config[i].ppsz_list =
+                    malloc( (p_module->p_config[i].i_list+1) * sizeof(char *));
+                if( p_module->p_config[i].ppsz_list )
+                {
+                    for( j = 0; j < p_module->p_config[i].i_list; j++ )
+                        LOAD_STRING( p_module->p_config[i].ppsz_list[j] );
+                    p_module->p_config[i].ppsz_list[j] = NULL;
+                }
+            }
+            if( p_module->p_config[i].ppsz_list_text )
+            {
+                p_module->p_config[i].ppsz_list_text =
+                    malloc( (p_module->p_config[i].i_list+1) * sizeof(char *));
+                if( p_module->p_config[i].ppsz_list_text )
+                {
+                  for( j = 0; j < p_module->p_config[i].i_list; j++ )
+                      LOAD_STRING( p_module->p_config[i].ppsz_list_text[j] );
+                  p_module->p_config[i].ppsz_list_text[j] = NULL;
+                }
+            }
+            if( p_module->p_config[i].pi_list )
+            {
+                p_module->p_config[i].pi_list =
+                    malloc( (p_module->p_config[i].i_list + 1) * sizeof(int) );
+                if( p_module->p_config[i].pi_list )
+                {
+                    for( j = 0; j < p_module->p_config[i].i_list; j++ )
+                        LOAD_IMMEDIATE( p_module->p_config[i].pi_list[j] );
+                }
+            }
+        }
+
+        if( p_module->p_config[i].i_action )
+        {
+            p_module->p_config[i].ppf_action =
+                malloc( p_module->p_config[i].i_action * sizeof(void *) );
+            p_module->p_config[i].ppsz_action_text =
+                malloc( p_module->p_config[i].i_action * sizeof(char *) );
+
+            for( j = 0; j < p_module->p_config[i].i_action; j++ )
+            {
+                p_module->p_config[i].ppf_action[j] = 0;
+                LOAD_STRING( p_module->p_config[i].ppsz_action_text[j] );
+            }
+        }
+
+        p_module->p_config[i].pf_callback = 0;
+    }
+
+    p_module->p_config[i].i_type = CONFIG_HINT_END;
+}
+
+/*****************************************************************************
+ * SavePluginsCache: saves the plugins cache to a file
+ *****************************************************************************/
+static void CacheSave( vlc_object_t *p_this )
+{
+    char *psz_filename, *psz_homedir, *psz_cachefile;
+    FILE *file;
+    int i, j, i_cache;
+    module_cache_t **pp_cache;
+
+    psz_cachefile = 0; // FIXME
+    if( !psz_cachefile || !psz_cachefile )
+    {
+        psz_homedir = p_this->p_vlc->psz_homedir;
+        if( !psz_homedir )
+        {
+            msg_Err( p_this, "psz_homedir is null" );
+            return;
+        }
+        psz_filename =
+            (char *)malloc( sizeof("/" CONFIG_DIR "/" PLUGINSCACHE_FILE) +
+                            strlen(psz_homedir) );
+
+        if( psz_filename )
+            sprintf( psz_filename, "%s/" CONFIG_DIR, psz_homedir );
+
+        if( !psz_filename )
+        {
+            msg_Err( p_this, "out of memory" );
+            return;
+        }
+
+        config_CreateDir( p_this, psz_filename );
+
+        strcat( psz_filename, "/" PLUGINSCACHE_FILE );
+    }
+    else
+    {
+        psz_filename = strdup( psz_cachefile );
+        if( !psz_filename )
+        {
+            msg_Err( p_this, "out of memory" );
+            return;
+        }
+    }
+
+    msg_Dbg( p_this, "saving plugins cache file %s", psz_filename );
+
+    file = fopen( psz_filename, "w" );
+    if( !file )
+    {
+        msg_Warn( p_this, "could not open plugins cache file %s for writing",
+                  psz_filename );
+        free( psz_filename );
+        return;
+    }
+    free( psz_filename );
+
+    /* Contains version number */
+    fprintf( file, PLUGINSCACHE_FILE COPYRIGHT_MESSAGE );
+
+    i_cache = p_this->p_libvlc->p_module_bank->i_cache;
+    pp_cache = p_this->p_libvlc->p_module_bank->pp_cache;
+
+    fwrite( &i_cache, sizeof(char), sizeof(i_cache), file );
+
+    for( i = 0; i < i_cache; i++ )
+    {
+        int32_t i_size;
+
+#define SAVE_IMMEDIATE(a) \
+        fwrite( &a, sizeof(char), sizeof(a), file )
+#define SAVE_STRING(a) \
+        { i_size = a ? strlen( a ) + 1 : 0; \
+          fwrite( &i_size, sizeof(char), sizeof(int32_t), file ); \
+          if( a ) fwrite( a, sizeof(char), i_size, file ); \
+        } while(0)
+
+        /* Save common info */
+        SAVE_STRING( pp_cache[i]->psz_file );
+        SAVE_IMMEDIATE( pp_cache[i]->i_time );
+        SAVE_IMMEDIATE( pp_cache[i]->i_size );
+        SAVE_IMMEDIATE( pp_cache[i]->b_junk );
+
+        if( pp_cache[i]->b_junk ) continue;
+
+        /* Save additional infos */
+        SAVE_STRING( pp_cache[i]->p_module->psz_object_name );
+        SAVE_STRING( pp_cache[i]->p_module->psz_shortname );
+        SAVE_STRING( pp_cache[i]->p_module->psz_longname );
+        SAVE_STRING( pp_cache[i]->p_module->psz_program );
+        for( j = 0; j < MODULE_SHORTCUT_MAX; j++ )
+        {
+            SAVE_STRING( pp_cache[i]->p_module->pp_shortcuts[j] ); // FIX
+        }
+        SAVE_STRING( pp_cache[i]->p_module->psz_capability );
+        SAVE_IMMEDIATE( pp_cache[i]->p_module->i_score );
+        SAVE_IMMEDIATE( pp_cache[i]->p_module->i_cpu );
+        SAVE_IMMEDIATE( pp_cache[i]->p_module->b_unloadable );
+        SAVE_IMMEDIATE( pp_cache[i]->p_module->b_reentrant );
+        SAVE_IMMEDIATE( pp_cache[i]->p_module->b_submodule );
+
+        /* Config stuff */
+        CacheSaveConfig( pp_cache[i]->p_module, file );
+
+        SAVE_STRING( pp_cache[i]->p_module->psz_filename );
+
+        int i_submodule;
+        SAVE_IMMEDIATE( pp_cache[i]->p_module->i_children );
+        for( i_submodule = 0; i_submodule < pp_cache[i]->p_module->i_children;
+             i_submodule++ )
+        {
+            module_t *p_module =
+                (module_t *)pp_cache[i]->p_module->pp_children[i_submodule];
+
+            SAVE_STRING( p_module->psz_object_name );
+            SAVE_STRING( p_module->psz_shortname );
+            SAVE_STRING( p_module->psz_longname );
+            SAVE_STRING( p_module->psz_program );
+            for( j = 0; j < MODULE_SHORTCUT_MAX; j++ )
+            {
+                SAVE_STRING( p_module->pp_shortcuts[j] ); // FIX
+            }
+            SAVE_STRING( p_module->psz_capability );
+            SAVE_IMMEDIATE( p_module->i_score );
+            SAVE_IMMEDIATE( p_module->i_cpu );
+            SAVE_IMMEDIATE( p_module->b_unloadable );
+            SAVE_IMMEDIATE( p_module->b_reentrant );
+        }
+    }
+
+    fclose( file );
+
+    return;
+}
+
+void CacheSaveConfig( module_t *p_module, FILE *file )
+{
+    int i, j, i_lines = 0;
+    module_config_t *p_item;
+    int32_t i_size;
+
+    SAVE_IMMEDIATE( p_module->i_config_items );
+    SAVE_IMMEDIATE( p_module->i_bool_items );
+
+    for( p_item = p_module->p_config; p_item->i_type != CONFIG_HINT_END;
+         p_item++ ) i_lines++;
+
+    SAVE_IMMEDIATE( i_lines );
+
+    for( i = 0; i < i_lines ; i++ )
+    {
+        SAVE_IMMEDIATE( p_module->p_config[i] );
+
+        SAVE_STRING( p_module->p_config[i].psz_type );
+        SAVE_STRING( p_module->p_config[i].psz_name );
+        SAVE_STRING( p_module->p_config[i].psz_text );
+        SAVE_STRING( p_module->p_config[i].psz_longtext );
+        SAVE_STRING( p_module->p_config[i].psz_value_orig );
+
+        if( p_module->p_config[i].i_list )
+        {
+            if( p_module->p_config[i].ppsz_list )
+            {
+                for( j = 0; j < p_module->p_config[i].i_list; j++ )
+                    SAVE_STRING( p_module->p_config[i].ppsz_list[j] );
+            }
+
+            if( p_module->p_config[i].ppsz_list_text )
+            {
+                for( j = 0; j < p_module->p_config[i].i_list; j++ )
+                    SAVE_STRING( p_module->p_config[i].ppsz_list_text[j] );
+            }
+            if( p_module->p_config[i].pi_list )
+            {
+                for( j = 0; j < p_module->p_config[i].i_list; j++ )
+                    SAVE_IMMEDIATE( p_module->p_config[i].pi_list[j] );
+            }
+        }
+
+        for( j = 0; j < p_module->p_config[i].i_action; j++ )
+            SAVE_STRING( p_module->p_config[i].ppsz_action_text[j] );
+    }
+}
+
+/*****************************************************************************
+ * CacheMerge: Merge a cache module descriptor with a full module descriptor.
+ *****************************************************************************/
+static void CacheMerge( vlc_object_t *p_this, module_t *p_cache,
+                        module_t *p_module )
+{
+    int i_submodule;
+
+    p_cache->pf_activate = p_module->pf_activate;
+    p_cache->pf_deactivate = p_module->pf_deactivate;
+    p_cache->p_symbols = p_module->p_symbols;
+    p_cache->handle = p_module->handle;
+
+    for( i_submodule = 0; i_submodule < p_module->i_children; i_submodule++ )
+    {
+        module_t *p_child = (module_t*)p_module->pp_children[i_submodule];
+        module_t *p_cchild = (module_t*)p_cache->pp_children[i_submodule];
+        p_cchild->pf_activate = p_child->pf_activate;
+        p_cchild->pf_deactivate = p_child->pf_deactivate;
+        p_cchild->p_symbols = p_child->p_symbols;
+    }
+
+    p_cache->b_loaded = VLC_TRUE;
+    p_module->b_loaded = VLC_FALSE;
+}
+
+/*****************************************************************************
+ * FindPluginCache: finds the cache entry corresponding to a file
+ *****************************************************************************/
+static module_cache_t *CacheFind( vlc_object_t *p_this, char *psz_file,
+                                  int64_t i_time, int64_t i_size )
+{
+    module_cache_t **pp_cache;
+    int i_cache, i;
+
+    pp_cache = p_this->p_libvlc->p_module_bank->pp_loaded_cache;
+    i_cache = p_this->p_libvlc->p_module_bank->i_loaded_cache;
+
+    for( i = 0; i < i_cache; i++ )
+    {
+        if( !strcmp( pp_cache[i]->psz_file, psz_file ) &&
+            pp_cache[i]->i_time == i_time &&
+            pp_cache[i]->i_size == i_size ) return pp_cache[i];
+    }
+
+    return NULL;
+}
+
 #endif /* HAVE_DYNAMIC_PLUGINS */