ResourceGroupManager.cs

From Axiom

Jump to: navigation, search
#region LGPL License
/*
Axiom Graphics Engine Library
Copyright (C) 2003-2006  Axiom Project Team

The overall design, and a majority of the core engine and rendering code 
contained within this library is a derivative of the open source Object Oriented 
Graphics Engine OGRE, which can be found at http://ogre.sourceforge.net.  
Many thanks to the OGRE team for maintaining such a high quality project.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#endregion

#region SVN Version Information
// <file>
//     <copyright see="prj:///doc/copyright.txt"/>
//     <license see="prj:///doc/license.txt"/>
//     <id value="$Id$"/>
// </file>
#endregion SVN Version Information

#region Namespace Declarations

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;

#endregion Namespace Declarations

namespace Axiom
{

    /// <summary>
    /// This defines an interface which is called back during
    /// resource group loading to indicate the progress of the load. 
    /// </summary>
    /// <remarks>
    /// Resource group loading is in 2 phases - creating resources from 
    /// declarations (which includes parsing scripts), and loading
    /// resources. Note that you don't necessarily have to have both; it
    /// is quite possible to just parse all the scripts for a group (see
    /// ResourceGroupManager.InitialiseResourceGroup, but not to 
    /// load the resource group. 
    /// The sequence of events is (* signifies a repeating item):
    /// <ul>
    ///     <li>resourceGroupScriptingStarted</li>
    ///     <li>scriptParseStarted (*)</li>
    ///     <li>scriptParseEnded (*)</li>
    ///     <li>resourceGroupScriptingEnded</li>
    ///     <li>resourceGroupLoadStarted</li>
    ///     <li>resourceLoadStarted (*)</li>
    ///     <li>resourceLoadEnded (*)</li>
    ///     <li>worldGeometryStageStarted (*)</li>
    ///     <li>worldGeometryStageEnded (*)</li>
    ///     <li>resourceGroupLoadEnded</li>
    /// </ul>
    /// </remarks>
    /// <ogre name="ResourceGroupListener">
    ///     <file name="OgreResourceGroupManager.h"   revision="1.12.2.4" lastUpdated="5/14/2006" lastUpdatedBy="Borrillis" />
    ///     <file name="OgreResourceGroupManager.cpp" revision="1.16.2.10" lastUpdated="5/14/2006" lastUpdatedBy="Borrillis" />
    ///     <Borrillis>
    ///			Note: This has changed from a Class in OGRE to an interface here to better support derived classes
    ///     </Borrillis>
    /// </ogre> 
    public interface IResourceGroupListener
    {
        /// <summary>
        /// This event is fired when a resource group begins parsing scripts.
        /// </summary>
        /// <param name="groupName">The name of the group</param>
        /// <param name="scriptCount">The number of scripts which will be parsed</param>
        void ResourceGroupScriptingStarted( string groupName, int scriptCount );

        /// <summary>
        /// This event is fired when a script is about to be parsed.
        /// </summary>
        /// <param name="scriptName">Name of the to be parsed</param>
        void ScriptParseStarted( string scriptName );

        /// <summary>
        /// This event is fired when the script has been fully parsed.
        /// </summary>
        void ScriptParseEnded();

        /// <summary>
        /// This event is fired when a resource group finished parsing scripts.
        /// </summary>
        /// <param name="groupName">The name of the group</param>
        void ResourceGroupScriptingEnded( string groupName );

        /// <summary>
        /// This event is fired  when a resource group begins loading.
        /// </summary>
        /// <param name="groupName">The name of the group being loaded</param>
        /// <param name="resourceCount">
        /// The number of resources which will be loaded, 
        /// including a number of stages required to load any linked world geometry
        /// </param>
        void ResourceGroupLoadStarted( string groupName, int resourceCount );

        /// <summary>
        /// This event is fired when a declared resource is about to be loaded. 
        /// </summary>
        /// <param name="resource">Weak reference to the resource loaded</param>
        void ResourceLoadStarted( Resource resource );

        /// <summary>
        /// This event is fired when the resource has been loaded. 
        /// </summary>
        void ResourceLoadEnded();

        /// <summary>
        /// This event is fired when a stage of loading linked world geometry 
        /// is about to start. The number of stages required will have been 
        /// included in the resourceCount passed in resourceGroupLoadStarted.
        /// </summary>
        /// <param name="description">Text description of what was just loaded</param>
        void WorldGeometryStageStarted( string description );

        /// <summary>
        /// This event is fired when a stage of loading linked world geometry 
        /// has been completed. The number of stages required will have been 
        /// included in the resourceCount passed in resourceGroupLoadStarted.
        /// </summary>
        /// <param name="description">Text description of what was just loaded</param>
        void WorldGeometryStageEnded();

        /// <summary>
        /// This event is fired when a resource group finished loading.
        /// </summary>
        void ResourceGroupLoadEnded( string groupName );
    }

    /// <summary>
    /// This singleton class manages the list of resource groups, and notifying
    /// the various resource managers of their obligations to load / unload
    /// resources in a group. It also provides facilities to monitor resource
    /// loading per group (to do progress bars etc), provided the resources 
    /// that are required are pre-registered.
    /// <para />
    /// Defining new resource groups,  and declaring the resources you intend to
    /// use in advance is optional, however it is a very useful feature. In addition, 
    /// if a ResourceManager supports the definition of resources through scripts, 
    ///	then this is the class which drives the locating of the scripts and telling
    ///	the ResourceManager to parse them. 
    /// @par
    ///	There are several states that a resource can be in (the concept, not the
    ///	object instance in this case):
    ///	<ol>
    ///	<li><b>Undefined</b>. Nobody knows about this resource yet. It might be
    ///	in the filesystem, but Ogre is oblivious to it at the moment - there 
    ///	is no Resource instance. This might be because it's never been declared
    ///	(either in a script, or using ResourceGroupManager::declareResource), or
    ///	it may have previously been a valid Resource instance but has been 
    ///	removed, either individually through ResourceManager::remove or as a group
    ///	through ResourceGroupManager::clearResourceGroup.</li>
    ///	<li><b>Declared</b>. Ogre has some forewarning of this resource, either
    ///	through calling ResourceGroupManager::declareResource, or by declaring
    ///	the resource in a script file which is on one of the resource locations
    ///	which has been defined for a group. There is still no instance of Resource,
    ///	but Ogre will know to create this resource when 
    ///	ResourceGroupManager::initialiseResourceGroup is called (which is automatic
    ///	if you declare the resource group before Root::initialise).</li>
    ///	<li><b>Unloaded</b>. There is now a Resource instance for this resource, 
    ///	although it is not loaded. This means that code which looks for this
    ///	named resource will find it, but the Resource is not using a lot of memory
    ///	because it is in an unloaded state. A Resource can get into this state
    ///	by having just been created by ResourceGroupManager::initialiseResourceGroup 
    ///	(either from a script, or from a call to declareResource), by 
    ///	being created directly from code (ResourceManager::create), or it may 
    ///	have previously been loaded and has been unloaded, either individually
    ///	through Resource::unload, or as a group through ResourceGroupManager::unloadResourceGroup.</li>
    ///	<li><b>Loaded</b>The Resource instance is fully loaded. This may have
    ///	happened implicitly because something used it, or it may have been 
    ///	loaded as part of a group.</li>
    ///	</ol>
    ///	<see>ResourceGroupManager.DeclareResource</see>
    ///	<see>ResourceGroupManager.InitialiseResourceGroup</see>
    ///	<see>ResourceGroupManager.LoadResourceGroup</see>
    ///	<see>ResourceGroupManager.UnloadResourceGroup</see>
    ///	<see>ResourceGroupManager.ClearResourceGroup</see>
    ///	</summary>
    /// <ogre name="ResourceGroupListener">
    ///     <file name="OgreResourceGroupManager.h"   revision="1.12.2.4" lastUpdated="5/14/2006" lastUpdatedBy="Borrillis" />
    ///     <file name="OgreResourceGroupManager.cpp" revision="1.16.2.10" lastUpdated="5/14/2006" lastUpdatedBy="Borrillis" />
    /// </ogre> 
    public class ResourceGroupManager : Singleton<ResourceGroupManager>
    {
        #region Delegates

        /// <summary>
        /// This event is fired when a resource group begins parsing scripts.
        /// </summary>
        /// <param name="groupName">The name of the group</param>
        /// <param name="scriptCount">The number of scripts which will be parsed</param>
        private delegate void ResourceGroupScriptingStarted( string groupName, int scriptCount );
        private ResourceGroupScriptingStarted _resourceGroupScriptingStarted;

        /// <summary>
        /// This event is fired when a script is about to be parsed.
        /// </summary>
        /// <param name="scriptName">Name of the to be parsed</param>
        private delegate void ScriptParseStarted( string scriptName );
        private ScriptParseStarted _scriptParseStarted;

        /// <summary>
        /// This event is fired when the script has been fully parsed.
        /// </summary>
        private delegate void ScriptParseEnded();
        private ScriptParseEnded _scriptParseEnded;

        /// <summary>
        /// This event is fired when a resource group finished parsing scripts.
        /// </summary>
        /// <param name="groupName">The name of the group</param>
        private delegate void ResourceGroupScriptingEnded( string groupName );
        private ResourceGroupScriptingEnded _resourceGroupScriptingEnded;

        /// <summary>
        /// This event is fired  when a resource group begins loading.
        /// </summary>
        /// <param name="groupName">The name of the group being loaded</param>
        /// <param name="resourceCount">
        /// The number of resources which will be loaded, 
        /// including a number of stages required to load any linked world geometry
        /// </param>
        private delegate void ResourceGroupLoadStarted( string groupName, int resourceCount );
        private ResourceGroupLoadStarted _resourceGroupLoadStarted;

        /// <summary>
        /// This event is fired when a declared resource is about to be loaded. 
        /// </summary>
        /// <param name="resource">Weak reference to the resource loaded</param>
        private delegate void ResourceLoadStarted( Resource resource );
        private ResourceLoadStarted _resourceLoadStarted;

        /// <summary>
        /// This event is fired when the resource has been loaded. 
        /// </summary>
        private delegate void ResourceLoadEnded();
        private ResourceLoadEnded _resourceLoadEnded;

        /// <summary>
        /// This event is fired when a stage of loading linked world geometry 
        /// is about to start. The number of stages required will have been 
        /// included in the resourceCount passed in resourceGroupLoadStarted.
        /// </summary>
        /// <param name="description">Text description of what was just loaded</param>
        private delegate void WorldGeometryStageStarted( string description );
        private WorldGeometryStageStarted _worldGeometryStageStarted;
        
        /// <summary>
        /// This event is fired when a stage of loading linked world geometry 
        /// has been completed. The number of stages required will have been 
        /// included in the resourceCount passed in resourceGroupLoadStarted.
        /// </summary>
        /// <param name="description">Text description of what was just loaded</param>
        private delegate void WorldGeometryStageEnded();
        private WorldGeometryStageEnded _worldGeometryStageEnded;
        
        /// <summary>
        /// This event is fired when a resource group finished loading.
        /// </summary>
        private delegate void ResourceGroupLoadEnded( string groupName );
        private ResourceGroupLoadEnded _resourceGroupLoadEnded;

        #endregion

        #region Collection Declarations
        /// List of resource declarations
        //         typedef std::list<ResourceDeclaration> ResourceDeclarationList;
        public class ResourceDeclarationList : List<ResourceDeclaration>
        {
        };

        /// <summary>Map of resource types (strings) to ResourceManagers, used to notify them to load / unload group contents</summary>
        //          typedef std::map<String, ResourceManager*> ResourceManagerMap;
        public class ResourceManagerMap : Dictionary<String, ResourceManager>
        {
        };

        /// <summary>Map of loading order (Real) to ScriptLoader, used to order script parsing</summary>
        //          typedef std::multimap<Real, ScriptLoader*> ScriptLoaderOrderMap;
        public class ScriptLoaderOrderMap : AxiomCollection<float, IScriptLoader>
        {
        };

        /// <summary></summary>
        //          typedef std::vector<ResourceGroupListener*> ResourceGroupListenerList;
        public class ResourceGroupListenerList : List<IResourceGroupListener>
        {
        };

        /// <summary>Resource index entry, resourcename->location </summary>
        //          typedef std::map<String, Archive*> ResourceLocationIndex;
        public class ResourceLocationIndex : Dictionary<String, Archive>
        {
        };

        /// <summary>List of possible file locations</summary>
        //          typedef std::list<ResourceLocation*> LocationList;
        public class LocationList : List<ResourceLocation>
        {
        };

        /// <summary>List of resources which can be loaded / unloaded </summary>
        //          typedef std::list<ResourcePtr> LoadUnloadResourceList;
        public class LoadUnloadResourceList : List<Resource>
        {
        };

        /// <summary>Map from resource group names to groups </summary>
        //          typedef std::map<String, ResourceGroup*> ResourceGroupMap;
        public class ResourceGroupMap : Dictionary<String, ResourceGroup>
        {
        };

        //          typedef std::map<Real, LoadUnloadResourceList*> LoadResourceOrderMap;
        /// <summary>Map of loading order (float) to LoadUnLoadResourceList  used to order resource loading</summary>
        public class LoadResourceOrderMap : AxiomCollection<float, LoadUnloadResourceList>
        {
        };

        #endregion

        #region Nested Types

        /// Nested struct defining a resource declaration
        public struct ResourceDeclaration
        {
            public string ResourceName;
            public string ResourceType;
            public NameValuePairList Parameters;
        };

        /// <summary>Resource location entry</summary>
        public struct ResourceLocation : IDisposable
        {
            /// <summary>Pointer to the archive which is the destination</summary>
            public Archive Archive;
            /// Whether this location was added recursively
            public bool Recursive;

            #region IDisposable Members

            public void Dispose()
            {
                //Archive.Dispose();
                Archive = null;
                Recursive = false;
            }

            #endregion
        };

        /// Resource group entry
        public class ResourceGroup : IDisposable
        {

            //OGRE_AUTO_MUTEX
            /// <summary>Group name </summary>
            public string Name;

            /// <summary>Whether group has been initialised </summary>
            public bool Initialized;

            /// <summary>List of possible locations to search </summary>
            public LocationList LocationList;

            /// <summary>Index of resource names to locations, built for speedy access (case sensitive archives) </summary>
            public ResourceLocationIndex ResourceIndexCaseSensitive;

            /// <summary>Index of resource names to locations, built for speedy access (case insensitive archives) </summary>
            public ResourceLocationIndex ResourceIndexCaseInsensitive;

            /// <summary>Pre-declared resources, ready to be created </summary>
            public ResourceDeclarationList ResourceDeclarations;

            /// <summary>
            /// Created resources which are ready to be loaded / unloaded
            /// Group by loading order of the type (defined by ResourceManager)
            /// (e.g. skeletons and materials before meshes) 
            /// </summary>
            public LoadResourceOrderMap LoadResourceOrderMap;

            /// <summary>Linked world geometry, as passed to setWorldGeometry </summary>
            public string WorldGeometry;

            /// <summary>Scene manager to use with linked world geometry </summary>
            public SceneManager WorldGeometrySceneManager;

            #region IDisposable Members

            public void Dispose()
            {
                if ( Initialized )
                {
                    Initialized = false;
                    LocationList.Clear();
                    ResourceIndexCaseInsensitive.Clear();
                    ResourceIndexCaseSensitive.Clear();
                    ResourceDeclarations.Clear();
                    LoadResourceOrderMap.Clear();
                    WorldGeometrySceneManager = null;
                    WorldGeometry = "";
                    Name = "";
                }
            }

            #endregion
        };

        #endregion

        #region Fields and Properties
        //OGRE_AUTO_MUTEX // public to allow external locking

        #region DefaultResourceGroupName Property

        /// Default resource group name
        private static string _defaultResourceGroupName = "General";
        public static string DefaultResourceGroupName
        {
            get
            {
                return _defaultResourceGroupName;
            }
        }

        #endregion DefaultResourceGroupName Property

        #region BootstrapResourceGroupName Property

        /// Bootstrap resource group name (min OGRE resources)
        private static string _bootstrapResourceGroupName = "Bootstrap";
        public static string BootstrapResourceGroupName
        {
            get
            {
                return _bootstrapResourceGroupName;
            }
        }

        #endregion BootstrapResourceGroupName Property

        #region resourceManagerMap Property

        private ResourceManagerMap _resourceManagerMap;
        protected ResourceManagerMap resourceManagerMap
        {
            get
            {
                return _resourceManagerMap;
            }
        }

        #endregion resourceManagerMap Property

        #region scriptLoaderMap Property

        private ScriptLoaderOrderMap _scriptLoaderOrderMap;
        protected ScriptLoaderOrderMap scriptLoaderOrderMap
        {
            get
            {
                return _scriptLoaderOrderMap;
            }
        }

        #endregion scriptLoaderMap Property

        #region resourceGroupListenerList Property

        private ResourceGroupListenerList _resourceGroupListenerList;
        protected ResourceGroupListenerList resourceGroupListenerList
        {
            get
            {
                return _resourceGroupListenerList;
            }
        }

        #endregion resourceGroupListenerList Property

        #region resourceGroupMap Property

        private ResourceGroupMap _resourceGroupMap;
        protected ResourceGroupMap resourceGroupMap
        {
            get
            {
                return _resourceGroupMap;
            }
        }

        #endregion resourceGroupMap Property

        #region WorldResourceGroupName Property

        /// <summary>Group name for world resources</summary>
        private String _worldGroupName;
        /// <summary>
        /// Gets/Sets the resource group that 'world' resources will use.
        /// </summary>
        /// <remarks>
        ///    This is the group which should be used by SceneManagers implementing
        ///    world geometry when looking for their resources. Defaults to the 
        ///    DefaultResourceGroupName but this can be altered.
        /// </remarks>
        public String WorldResourceGroupName
        {
            get
            {
                return _worldGroupName;
            }
            set
            {
                _worldGroupName = value;
            }
        }

        #endregion WorldResourceGroupName Property

        #region currentGroup Property

        /// Stored current group - optimisation for when bulk loading a group
        private ResourceGroup _currentGroup;
        /// Stored current group - optimisation for when bulk loading a group
        protected ResourceGroup currentGroup
        {
            get
            {
                return _currentGroup;
            }
            set
            {
                _currentGroup = value;
            }
        }

        #endregion currentGroup Property

        #endregion Fields and Properties

        #region Constructors and Destructor

        /// <summary>
        ///     Internal constructor.  This class cannot be instantiated externally.
        /// </summary>
        private ResourceGroupManager()
        {

            // Create the 'General' group
            CreateResourceGroup( DefaultResourceGroupName );
            // default world group to the default group
            _worldGroupName = DefaultResourceGroupName;
            _currentGroup = null;

        }

        ~ResourceGroupManager()
        {
            // delete all resource groups
            foreach ( KeyValuePair<string,ResourceGroup> pair in resourceGroupMap )
            {
                ResourceGroup rg = pair.Value;
                _deleteGroup( rg );
            }
            resourceGroupMap.Clear();
            _currentGroup = null;
        }

        #endregion Constructors and Destructor

        #region Event Firing Methods

        /// <summary>Internal event firing method </summary>
        /// <param name="groupName"></param>
        /// <param name="scriptCount"></param>
        private void _fireResourceGroupScriptingStarted( string groupName, int scriptCount )
        {
            _resourceGroupScriptingStarted( groupName, scriptCount );
        }

        /// <summary>Internal event firing method </summary>
        /// <param name="scriptName"></param>
        private void _fireScriptStarted( string scriptName )
        {
            _scriptParseStarted( scriptName );
        }

        /// <summary>Internal event firing method</summary>
        private void _fireScriptEnded()
        {
            _scriptParseEnded();
        }

        /// <summary>Internal event firing method </summary>
        /// <param name="groupName"></param>
        private void _fireResourceGroupScriptingEnded( string groupName )
        {
            _resourceGroupScriptingEnded( groupName );
        }

        /// <summary>Internal event firing method </summary>
        /// <param name="groupName"></param>
        /// <param name="resourceCount"></param>
        private void _fireResourceGroupLoadStarted( string groupName, int resourceCount )
        {
            _resourceGroupLoadStarted( groupName, resourceCount );
        }

        /// <summary>Internal event firing method </summary>
        /// <param name="resource"></param>
        private void _fireResourceStarted( Resource resource )
        {
            _resourceLoadStarted( resource );
        }

        /// <summary>Internal event firing method </summary>
        private void _fireResourceEnded()
        {
            _resourceLoadEnded();
        }

        /// <summary>Internal event firing method </summary>
        /// <param name="groupName"></param>
        private void _fireResourceGroupLoadEnded( string groupName )
        {
            _resourceGroupLoadEnded( groupName );
        }

        #endregion Event Firing Methods

        #region Public Methods

        /// <summary>
        ///  Create a resource group.
        /// </summary>
        /// <remarks>
        ///    A resource group allows you to define a set of resources that can 
        ///    be loaded / unloaded as a unit. For example, it might be all the 
        ///    resources used for the level of a game. There is always one predefined
        ///    resource group called ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, 
        ///	which is typically used to hold all resources which do not need to 
        ///	be unloaded until shutdown. You can create additional ones so that 
        ///	you can control the life of your resources in whichever way you wish.
        /// <para>
        ///    Once you have defined a resource group, resources which will be loaded
        ///	as part of it are defined in one of 3 ways:
        ///	<ol>
        ///	<li>Manually through declareResource(); this is useful for scripted
        ///		declarations since it is entirely generalised, and does not 
        ///		create Resource instances right away</li>
        ///	<li>Through the use of scripts; some ResourceManager subtypes have
        ///		script formats (e.g. .material, .overlay) which can be used
        ///		to declare resources</li>
        ///	<li>By calling ResourceManager::create to create a resource manually.
        ///	This resource will go on the list for it's group and will be loaded
        ///	and unloaded with that group</li>
        ///	</ol>
        ///	You must remember to call initialiseResourceGroup if you intend to use
        ///	the first 2 types.
        /// </para>
        /// </remarks>
        /// <param name="name">The name to give the resource group.</param>
        public void CreateResourceGroup( string name )
        {
            LogManager.Instance.Write( "Creating resource group " + name );
            if ( getResourceGroup( name ) == null )
            {
                throw new AxiomException( "Resource group with name '" + name + "' already exists!" );
            }
            ResourceGroup grp = new ResourceGroup();
            grp.Initialized = false;
            grp.Name = name;
            grp.WorldGeometrySceneManager = null;
            resourceGroupMap.Add( name, grp );
        }

        /// <summary>
        /// Initialises a resource group.
        /// </summary>
        /// <remarks>
        ///	After creating a resource group, adding some resource locations, and
        ///	perhaps pre-declaring some resources using declareResource(), but 
        ///	before you need to use the resources in the group, you 
        ///	should call this method to initialise the group. By calling this,
        ///	you are triggering the following processes:
        ///	<ol>
        ///	<li>Scripts for all resource types which support scripting are
        ///		parsed from the resource locations, and resources within them are
        ///		created (but not loaded yet).</li>
        ///	<li>Creates all the resources which have just pre-declared using
        ///	declareResource (again, these are not loaded yet)</li>
        ///	</ol>
        ///	So what this essentially does is create a bunch of unloaded Resource entries
        ///	in the respective ResourceManagers based on scripts, and resources
        ///	you've pre-declared. That means that code looking for these resources
        ///	will find them, but they won't be taking up much memory yet, until
        ///	they are either used, or they are loaded in bulk using loadResourceGroup.
        ///	Loading the resource group in bulk is entirely optional, but has the 
        ///	advantage of coming with progress reporting as resources are loaded.
        /// <para>
        ///	Failure to call this method means that loadResourceGroup will do 
        ///	nothing, and any resources you define in scripts will not be found.
        ///	Similarly, once you have called this method you won't be able to
        ///	pick up any new scripts or pre-declared resources, unless you
        ///	call clearResourceGroup, set up declared resources, and call this
        ///	method again.
        /// </para>
        /// <para>
        ///	When you call Root.Initialise, all resource groups that have already been
        ///	created are automatically initialised too. Therefore you do not need to 
        ///	call this method for groups you define and set up before you call 
        ///	Root.Initialise. However, since one of the most useful features of 
        ///	resource groups is to set them up after the main system initialisation
        ///	has occurred (e.g. a group per game level), you must remember to call this
        ///	method for the groups you create after this.
        /// </para>
        /// </remarks>
        /// <param name="name">The name of the resource group to initialise</param>
        public void InitialiseResourceGroup( string groupName )
        {
            LogManager.Instance.Write( "Initialising resource group {0}", groupName );
            ResourceGroup grp = getResourceGroup( groupName );
            if ( grp == null )
            {
                throw new AxiomException( "Cannot find a group named {0} : ResourceGroupManager::parseResourceGroupScripts", groupName );
            }

            if ( !grp.Initialized )
            {
                // Set current group
                _currentGroup = grp;
                _parseResourceGroupScripts( grp );
                _createDeclaredResources( grp );
                grp.Initialized = true;

                // Reset current group
                _currentGroup = null;
            }
        }

        /// <summary>
        /// Initialise all resource groups which are yet to be initialised.
        /// </summary>
        /// <see cref="ResourceGroupManager.initializeResourceGroup"/>
        public void InitialiseAllResourceGroups()
        {
            LogManager.Instance.Write( "Initialising all resource groups:" );
            // Intialise all declared resource groups
            foreach ( KeyValuePair<string, ResourceGroup> pair in resourceGroupMap )
            {
                ResourceGroup grp = pair.Value;
                if ( !grp.Initialized )
                {
                    // Set current group
                    _currentGroup = grp;
                    _parseResourceGroupScripts( grp );
                    _createDeclaredResources( grp );
                    grp.Initialized = true;

                    // Reset current group
                    _currentGroup = null;
                }
                LogManager.Instance.Write( "     {0} initialized.", grp.Name );
            }
        }

        #region LoadResourceGrouop Method

        /// <overloads>
        /// <summary>Loads a resource group.</summary>
        /// <remarks>            
        /// Loads any created resources which are part of the named group.
        /// Note that resources must have already been created by calling
        /// ResourceManager::create, or declared using declareResource() or
        /// in a script (such as .material and .overlay). The latter requires
        /// that initialiseResourceGroup has been called. 
        /// 
        /// When this method is called, this class will callback any ResourceGroupListeners
        /// which have been registered to update them on progress. 
        /// </remarks>
        /// <param name="name">The name to of the resource group to load.</param>
        /// </overloads>
        public void LoadResourceGroup( string name )
        {
            LoadResourceGroup( name, true, true );
        }

        /// <param name="loadMainResources">If true, loads normal resources associated 
        /// with the group (you might want to set this to false if you wanted
        /// to just load world geometry in bulk)</param>
        /// <param name="loadWorldGeom">If true, loads any linked world geometry <see>ResourceGroupManager.LinkWorldGeometryToResourceGroup</see></param>
        public void LoadResourceGroup( string name, bool loadMainResources, bool loadWorldGeom )
        {
            // Can only bulk-load one group at a time (reasonable limitation I think)
            //OGRE_LOCK_AUTO_MUTEX
            LogManager.Instance.Write( "Loading resource group '{0}' - Resources: {1} World Geometry: {2}", name, loadMainResources, loadWorldGeom );
            // load all created resources
            ResourceGroup grp = getResourceGroup( name );
            if ( grp == null )
            {
                throw new AxiomException( "Cannot find a group named {0} : ResourceGroupManager::parseResourceGroupScripts", name );
            }

            // Set current group
            _currentGroup = grp;

            // Count up resources for starting event
            int resourceCount = 0;
            if ( loadMainResources )
            {
                foreach ( KeyValuePair<float,LoadUnloadResourceList> pair in grp.LoadResourceOrderMap )
                {
                    LoadUnloadResourceList lurl = pair.Value;
                    resourceCount += lurl.Count;
                }
            }
            // Estimate world geometry size
            if ( grp.WorldGeometrySceneManager != null && loadWorldGeom )
            {
                resourceCount += grp.WorldGeometrySceneManager.EstimateWorldGeometry( grp.WorldGeometry );
            }

            _fireResourceGroupLoadStarted( name, resourceCount );

            // Now load for real
            if ( loadMainResources )
            {
                foreach ( KeyValuePair<float, LoadUnloadResourceList> pair in grp.LoadResourceOrderMap )
                {
                    LoadUnloadResourceList lurl = pair.Value;
                    foreach ( Resource res in lurl )
                    {
                        // If loading one of these resources cascade-loads another resource, 
                        // the list will get longer! But these should be loaded immediately
                        if ( !res.IsLoaded )
                        {
                            _fireResourceStarted( res );
                            res.Load();
                            _fireResourceEnded();
                        }
                    }
                }
            }
            // Load World Geometry
            if ( grp.WorldGeometrySceneManager != null && loadWorldGeom )
            {
                grp.WorldGeometrySceneManager.SetWorldGeometry( grp.WorldGeometry );
            }
            _fireResourceGroupLoadEnded( name );

            // reset current group
            _currentGroup = null;

            LogManager.Instance.Write( "Finished loading resource group {0}.", name );
        }

        #endregion LoadResourceGroup Method

        /// <summary>Unloads a resource group.</summary>
        /// <remarks>
        /// This method unloads all the resources that have been declared as
        /// being part of the named resource group. Note that these resources
        /// will still exist in their respective ResourceManager classes, but
        /// will be in an unloaded state. If you want to remove them entirely,
        /// you should use ClearResourceGroup or DestroyResourceGroup.
        /// </remarks>
        /// <param name="name">The name to of the resource group to unload.</param>
        public void UnloadResourceGroup( string groupName )
        {

            LogManager.Instance.Write( "Unloading resource group {0}", groupName );
            ResourceGroup grp = getResourceGroup( groupName );
            if ( grp == null )
            {
                throw new AxiomException( "Cannot find a group named {0} : ResourceGroupManager::parseResourceGroupScripts", groupName );
            }
            // Set current group
            _currentGroup = grp;

            foreach ( KeyValuePair<float,LoadUnloadResourceList> pair in grp.LoadResourceOrderMap )
            {
                LoadUnloadResourceList lurl = pair.Value;
                foreach ( Resource res in lurl )
                {
                    res.Unload();
                }
            }

            // reset current group
            _currentGroup = null;
            LogManager.Instance.Write( "Finished unloading resource group {0}", groupName );
        }

        /// <summary>Clears a resource group.</summary>
        /// <remarks>            
        /// This method unloads all resources in the group, but in addition it
        /// removes all those resources from their ResourceManagers, and then 
        /// clears all the members from the list. That means after calling this
        /// method, there are no resources declared as part of the named group
        /// any more. Resource locations still persist though.
        /// </remarks>
        /// <param name="name">The name to of the resource group to clear.</param>
        public void ClearResourceGroup( string groupName )
        {
            LogManager.Instance.Write( "clearing resource group {0}", groupName );
            ResourceGroup grp = getResourceGroup( groupName );
            if ( grp == null )
            {
                throw new AxiomException( "Cannot find a group named {0} : ResourceGroupManager::ClearResourceGroup", groupName );
            }
            // set current group
            _currentGroup = grp;
            _dropGroupContents( grp );
            // clear initialised flag
            grp.Initialized = false;
            // reset current group
            _currentGroup = null;
            LogManager.Instance.Write( "Finished clearing resource group {0}", groupName );
        }

        /// <summary>
        /// Destroys a resource group, clearing it first, destroying the resources
        /// which are part of it, and then removing it from
        /// the list of resource groups.
        /// </summary>
        /// <param name="name">The name of the resource group to destroy.</param>
        public void DestroyResourceGroup( string groupName )
        {
            LogManager.Instance.Write( "Destroying resource group " + groupName );
            ResourceGroup grp = getResourceGroup( groupName );
            if ( grp == null )
            {
                throw new AxiomException( "Cannot find a group named {0} : ResourceGroupManager::DestroyResourceGroup", groupName );
            }
            // set current group
            _currentGroup = grp;
            UnloadResourceGroup( groupName ); // will throw an exception if name not valid
            _dropGroupContents( grp );
            _deleteGroup( grp );
            resourceGroupMap.Remove( groupName );
            // reset current group
            _currentGroup = null;
        }

        #region AddResourceLocation Method 

        /// <overloads>
        /// <summary>Method to add a resource location to for a given resource group.</summary>
        /// <remarks>
        /// Resource locations are places which are searched to load resource files.
        /// When you choose to load a file, or to search for valid files to load, 
        /// the resource locations are used.
        /// </remarks>
        /// <param name="name">The name of the resource location; probably a directory, zip file, URL etc.</param>
        /// <param name="locType">
        /// The codename for the resource type, which must correspond to the 
        /// Archive factory which is providing the implementation.
        /// </param>
        /// </overloads>
        public void AddResourceLocation( string name, string locType )
        {
            AddResourceLocation( name, locType, DefaultResourceGroupName, false );
        }

        /// <param name="resGroup">
        /// The name of the resource group for which this location is
        /// to apply. ResourceGroupManager.DefaultResourceGroupName is the 
        /// default group which always exists, and can
        /// be used for resources which are unlikely to be unloaded until application
        /// shutdown. Otherwise it must be the name of a group; if it
        /// has not already been created with createResourceGroup then it is created
        /// automatically.
        /// </param>
        public void AddResourceLocation( string name, string locType, string resGroup )
        {
            AddResourceLocation( name, locType, resGroup, false );
        }

        /// <param name="recursive"> 
        /// Whether subdirectories will be searched for files when using 
        /// a pattern match (such as *.material), and whether subdirectories will be
        /// indexed. This can slow down initial loading of the archive and searches.
        /// When opening a resource you still need to use the fully qualified name, 
        /// this allows duplicate names in alternate paths.
        /// </param>
        public void AddResourceLocation( string name, string locType, bool recursive )
        {
            AddResourceLocation( name, locType, DefaultResourceGroupName, recursive );
        }

        /// <param name="resGroup">
        /// The name of the resource group for which this location is
        /// to apply. ResourceGroupManager.DefaultResourceGroupName is the 
        /// default group which always exists, and can
        /// be used for resources which are unlikely to be unloaded until application
        /// shutdown. Otherwise it must be the name of a group; if it
        /// has not already been created with createResourceGroup then it is created
        /// automatically.
        /// </param>
        /// <param name="recursive"> 
        /// Whether subdirectories will be searched for files when using 
        /// a pattern match (such as *.material), and whether subdirectories will be
        /// indexed. This can slow down initial loading of the archive and searches.
        /// When opening a resource you still need to use the fully qualified name, 
        /// this allows duplicate names in alternate paths.
        /// </param>
        public void AddResourceLocation( string name, string locType, string resGroup, bool recursive )
        {
            ResourceGroup grp = getResourceGroup( resGroup );
            if ( grp != null )
            {
                CreateResourceGroup( resGroup );
                grp = getResourceGroup( resGroup );
            }


            // Get archive
            Archive arch = ArchiveManager.Instance.Load( name, locType );
            // Add to location list
            ResourceLocation loc = new ResourceLocation();
            loc.Archive = arch;
            loc.Recursive = recursive;
            grp.LocationList.Add( loc );
            // Index resources
            string[] vec = arch.Find( "*", recursive );
            foreach ( string it in vec )
            {
                // Index under full name, case sensitive
                grp.ResourceIndexCaseSensitive[ it ] = arch;
                if ( arch.IsCaseSensitive() )
                {
                    // Index under lower case name too for case insensitive match
                    grp.ResourceIndexCaseInsensitive[ it.ToLower() ] = arch;
                }
            }

            LogManager.Instance.Write( "Added resource location '{0}' of type '{1}' to resource group '{2}'{3}", name, locType, resGroup, recursive ? " with recursive option" : "" );
        }

        #endregion AddResourceLocation Method

        #region RemoveResourceLocation Method

        /// <overloads>
        /// <summary>
        /// Removes a resource location from the search path.
        /// </summary>
        /// <param name="locationName">the name of the ResourceLocation</param>
        /// </overloads>
        public void RemoveResourceLocation( string locationName )
        {
            RemoveResourceLocation( locationName, DefaultResourceGroupName );
        }

        /// <param name="groupName">the name of the ResourceGroup</param>
        public void RemoveResourceLocation( string locationName, string groupName )
        {
            LogManager.Instance.Write( "Remove Resource Location " + groupName );
            ResourceGroup grp = getResourceGroup( groupName );
            if ( grp == null )
            {
                throw new AxiomException( "Cannot find a group named {0} : ResourceGroupManager::RemoveResourceLocation", groupName );
            }

            // Remove from location list
            foreach ( ResourceLocation loc in grp.LocationList )
            {
                Archive arch = loc.Archive;
                if ( arch.Name == locationName )
                {
                    // Delete indexes
                    foreach ( string name in grp.ResourceIndexCaseInsensitive.Keys )
                    {
                        if ( grp.ResourceIndexCaseInsensitive[ name ] == arch )
                            grp.ResourceIndexCaseInsensitive.Remove( name );
                    }

                    foreach ( string name in grp.ResourceIndexCaseSensitive.Keys )
                    {
                        if ( grp.ResourceIndexCaseSensitive[ name ] == arch )
                            grp.ResourceIndexCaseSensitive.Remove( name );
                    }

                    loc.Dispose();

                    grp.LocationList.Remove( loc );
                    break;
                }

            }

            LogManager.Instance.Write( "Removed resource location " + locationName );

        }

        #endregion RemoveResourceLocation Method

        #region DeclareResource Method

        /// <overloads>
        /// <summary>
        /// Declares a resource to be a part of a resource group, allowing you to load and unload it as part of the group.
        /// </summary>
        /// <remarks>            
        /// By declaring resources before you attempt to use them, you can 
        /// more easily control the loading and unloading of those resources
        /// by their group. Declaring them also allows them to be enumerated, 
        /// which means events can be raised to indicate the loading progress
        /// <see>ResourceGroupListener</see>. Note that another way of declaring
        /// resources is to use a script specific to the resource type, if
        /// available (e.g. .material).
        /// <para>			
        /// Declared resources are not created as Resource instances (and thus
        /// are not available through their ResourceManager) until initialiseResourceGroup
        /// is called, at which point all declared resources will become created 
        /// (but unloaded) Resource instances, along with any resources declared
        /// in scripts in resource locations associated with the group.
        /// </para>
        /// </remarks>
        /// <param name="name">The resource name. </param>
        /// <param name="resourceType">
        /// The type of the resource. Axiom comes preconfigured with 
        /// a number of resource types: 
        /// <ul>
        /// <li>Font</li>
        /// <li>Material</li>
        /// <li>Mesh</li>
        /// <li>Overlay</li>
        /// <li>Skeleton</li>
        /// </ul>
        /// .. but more can be added by plugin ResourceManager classes.</param>
        /// </overloads>
        public void DeclareResource( string name, string resourceType )
        {
            DeclareResource( name, resourceType, DefaultResourceGroupName, new NameValuePairList() );
        }

        /// <param name="groupName">The name of the group to which it will belong.</param>
        public void DeclareResource( string name, string resourceType, string groupName )
        {
            DeclareResource( name, resourceType, groupName, new NameValuePairList() );
        }

        /// <param name="loadParameters">
        /// A list of name / value pairs which supply custom
        /// parameters to the resource which will be required before it can 
        /// be loaded. These are specific to the resource type.
        /// </param>
        public void DeclareResource( string name, string resourceType, NameValuePairList loadParameters )
        {
            DeclareResource( name, resourceType, DefaultResourceGroupName, loadParameters );
        }

        /// <param name="groupName">The name of the group to which it will belong.</param>
        /// <param name="loadParameters">
        /// A list of name / value pairs which supply custom
        /// parameters to the resource which will be required before it can 
        /// be loaded. These are specific to the resource type.
        /// </param>
        public void DeclareResource( string name, string resourceType, string groupName, NameValuePairList loadParameters )
        {
            ResourceGroup grp = getResourceGroup( name );
            if ( grp == null )
            {
                throw new AxiomException( "Cannot find a group named {0} : ResourceGroupManager::DeclareResource", name );
            }

            ResourceDeclaration dcl;
            dcl.Parameters = loadParameters;
            dcl.ResourceName = name;
            dcl.ResourceType = resourceType;
            grp.ResourceDeclarations.Add( dcl );
        }

        #endregion DeclareResource Method

        /// <summary>Undeclare a resource.</summary>
        /// <remarks>
        /// Note that this will not cause it to be unloaded
        /// if it is already loaded, nor will it destroy a resource which has 
        /// already been created if InitialiseResourceGroup has been called already.
        /// Only UnloadResourceGroup / ClearResourceGroup / DestroyResourceGroup 
        /// will do that.
        /// </remarks>
        /// <param name="name">The name of the resource. </param>
        /// <param name="groupName">The name of the group this resource was declared in.</param>
        public void UndeclareResource( string name, string groupName )
        {
            ResourceGroup grp = getResourceGroup( groupName );
            if ( grp == null )
            {
                throw new AxiomException( "Cannot find a group named {0} : ResourceGroupManager::UndeclareResource", groupName );
            }

            foreach ( ResourceDeclaration resDec in grp.ResourceDeclarations )
            {
                if ( resDec.ResourceName == name )
                {
                    grp.ResourceDeclarations.Remove( resDec );
                    break;
                }
            }
        }

        #region OpenResource Method
        /// <overloads>
        /// <summary>
        /// Open a single resource by name and return a Stream pointing at the source of the data.
        /// </summary>
        /// <param name="resourceName">
        /// The name of the resource to locate.
        /// Even if resource locations are added recursively, you
        /// must provide a fully qualified name to this method. You 
        /// can find out the matching fully qualified names by using the
        /// find() method if you need to.
        /// </param>
        /// <returns>Stream containing the data, will be destroyed automatically when no longer referenced</returns>
        /// </overloads>
        public Stream OpenResource( string resourceName )
        {
            return OpenResource( resourceName, DefaultResourceGroupName );
        }
        /// <param name="groupName">
        /// The name of the resource group; this determines which locations are searched.
        /// </param>
        public Stream OpenResource( string resourceName, string groupName )
        {
            // Try to find in resource index first
            ResourceGroup grp = getResourceGroup( groupName );
            if ( grp == null )
            {
                throw new AxiomException( "Cannot find a group named {0} : ResourceGroupManager::OpenResource", groupName );
            }

            Archive arch = null;
            if ( grp.ResourceIndexCaseSensitive.ContainsKey( resourceName ) )
            {
                arch = grp.ResourceIndexCaseSensitive[ resourceName ];
                return arch.Open( resourceName );
            }
            else
            {
                string lc = resourceName.ToLower();
                if ( grp.ResourceIndexCaseInsensitive.ContainsKey( lc ) )
                {
                    arch = grp.ResourceIndexCaseInsensitive[ lc ];
                    return arch.Open( lc );
                }
                else
                {
                    foreach ( ResourceLocation rl in grp.LocationList )
                    {
                        arch = rl.Archive;
                        if ( arch.Exists( resourceName ) )
                        {
                            return arch.Open( resourceName );
                        }
                    }
                }
            }

            // Not found
            throw new AxiomException( "Cannot locate resource {0} in resource group {1}.", resourceName, groupName );

        }
        #endregion OpenResource Method

        #region OpenResources Method
        /// <overloads>
        /// <summary>
        /// Open all resources matching a given pattern (which can contain
        /// the character '*' as a wildcard), and return a collection of 
        /// DataStream objects on them.
        /// </summary>
        /// <param name="pattern">
        /// The pattern to look for. If resource locations have been
        /// added recursively, subdirectories will be searched too so this
        /// does not need to be fully qualified.
        /// </param>
        /// <returns>A list of Stream objects.</returns>
        /// </overloads>
        public List<Stream> OpenResources( string pattern )
        {
            return OpenResources( pattern, DefaultResourceGroupName );
        }

        /// <param name="groupName">
        /// The resource group; this determines which locations are searched.
        /// </param>
        public List<Stream> OpenResources( string pattern, string groupName )
        {
            ResourceGroup grp = getResourceGroup( groupName );
            if ( grp == null )
            {
                throw new AxiomException( "Cannot find a group named {0} : ResourceGroupManager::OpenResources", groupName );
            }

            // Iterate through all the archives and build up a combined list of
            // streams
            List<Stream> ret = new List<Stream>();

            foreach ( ResourceLocation li in grp.LocationList )
            {
                Archive arch = li.Archive;
                // Find all the names based on whether this archive is recursive
                string[] names = arch.Find( pattern, li.Recursive );

                // Iterate over the names and load a stream for each
                foreach ( string resource in names )
                {
                    Stream ptr = arch.Open( resource );
                    if ( ptr != null )
                    {
                        ret.Add( ptr );
                    }
                }
            }
            return ret;
        }

        #endregion OpenResources Method

        /// <summary>List all file names in a resource group.</summary>
        /// <remarks>
        /// This method only returns filenames, you can also retrieve other information using listFileInfo.
        /// </remarks>
        /// <param name="groupName">The name of the group</param>
        /// <returns>A list of filenames matching the criteria, all are fully qualified</returns>
        public List<string> ListResourceNames( string groupName )
        {
            List<string> vec = new List<string>();

            ResourceGroup grp = getResourceGroup( groupName );
            if ( grp == null )
            {
                throw new AxiomException( "Cannot find a group named {0} : ResourceGroupManager::ListResourceNames", groupName );
            }

            // Iterate over the archives
            foreach ( ResourceLocation rl in grp.LocationList )
            {
                string[] lst = rl.Archive.List( rl.Recursive );
                vec.AddRange( lst );
            }

            return vec;
        }

        /// <summary>List all files in a resource group with accompanying information.</summary>
        /// <param name="groupName">The name of the group</param>
        /// <returns>A list of structures detailing quite a lot of information about all the files in the archive.</returns>
        public FileInfoList ListResourceFileInfo( string groupName )
        {
            FileInfoList vec = new FileInfoList();

            ResourceGroup grp = getResourceGroup( groupName );
            if ( grp == null )
            {
                throw new AxiomException( "Cannot find a group named {0} : ResourceGroupManager::ListResourceFileInfo", groupName );
            }

            // Iterate over the archives
            foreach ( ResourceLocation rl in grp.LocationList )
            {
                FileInfoList lst = rl.Archive.ListFileInfo( rl.Recursive );
                vec.AddRange( lst );
            }

            return vec;

        }

        /// <summary>
        /// Find all file names matching a given pattern in a resource group.
        /// </summary>
        /// <remarks>
        /// This method only returns filenames, you can also retrieve other
        /// information using findFileInfo.
        /// </remarks>
        /// <param name="groupName">The name of the group</param>
        /// <param name="pattern">The pattern to search for; wildcards (*) are allowed</param>
        /// <returns>A list of filenames matching the criteria, all are fully qualified</returns>
        public List<string> FindResourceNames( string groupName, string pattern )
        {
            List<string> vec = new List<string>();

            // Try to find in resource index first
            ResourceGroup grp = getResourceGroup( groupName );
            if ( grp == null )
            {
                throw new AxiomException( "Cannot find a group named {0} : ResourceGroupManager::FindResourceNames", groupName );
            }


            // Iterate over the archives
            foreach ( ResourceLocation rl in grp.LocationList )
            {
                List<string> lst = rl.Archive.Find( pattern, rl.Recursive );
                vec.AddRange( lst );
            }

            return vec;
        }

        /// <summary>Find out if the named file exists in a group. /summary>
        /// <param name="group">The name of the resource group</param>
        /// <param name="filename">Fully qualified name of the file to test for</param>
        public bool ResourceExists( string group, string filename )
        {
            // Try to find in resource index first
            ResourceGroup grp = getResourceGroup( group );
            if ( grp == null )
            {
                throw new AxiomException( "Cannot find a group named {0} : ResourceGroupManager::ResourceExists", group );
            }

            if ( grp.ResourceIndexCaseSensitive.ContainsKey( filename ) )
            {
                return true;
            }
            else
            {
                string lc = filename.ToLower();
                if ( grp.ResourceIndexCaseInsensitive.ContainsKey( lc ) )
                {
                    return true;
                }
                else
                {
                    foreach ( ResourceLocation rl in grp.LocationList )
                    {
                        Archive arch = rl.Archive;
                        if ( arch.Exists( filename ) )
                        {
                            return true;
                        }
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// Find all files matching a given pattern in a group and get 
        /// some detailed information about them.
        /// </summary>
        /// <param name="groupName">The name of the resource group</param>
        /// <param name="pattern">The pattern to search for; wildcards (*) are allowed</param>
        /// <returns>A list of file information structures for all files matching the criteria.</returns>
        public FileInfoList FindResourceFileInfo( string groupName, string pattern )
        {
            FileInfoList vec = new FileInfoList();

            // Try to find in resource index first
            ResourceGroup grp = getResourceGroup( groupName );
            if ( grp == null )
            {
                throw new AxiomException( "Cannot find a group named {0} : ResourceGroupManager::FindResourceNames", groupName );
            }


            // Iterate over the archives
            foreach ( ResourceLocation rl in grp.LocationList )
            {
                FileInfoList lst = rl.Archive.FindFileInfo( pattern, rl.Recursive );
                vec.AddRange( lst );
            }

            return vec;        
        }

        /// <summary>
        /// Adds a ResourceGroupListener which will be called back during 
        /// resource loading events. 
        /// </summary>
        /// <param name="rgl"></param>
        public void AddResourceGroupListener( IResourceGroupListener rgl )
        {
            if ( rgl != null )
            {
                _resourceGroupListenerList.Add( rgl );
                this._resourceGroupScriptingStarted += new ResourceGroupScriptingStarted( rgl.ResourceGroupScriptingStarted );
                this._resourceGroupScriptingEnded += new ResourceGroupScriptingEnded( rgl.ResourceGroupScriptingEnded );
                this._resourceGroupLoadStarted += new ResourceGroupLoadStarted( rgl.ResourceGroupLoadStarted );
                this._resourceGroupLoadEnded += new ResourceGroupLoadEnded( rgl.ResourceGroupLoadEnded );
                this._resourceLoadStarted += new ResourceLoadStarted( rgl.ResourceLoadStarted );
                this._resourceLoadEnded += new ResourceLoadEnded( rgl.ResourceLoadEnded );
                this._scriptParseStarted += new ScriptParseStarted( rgl.ScriptParseStarted );
                this._scriptParseEnded += new ScriptParseEnded( rgl.ScriptParseEnded );
                this._worldGeometryStageStarted += new WorldGeometryStageStarted( rgl.WorldGeometryStageStarted );
                this._worldGeometryStageEnded += new WorldGeometryStageEnded( rgl.WorldGeometryStageEnded );

            }
        }

        /// <summary>
        /// Removes a ResourceGroupListener
        /// </summary>
        /// <param name="rgl"></param>
        public void RemoveResourceGroupListener( IResourceGroupListener rgl )
        {
            if ( rgl != null )
            {
                _resourceGroupListenerList.Remove( rgl );
                this._resourceGroupScriptingStarted -= new ResourceGroupScriptingStarted( rgl.ResourceGroupScriptingStarted );
                this._resourceGroupScriptingEnded -= new ResourceGroupScriptingEnded( rgl.ResourceGroupScriptingEnded );
                this._resourceGroupLoadStarted -= new ResourceGroupLoadStarted( rgl.ResourceGroupLoadStarted );
                this._resourceGroupLoadEnded -= new ResourceGroupLoadEnded( rgl.ResourceGroupLoadEnded );
                this._resourceLoadStarted -= new ResourceLoadStarted( rgl.ResourceLoadStarted );
                this._resourceLoadEnded -= new ResourceLoadEnded( rgl.ResourceLoadEnded );
                this._scriptParseStarted -= new ScriptParseStarted( rgl.ScriptParseStarted );
                this._scriptParseEnded -= new ScriptParseEnded( rgl.ScriptParseEnded );
                this._worldGeometryStageStarted -= new WorldGeometryStageStarted( rgl.WorldGeometryStageStarted );
                this._worldGeometryStageEnded -= new WorldGeometryStageEnded( rgl.WorldGeometryStageEnded );
            }
        }

        /// <summary>
        /// Associates some world geometry with a resource group, causing it to 
        /// be loaded / unloaded with the resource group.
        /// </summary>
        /// <remarks>
        /// You would use this method to essentially defer a call to 
        /// SceneManager::setWorldGeometry to the time when the resource group
        /// is loaded. The advantage of this is that compatible scene managers 
        /// will include the estimate of the number of loading stages for that
        /// world geometry when the resource group begins loading, allowing you
        /// to include that in a loading progress report.
        /// </remarks>
        /// <param name="groupName">The name of the resource group</param>
        /// <param name="worldGeometry">The parameter which should be passed to setWorldGeometry</param>
        /// <param name="sceneManager">The SceneManager which should be called</param>
        public void LinkWorldGeometryToResourceGroup( string groupName, string worldGeometry, SceneManager sceneManager )
        {
            // Try to find in resource index first
            ResourceGroup grp = getResourceGroup( groupName );
            if ( grp == null )
            {
                throw new AxiomException( "Cannot find a group named {0} : ResourceGroupManager::LinkWorldGeometryToResourceGroup", groupName );
            }

            grp.WorldGeometry = worldGeometry;
            grp.WorldGeometrySceneManager = sceneManager;
        }

        /// <summary>
        /// Clear any link to world geometry from a resource group.
        /// </summary>
        /// <remarks>Basically undoes a previous call to linkWorldGeometryToResourceGroup.</remarks>
        /// <param name="groupName">The name of the resource group</param>
        public void UnlinkWorldGeometryFromResourceGroup( string groupName )
        {
            // Try to find in resource index first
            ResourceGroup grp = getResourceGroup( groupName );
            if ( grp == null )
            {
                throw new AxiomException( "Cannot find a group named {0} : ResourceGroupManager::UnlinkWorldGeometryFromResourceGroup", groupName );
            }

            grp.WorldGeometry = "";
            grp.WorldGeometrySceneManager = null;
        }

        /// <summary>
        /// Shutdown all ResourceManagers, performed as part of clean-up. 
        /// </summary>
        public void ShutdownAll()
        {
            foreach ( KeyValuePair<string, ResourceManager> pair in _resourceManagerMap )
            {
                ResourceManager rm = pair.Value;
                rm.RemoveAll();
            }
        }


        /// <summary>Get a list of the currently defined resource groups.</summary>
        /// <remarks>
        /// This method intentionally returns a copy rather than a reference in
        /// order to avoid any contention issues in multithreaded applications.
        /// </remarks>
        /// <returns>A copy of list of currently defined groups.</returns>
        public List<string> GetResourceGroups()
        {
            List<string> vec = new List<string>();

            foreach ( KeyValuePair<string,ResourceGroup> pair in _resourceGroupMap )
            {
                ResourceGroup rg = pair.Value;
                vec.Add( rg.Name );
            }

            return vec;
        }

        /// <summary>Get the list of resource declarations for the specified group name.</summary>
        /// <remarks>
        /// This method intentionally returns a copy rather than a reference in
        /// order to avoid any contention issues in multithreaded applications.
        /// </remarks>
        /// /// <param name="groupName">The name of the group</param>
        /// <returns>A copy of list of currently defined resources.</returns>
        public ResourceDeclaration[] getResourceDeclarationList( string groupName )
        {
            // Try to find in resource index first
            ResourceGroup grp = getResourceGroup( groupName );
            if ( grp == null )
            {
                throw new AxiomException( "Cannot find a group named {0} : ResourceGroupManager::getResourceDeclarationList", groupName );
            }
            return grp.ResourceDeclarations.ToArray();
        }

        #endregion Public Methods

        #region Internal Methods

        /// <summary>
        /// Get resource group
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        protected ResourceGroup getResourceGroup( string name )
        {
            if ( _resourceGroupMap.ContainsKey( name ) )
            {
                return _resourceGroupMap[ name ];
            }

            return null;

        }

        /// <summary>
        /// Internal method for registering a ResourceManager (which should be
        /// a singleton). Creators of plugins can register new ResourceManagers
        /// this way if they wish.
        /// </summary>
        /// <remarks>
        /// ResourceManagers that wish to parse scripts must also call registerScriptLoader.
        /// </remarks>
        /// <param name="resourceType">String identifying the resource type, must be unique.</param>
        /// <param name="rm">the ResourceManager instance.</param>
        internal void RegisterResourceManager( string resourceType, ResourceManager rm )
        {
            LogManager.Instance.Write( "Registering ResourceManager for type {0}", resourceType );
            _resourceManagerMap[ resourceType ] = rm;
        }

        /// <summary>
        /// Internal method for unregistering a ResourceManager.
        /// </summary>
        /// <remarks>
        /// ResourceManagers that wish to parse scripts must also call unregisterScriptLoader.
        /// </remarks>
        /// <param name="resourceType">String identifying the resource type.</param>
        internal void UnregisterResourceManager( string resourceType )
        {
            LogManager.Instance.Write( "Unregistering ResourceManager for type {0}", resourceType );
            if ( _resourceManagerMap.ContainsKey( resourceType ) )
            {
                _resourceManagerMap.Remove( resourceType );
            }
        }

        /// <summary>
        /// Internal method for registering a ScriptLoader. ScriptLoaders parse scripts when resource groups are initialised.
        /// </summary>
        /// <param name="su">ScriptLoader instance.</param>
        internal void RegisterScriptLoader( IScriptLoader su )
        {
            LogManager.Instance.Write( "Registering ScriptLoader for patterns {0}", su.ScriptPatterns );
            _scriptLoaderOrderMap.Add( su.LoadingOrder, su );
        }

        /// <summary>
        /// Internal method for unregistering a ScriptLoader.
        /// </summary>
        /// <param name="su">ScriptLoader instance.</param>
        internal void UnregisterScriptLoader( IScriptLoader su )
        {
            LogManager.Instance.Write( "Registering ScriptLoader for patterns {0}", su.ScriptPatterns );
            if ( _scriptLoaderOrderMap.ContainsValue( su ) )
            {
                _scriptLoaderOrderMap.Remove( su );
            }
        }

        /// <summary>
        /// Internal method for getting a registered ResourceManager.
        /// </summary>
        /// <param name="resourceType">String identifying the resource type.</param>
        internal ResourceManager GetResourceManager( string resourceType )
        {
            if ( _resourceManagerMap.ContainsKey( resourceType ) == true )
            {
                return _resourceManagerMap[ resourceType ];
            }
            throw new AxiomException( "Cannot locate resource manager for resource type '{0}' ResourceGroupManager::_getResourceManager", resourceType );
        }

        /// <summary>Internal method called by ResourceManager when a resource is created.</summary>
        /// <param name="res">reference to resource</param>
        internal void notifyResourceCreated( Resource res )
        {
            if ( _currentGroup != null )
            {
                // Use current group (batch loading)
                _addCreatedResource( res, _currentGroup );
            }
            else
            {
                // Find group
                ResourceGroup grp = getResourceGroup( res.Group );
                if ( grp != null )
                {
                    _addCreatedResource( res, grp );
                }
            }
        }

        /// <summary>Internal method called by ResourceManager when a resource is removed.</summary>
        /// <param name="res">reference to resource</param>
        internal void notifyResourceRemoved( Resource res )
        {
            if ( _currentGroup != null )
            {
                // Do nothing - we're batch unloading so list will be cleared
            }
            else
            {
                // Find group
                ResourceGroup grp = getResourceGroup( res.Group );
                if ( grp != null )
                {
                    if ( grp.LoadResourceOrderMap.ContainsKey( res.Parent.LoadingOrder ) )
                    {

                        // Iterate over the resource list and remove
                        LoadUnloadResourceList resList = grp.LoadResourceOrderMap[ res.Parent.LoadingOrder ];
                        foreach ( Resource r in resList )
                        {
                            if ( IntPtr.ReferenceEquals( r, res ) )
                            {
                                // this is the one
                                resList.Remove( r );
                                break;
                            }
                        }
                    }
                }
            }
        }

        /// <summary>Internal method called by ResourceManager when all resources for that manager are removed.</summary>
        /// <param name="manager">the manager for which all resources are being removed</param>
        internal void notifyAllResourcesRemoved( ResourceManager manager )
        {
            // Iterate over all groups
            foreach ( KeyValuePair<string, ResourceGroup> rgPair in _resourceGroupMap )
            {
                ResourceGroup rg = rgPair.Value;
                // Iterate over all priorities
                foreach ( KeyValuePair<float, LoadUnloadResourceList> rlPair in rg.LoadResourceOrderMap )
                {
                    LoadUnloadResourceList rl = rlPair.Value;
                    // Iterate over all resources
                    foreach ( Resource res in rl )
                    {
                        if ( res.Parent == manager )
                        {
                            // Increment first since iterator will be invalidated
                            //LoadUnloadResourceList::iterator del = l++;
                            rl.Remove( res );
                        }
                    }
                }

            }
        }

        /// <summary>Notify this manager that one stage of world geometry loading has been started.</summary>
        /// <remarks>            
        /// Custom SceneManagers which load custom world geometry should call this 
        /// method the number of times equal to the value they return from 
        /// SceneManager.estimateWorldGeometry while loading their geometry.
        /// </remarks>
        /// <param name="description"></param>
        internal void notifyWorldGeometryStageStarted( string description )
        {
            _worldGeometryStageStarted( description );
        }

        /// <summary>Notify this manager that one stage of world geometry loading has been completed.</summary>
        /// <remarks>            
        /// Custom SceneManagers which load custom world geometry should call this 
        /// method the number of times equal to the value they return from 
        /// SceneManager.estimateWorldGeometry while loading their geometry.
        /// </remarks>
        internal void notifyWorldGeometryStageEnded()
        {
            _worldGeometryStageEnded();
        }

        #endregion Internal Methods

        #region Private Methods

        /// <summary>
        /// Parses all the available scripts found in the resource locations
        /// for the given group, for all ResourceManagers. 
        /// </summary>
        /// <remarks>
        ///	Called as part of initializeResourceGroup
        /// </remarks>
        private void _parseResourceGroupScripts( ResourceGroup grp )
        {
            LogManager.Instance.Write( "Parsing scripts for resource group " + grp.Name );

            // Count up the number of scripts we have to parse
            List<Tuple<IScriptLoader, List<FileInfoList>>> scriptLoaderFileList = new List<Tuple<IScriptLoader, List<FileInfoList>>>();

            int scriptCount = 0;
            // Iterate over script users in loading order and get streams
            foreach ( KeyValuePair<float, IScriptLoader> pairsl in _scriptLoaderOrderMap )
            {
                IScriptLoader sl = pairsl.Value;
                List<FileInfoList> fileListList = new List<FileInfoList>();

                // Get all the patterns and search them
                List<string> patterns = sl.ScriptPatterns;
                foreach ( string p in patterns )
                {
                    FileInfoList fileList = FindResourceFileInfo( grp.Name, p );
                    scriptCount += fileList.Count;
                    fileListList.Add( fileList );
                }
                scriptLoaderFileList.Add( new Tuple<IScriptLoader, List<FileInfoList>>( sl, fileListList ) );
            }
            // Fire scripting event
            _fireResourceGroupScriptingStarted( grp.Name, scriptCount );

            // Iterate over scripts and parse
            // Note we respect original ordering
            foreach ( Tuple<IScriptLoader, List<FileInfoList>> slfli in scriptLoaderFileList )
            {
                IScriptLoader su = slfli.first;
                // Iterate over each list
                foreach ( FileInfoList flli in slfli.second )
                {
                    // Iterate over each item in the list
                    foreach ( FileInfo fii in flli )
                    {
                        LogManager.Instance.Write( "Parsing script " + fii.Filename );
                        _fireScriptStarted( fii.Filename );
                        {
                            Stream stream = fii.Archive.Open( fii.Filename );
                            if ( stream != null )
                            {
                                su.ParseScript( stream, grp.Name );
                            }
                        }
                        _fireScriptEnded();
                    }
                }
            }

            _fireResourceGroupScriptingEnded( grp.Name );
            LogManager.Instance.Write( "Finished parsing scripts for resource group " + grp.Name );
        }

        /// <summary>Create all the pre-declared resources.</summary>
        /// <remarks>Called as part of initialiseResourceGroup</remarks>
        private void _createDeclaredResources( ResourceGroup grp )
        {
            foreach ( ResourceDeclaration dcl in grp.ResourceDeclarations )
            {
                // Retrieve the appropriate manager
                ResourceManager mgr = _getResourceManager( dcl.resourceType );
                // Create the resource
                Resource res = mgr.Create( dcl.resourceName, grp.Name );
                // Set custom parameters
                res.SetParameterList( dcl.Parameters );
                // Add resource to load list
                LoadUnloadResourceList loadList;
                if ( grp.LoadResourceOrderMap.ContainsKey( mgr.LoadingOrder ) == true )
                {
                    loadList = new LoadUnloadResourceList();
                    grp.LoadResourceOrderMap.Add( mgr.LoadingOrder, loadList );
                }
                else
                {
                    loadList = grp.LoadResourceOrderMap[ mgr.LoadingOrder ];
                }
                loadList.Add( res );

            }
        }

        /** Adds a created resource to a group. */
        private void _addCreatedResource( Resource res, ResourceGroup group )
        {
            Real order = res.Creator.LoadingOrder;

            LoadUnloadResourceList loadList;
            if ( group.LoadResourceOrderMap.ContainsKey( order ) == true )
            {
                loadList = new LoadUnloadResourceList();
                group.LoadResourceOrderMap.Add( order, loadList );
            }
            else
            {
                loadList = group.LoadResourceOrderMap[ order ];
            }

            loadList.Add( res );
        }

        /// <summary>
        ///  Drops contents of a group, leave group there, notify ResourceManagers.
        /// </summary>
        /// <param name="grp"></param>
        private void _dropGroupContents( ResourceGroup grp )
        {

            bool groupSet = false;
            if ( _currentGroup != null )
            {
                // Set current group to indicate ignoring of notifications
                _currentGroup = grp;
                groupSet = true;
            }
            // delete all the load list entries
            foreach ( KeyValuePair<float, LoadUnloadResourceList> pair in grp.LoadResourceOrderMap )
            {
                LoadUnloadResourceList rl = pair.Value;
                foreach ( Resource res in rl )
                {
                    res.Parent.Remove( res );
                }
            }
            grp.LoadResourceOrderMap.Clear();

            if ( groupSet )
            {
                _currentGroup = null;
            }
        }

        /// <summary>
        /// Delete a group for shutdown - don't notify ResourceManagers. 
        /// </summary>
        /// <param name="grp"></param>
        private void _deleteGroup( ResourceGroup grp )
        {
            // delete all the load list entries
            foreach ( KeyValuePair<float, LoadUnloadResourceList> pair in grp.LoadResourceOrderMap )
            {
                // Don't iterate over resources to drop with ResourceManager
                // Assume this is being done anyway since this is a shutdown method
                LoadUnloadResourceList lurl = pair.Value;
                lurl.Clear();
            }

            // Drop location list
            foreach ( ResourceLocation loc in grp.LocationList )
            {
                loc.Dispose();
            }

            // delete ResourceGroup
            grp.Dispose();
        }

        #endregion Private Methods

    };
}