diff api/WarFoundryLoader.cs @ 0:520818033bb6

Initial commit of WarFoundry code
author IBBoard <dev@ibboard.co.uk>
date Fri, 19 Dec 2008 15:57:51 +0000
parents
children b9346894319c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/api/WarFoundryLoader.cs	Fri Dec 19 15:57:51 2008 +0000
@@ -0,0 +1,664 @@
+// WarFoundryLoader.cs
+//
+//  Copyright (C) 2007 IBBoard
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License version 2.1 of the License as published by the Free
+// Software Foundation.
+//
+// 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 
+//
+//
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using IBBoard.Collections;
+using IBBoard.IO;
+using IBBoard.Logging;
+using IBBoard.WarFoundry.API.Factories;
+using IBBoard.WarFoundry.API.Objects;
+using ICSharpCode.SharpZipLib.Zip;
+
+namespace IBBoard.WarFoundry.API
+{
+	public class WarFoundryLoader
+	{		
+		private static WarFoundryLoader loader;
+		
+		/// <summary>
+		/// Gets the default <see cref="WarFoundryLoader"/> used to load WarFoundry data files.
+		/// </summary>
+		/// <returns>
+		/// The default <see cref="WarFoundryLoader"/>
+		/// </returns>
+		public static WarFoundryLoader GetDefault()
+		{
+			if (loader == null)
+			{
+				loader = new WarFoundryLoader();
+			}
+			
+			return loader;
+		}
+		
+		private ICollection<DirectoryInfo> directories;
+		private ICollection<INativeWarFoundryFactory> factories;
+		private ICollection<INonNativeWarFoundryFactory> nonNativeFactories;
+		private Dictionary<string, GameSystem> systemsTable;
+		private Dictionary<string, Dictionary<string, Dictionary<string, Race>>> racesTable; //Keys are: System, Race, SubRace
+		private Dictionary<IWarFoundryFactory, SimpleSet<IWarFoundryObject>> loadedObjects;
+		
+		protected WarFoundryLoader()
+		{
+			directories = new List<DirectoryInfo>();
+			factories = new List<INativeWarFoundryFactory>();
+			nonNativeFactories = new List<INonNativeWarFoundryFactory>();
+			loadedObjects = new Dictionary<IWarFoundryFactory,SimpleSet<IWarFoundryObject>>();
+		}
+		
+		/// <summary>
+		/// Adds a directory to the collection of directories that will be searched for WarFoundry data files.
+		/// </summary>
+		/// <param name="directory">
+		/// The <see cref="DirectoryInfo"/> to add to the list for searching for data files
+		/// </param>
+		public void AddLoadDirectory(DirectoryInfo directory)
+		{
+			if (!directories.Contains(directory))
+			{
+				directories.Add(directory);
+			}
+		}
+		
+		/// <summary>
+		/// Removes a directory from the collection of directories that will be searched for WarFoundry data files.
+		/// </summary>
+		/// <param name="directory">
+		/// A <see cref="DirectoryInfo"/>
+		/// </param>
+		public void RemoveLoadDirectory(DirectoryInfo directory)
+		{
+			if (directories.Contains(directory))
+			{
+				directories.Remove(directory);
+			}
+		}
+		
+		/// <summary>
+		/// Registers a <see cref="INativeWarFoundryFactory"/> as a factory that can parse native data files.
+		/// </summary>
+		/// <param name="factory">
+		/// The <see cref="INativeWarFoundryFactory"/> to register to parse native data files.
+		/// </param>
+		public void RegisterFactory(INativeWarFoundryFactory factory)
+		{
+			if (!factories.Contains(factory))
+			{
+				factories.Add(factory);
+			}
+		}
+		
+		/// <summary>
+		/// Unregisters a <see cref="INativeWarFoundryFactory"/> so that it will no longer be used to try to parse native data files.
+		/// </summary>
+		/// <param name="factory">
+		/// The <see cref="INativeWarFoundryFactory"/> to remove from the collection of factories that are used to try to parse native data files.
+		/// </param>
+		public void UnregisterFactory(INativeWarFoundryFactory factory)
+		{
+			if (factories.Contains(factory))
+			{
+				factories.Remove(factory);
+			}
+		}
+		
+		/// <summary>
+		/// Registers a <see cref="INonNativeWarFoundryFactory"/> so that it will be used to try to parse non-native data files from other applications.
+		/// </summary>
+		/// <param name="factory">
+		/// The <see cref="INonNativeWarFoundryFactory"/> to register to parse non-native data files.
+		/// </param>
+		public void RegisterNonNativeFactory(INonNativeWarFoundryFactory factory)
+		{
+			if (!nonNativeFactories.Contains(factory))
+			{
+				nonNativeFactories.Add(factory);
+			}
+		}
+		
+		/// <summary>
+		/// Unregisters a <see cref="INonNativeWarFoundryFactory"/> so that it will no longer be used to try to parse non-native data files from other applications.
+		/// </summary>
+		/// <param name="factory">
+		/// The <see cref="INonNativeWarFoundryFactory"/> to remove from the collection of factories that are used to try to parse non-native data files.
+		/// </param>
+		public void UnregisterNonNativeFactory(INonNativeWarFoundryFactory factory)
+		{
+			if (nonNativeFactories.Contains(factory))
+			{
+				nonNativeFactories.Remove(factory);
+			}
+		}
+		
+		/// <summary>
+		/// Loads all of the data files in the registered directories.
+		/// </summary>
+		public void LoadFiles()
+		{
+			LogNotifier.Debug(GetType(), "Load files");
+			PrepareForFileLoad();
+			
+			foreach (DirectoryInfo directory in directories)
+			{
+				LogNotifier.Debug(GetType(), "Load from "+directory.FullName);
+				
+				foreach (FileInfo file in directory.GetFiles())
+				{
+					LoadFile(file);
+				}
+			}
+			
+			ICollection<Race> races = new List<Race>(); 
+			
+			foreach (SimpleSet<IWarFoundryObject> objs in loadedObjects.Values)
+			{			
+				foreach (IWarFoundryObject obj in  objs)
+				{
+					if (obj is Race)
+					{
+						races.Add((Race)obj);
+					}
+					else if (obj is GameSystem)
+					{
+						StoreGameSystem((GameSystem)obj);
+					}
+				}
+			}
+			
+			foreach (Race race in races)
+			{
+				StoreRace(race);
+			}					
+		}
+		
+		protected void PrepareForFileLoad()
+		{
+			//Just set up blank dictionaries for now - may try different and more complex handling in future
+			systemsTable = new Dictionary<string,GameSystem>();
+			racesTable = new Dictionary<string,Dictionary<string,Dictionary<string,Race>>>();
+		}
+		
+		/// <summary>
+		/// Loads a single file through the registered WarFoundryFactories, if a factory exists that supports the file format.
+		/// </summary>
+		/// <param name="file">
+		/// A <see cref="FileInfo"/> for the file to attempt to load
+		/// </param>
+		protected void LoadFile(FileInfo file)
+		{
+			bool handled = false;
+			
+			if (!handled)
+			{
+				ICollection<IWarFoundryObject> objs = null;
+				IWarFoundryFactory loadFactory = null;
+				
+				if (nonNativeFactories.Count > 0)
+				{
+					LogNotifier.Debug(GetType(), "Attempting to load "+file.FullName+" as a non-native file");
+					
+					foreach (INonNativeWarFoundryFactory factory in nonNativeFactories)
+					{
+						LogNotifier.Debug(GetType(), "Load using "+factory.GetType().AssemblyQualifiedName+"? " + (factory.CanHandleFileFormat(file) ? "yes" : "no"));
+						
+						if (factory.CanHandleFileFormat(file))
+						{
+							objs = factory.CreateObjectsFromFile(file);
+							
+							if (objs!=null)
+							{
+								loadFactory = factory;
+								break;
+							}
+						}			         
+					}
+				}
+				
+				if (objs == null)
+				{
+					LogNotifier.Debug(GetType(), "Attempting to load "+file.FullName+" as native file");
+					
+					foreach (INativeWarFoundryFactory factory in factories)
+					{
+						if (factory.CanHandleFileFormat(file))
+						{
+							objs = factory.CreateObjectsFromFile(file);
+							
+							if (objs!=null)
+							{
+								loadFactory = factory;
+								break;
+							}
+						}
+					}
+				}
+					
+				if (objs!=null)
+				{
+					AddLoadedObjects(objs, loadFactory);
+				}
+			}
+		}
+		
+		private void AddLoadedObjects(ICollection<IWarFoundryObject> loadedObjs, IWarFoundryFactory factory)
+		{
+			SimpleSet<IWarFoundryObject> objs;
+			loadedObjects.TryGetValue(factory, out objs);
+			
+			if (objs == null)
+			{
+				objs = new SimpleSet<IWarFoundryObject>();
+				loadedObjects.Add(factory, objs);
+			}
+				
+			objs.AddRange(loadedObjs);
+		}
+		
+		private void AddLoadedObject(IWarFoundryObject obj, IWarFoundryFactory factory)
+		{
+			SimpleSet<IWarFoundryObject> objs;
+			loadedObjects.TryGetValue(factory, out objs);
+			
+			if (objs == null)
+			{
+				objs = new SimpleSet<IWarFoundryObject>();
+				loadedObjects.Add(factory, objs);
+			}
+				
+			objs.Add(obj);
+		}
+		
+		protected virtual ZipFile MakeZipFile(FileInfo file)
+		{
+			return new ZipFile(file.FullName);
+		}
+		
+		protected void StoreGameSystem(GameSystem system)
+		{
+			string sysid = system.ID.ToLower();
+					
+			if (systemsTable.ContainsKey(sysid))
+			{
+				LogNotifier.WarnFormat(GetType(), "System {0} ({1}) has already been loaded. Duplicate file ({3}) discarded", system.Name, system.ID, system.SourceFile.FullName);
+			}
+			else
+			{
+				systemsTable.Add(sysid, (GameSystem)system);
+			}
+		}
+		
+		
+		/// <summary>
+		/// Stores a loaded <see cref="GameSystem"/> that has been generated outside the core loading structure.
+		/// 
+		/// Note: Calls to this function should be made before calls to StoreRace(Race, IWarFoundryFactory).
+		/// </summary>
+		/// <param name="system">
+		/// The <see cref="GameSystem"/> to register as loaded.
+		/// </param>
+		/// <param name="factory">
+		/// The <see cref="IWarFoundryFactory"/> that created it.
+		/// </param>
+		public void StoreGameSystem(GameSystem system, IWarFoundryFactory factory)
+		{
+			AddLoadedObject(system, factory);	
+			StoreGameSystem(system);
+		}
+		
+		protected void StoreRace(Race race)
+		{
+			Dictionary<string, Dictionary<string, Race>> systemRaces;
+
+			if (race.GameSystem == null)
+			{
+				throw new InvalidOperationException("Race cannot have null game system. Game system should be loaded before race.");
+			}
+			
+			string systemID = race.GameSystem.ID.ToLower();
+			racesTable.TryGetValue(systemID, out systemRaces);
+			
+			if (systemRaces==null)
+			{
+				systemRaces = new Dictionary<string,Dictionary<string,Race>>();
+				racesTable.Add(systemID, systemRaces);
+			}
+			
+			Dictionary<string, Race> subRaces;
+			systemRaces.TryGetValue(race.ID.ToLower(), out subRaces);
+			
+			if (subRaces==null)
+			{
+				subRaces = new Dictionary<string,Race>();
+				systemRaces.Add(race.ID.ToLower(), subRaces);
+			}
+
+			if (subRaces.ContainsKey(race.SubID.ToLower()))
+			{
+				LogNotifier.WarnFormat(GetType(), "Race {0} ({1} - {2}) for system {3} ({4}) has already been loaded. Duplicate file ({5}) discarded", race.Name, race.ID, race.SubID, race.GameSystem.ID, race.GameSystem.Name, race.SourceFile.Name);
+				race = null;
+			}
+			else
+			{
+				subRaces.Add(race.SubID.ToLower(), race);
+			}
+		}
+		
+		/// <summary>
+		/// Stores a loaded <see cref="Race"/> that has been generated outside the core loading structure.
+		/// 
+		/// Note: Calls to this function should ensure that the relevant <see cref="GameSystem"> has been created first.
+		/// </summary>
+		/// <param name="race">
+		/// The <see cref="Race"/> to register as loaded.
+		/// </param>
+		/// <param name="factory">
+		/// The <see cref="IWarFoundryFactory"/> that created it
+		/// </param>
+		public void StoreRace(Race race, IWarFoundryFactory factory)
+		{
+			AddLoadedObject(race, factory);
+			StoreRace(race);
+		}
+		
+		/// <summary>
+		/// Gets all <see cref="GameSystem"/>s that are currently available, determined by those that can be loaded with the current <see cref="IWarFoundryFactory"/>s. 
+		/// </summary>
+		/// <returns>
+		/// An array of <see cref="GameSystem"/>s that are currently available.
+		/// </returns>
+		public GameSystem[] GetGameSystems()
+		{
+			if (systemsTable==null)
+			{
+				LoadFiles();
+			}
+			
+			GameSystem[] systems = new GameSystem[systemsTable.Count];
+			int iSys = 0;
+			
+			foreach (GameSystem sys in systemsTable.Values)
+			{
+				systems[iSys++] = sys;
+			}
+			
+			return systems;
+		}
+
+		/// <summary>
+		/// Gets a single <see cref="GameSystem"/> with a given ID. 
+		/// </summary>
+		/// <param name="systemID">
+		/// The ID of the <see cref="GameSystem"/> to get, as a <see cref="System.String"/>.
+		/// </param>
+		/// <returns>
+		/// The <see cref="GameSystem"/> with the given ID, or <code>null</code> if one doesn't exist.
+		/// </returns>
+		public GameSystem GetGameSystem(string systemID)
+		{
+			if (systemsTable==null)
+			{
+				LoadFiles();
+			}
+			
+			GameSystem system;
+			systemsTable.TryGetValue(systemID.ToLower(), out system);
+			return system;
+		}
+
+		/// <summary>
+		/// Gets an array of the races for the specified <see cref="GameSystem"/>.
+		/// </summary>
+		/// <param name="system">
+		/// The <see cref="GameSystem"/> to get the available races for.
+		/// </param>
+		/// <returns>
+		/// An array of <see cref="Race"/>s for the <see cref="GameSystem"/>
+		/// </returns>
+		public Race[] GetRaces(GameSystem system)
+		{
+			return GetRaces(system.ID);
+		}
+
+		/// <summary>
+		/// Gets an array of the races for a game system by ID.
+		/// </summary>
+		/// <param name="systemID">
+		/// The <see cref="System.String"/> ID of the game system to get races for
+		/// </param>
+		/// <returns>
+		/// An array of <see cref="Race"/>s for the specified game system
+		/// </returns>
+		public Race[] GetRaces(string systemID)
+		{
+			if (racesTable==null)
+			{
+				LoadFiles();
+			}
+			
+			systemID = systemID.ToLower();
+			
+			foreach (string key in racesTable.Keys)
+			{
+				Console.WriteLine(key);
+			}
+
+			Dictionary<string, Dictionary<string, Race>> system;
+			racesTable.TryGetValue(systemID, out system);
+			
+			if (system==null)
+			{
+				return new Race[0];
+			}
+
+			int count = 0;
+
+			foreach (Dictionary<string, Race> racesDict in system.Values)
+			{
+				count+= racesDict.Count;
+			}
+
+			Race[] races = new Race[count];
+			int i = 0;
+
+			foreach (string raceID in system.Keys)
+			{
+				foreach (string raceSubId in system[raceID].Keys)
+				{
+					races[i++] = GetRace(systemID, raceID, raceSubId);
+				}
+			}
+
+			return races;
+		}
+
+		/// <summary>
+		/// Gets a single race for a given <see cref="GameSystem"/> by ID of the race.
+		/// </summary>
+		/// <param name="system">
+		/// The <see cref="GameSystem"/> that the race is part of.
+		/// </param>
+		/// <param name="raceID">
+		/// A <see cref="System.String"/> ID for the race to load.
+		/// </param>
+		/// <returns>
+		/// A <see cref="Race"/> with the specified ID from the <see cref="GameSystem"/>, or <code>null</code> if one doesn't exist.
+		/// </returns>
+		public Race GetRace(GameSystem system, string raceID)
+		{
+			return GetRace(system.ID, raceID);
+		}
+
+		/// <summary>
+		/// Gets a single race for a given game system by ID of the game system and race.
+		/// </summary>
+		/// <param name="systemID">
+		/// The <see cref="System.String"/> ID of the game system that the race is part of.
+		/// </param>
+		/// <param name="raceID">
+		/// The <see cref="System.String"/> ID for the race to load.
+		/// </param>
+		/// <returns>
+		/// A <see cref="Race"/> with the specified ID from the game system with the specified ID, or <code>null</code> if there is no race or game system with those IDs.
+		/// </returns>
+		public Race GetRace(string systemID, string raceID)
+		{
+			return GetRace(systemID, raceID, "");
+		}
+
+		/// <summary>
+		/// Gets a single race for a given <see cref="GameSystem"/> by the race's ID and sub-race ID.
+		/// </summary>
+		/// <param name="system">
+		/// The <see cref="GameSystem"/> that the race is part of.
+		/// </param>
+		/// <param name="raceID">
+		/// The <see cref="System.String"/> ID for the race to load.
+		/// </param>
+		/// <param name="raceSubID">
+		/// A <see cref="System.String"/>
+		/// </param>
+		/// <returns>
+		/// A <see cref="Race"/>
+		/// </returns>
+		public Race GetRace(GameSystem system, string raceID, string raceSubID)
+		{
+			return GetRace(system.ID, raceID, raceSubID);
+		}
+
+		/// <summary>
+		/// Gets a single race for a given game system by the game system's ID and the race's ID and sub-race ID.
+		/// </summary>
+		/// <param name="systemID">
+		/// The <see cref="System.String"/> ID of the game system that the race is part of.
+		/// </param>
+		/// <param name="raceID">
+		/// The <see cref="System.String"/> ID for the race to load.
+		/// </param>
+		/// <param name="raceSubID">
+		/// A <see cref="System.String"/>
+		/// </param>
+		/// <returns>
+		/// A <see cref="Race"/>
+		/// </returns>
+		public Race GetRace(string systemID, string raceID, string raceSubID)
+		{
+			if (racesTable==null)
+			{
+				LoadFiles();
+			}
+			
+			Race race = null;
+			
+			systemID = systemID.ToLower();
+
+			Dictionary<string, Dictionary<string, Race>> races;
+			racesTable.TryGetValue(systemID, out races);
+
+			if (races!=null)
+			{
+				Dictionary<string, Race> subraces;
+				races.TryGetValue(raceID, out subraces);
+
+				if (subraces!=null)
+				{
+					subraces.TryGetValue(raceSubID, out race);
+				}
+			}
+			
+			return race;
+		}
+
+		/// <summary>
+		/// Gets the IDs of all of the game systems currently available.
+		/// </summary>
+		/// <returns>
+		/// An array of <see cref="System.String"/>s representing the IDs of the game systems.
+		/// </returns>
+		public string[] GetGameSystemIDs()
+		{
+			if (systemsTable==null)
+			{
+				LoadFiles();
+			}
+
+			string[] keys = new string[systemsTable.Keys.Count];
+			int i = 0;
+
+			foreach (string key in systemsTable.Keys)
+			{
+				keys[i++] = key;
+			}
+
+			return keys;
+		}
+		
+		/// <summary>
+		/// Gets the IDs of all of the races of a specified game system.
+		/// </summary>
+		/// <param name="system">
+		/// The <see cref="GameSystem"/> to get the available races for.
+		/// </param>
+		/// <returns>
+		/// An array of <see cref="System.String"/>s representing the IDs of the races of the specified game system.
+		/// </returns>
+		public string[] GetSystemRaceIDs(GameSystem system)
+		{
+			return GetSystemRaceIDs(system.ID);
+		}
+
+		/// <summary>
+		/// Gets the IDs of all of the races of a specified game system.
+		/// </summary>
+		/// <param name="systemID">
+		/// The <see cref="System.String"/> ID of the game system to get the available races for.
+		/// </param>
+		/// <returns>
+		/// An array of <see cref="System.String"/>s representing the IDs of the races of the specified game system.
+		/// </returns>
+		public string[] GetSystemRaceIDs(string systemID)
+		{
+			if (racesTable == null)
+			{
+				LoadFiles();
+			}
+
+			Dictionary<string, Dictionary<string, Race>> races = racesTable[systemID.ToLower()];
+
+			if (races==null)
+			{
+				return new string[0];
+			}
+			else
+			{
+				string[] keys = new string[races.Keys.Count];
+				int i = 0;
+
+				foreach (string key in races.Keys)
+				{
+					keys[i++] = key;
+				}
+
+				return keys;
+			}
+		}
+	}
+}