changeset 8:613bc5eaac59

Re #9 - Make WarFoundry loading granular * Remove specific staged loading classes * Rework category loading for GameSystem and Race to make it use AddCategory(Category) method * Promote staged loading from Native Factory to all Factories level * Refactor XML Factory to use smaller methods Also removed some commented code that isn't used any more
author IBBoard <dev@ibboard.co.uk>
date Sun, 04 Jan 2009 19:24:13 +0000
parents 895c8a2378a1
children 6ad505b6c36e
files IBBoard.WarFoundry.API.mdp api/Factories/AbstractNativeWarFoundryFactory.cs api/Factories/AbstractWarFoundryFactory.cs api/Factories/IWarFoundryFactory.cs api/Factories/Xml/WarFoundryXmlFactory.cs api/Objects/Army.cs api/Objects/Category.cs api/Objects/GameSystem.cs api/Objects/IWarFoundryNativeSourceObject.cs api/Objects/IWarFoundryObject.cs api/Objects/IWarFoundryStagedLoadObject.cs api/Objects/Race.cs api/Objects/StagedLoadingGameSystem.cs api/Objects/StagedLoadingRace.cs api/Objects/UnitType.cs api/Objects/WarFoundryObject.cs api/Objects/WarFoundryStagedLoadingObject.cs api/WarFoundryLoader.cs
diffstat 18 files changed, 334 insertions(+), 646 deletions(-) [+]
line wrap: on
line diff
--- a/IBBoard.WarFoundry.API.mdp	Sun Jan 04 13:12:55 2009 +0000
+++ b/IBBoard.WarFoundry.API.mdp	Sun Jan 04 19:24:13 2009 +0000
@@ -57,7 +57,6 @@
     <File name="api/Objects/Category.cs" subtype="Code" buildaction="Compile" />
     <File name="api/Objects/EquipmentItem.cs" subtype="Code" buildaction="Compile" />
     <File name="api/Objects/GameSystem.cs" subtype="Code" buildaction="Compile" />
-    <File name="api/Objects/StagedLoadingRace.cs" subtype="Code" buildaction="Compile" />
     <File name="api/Objects/Stat.cs" subtype="Code" buildaction="Compile" />
     <File name="api/Objects/Stats.cs" subtype="Code" buildaction="Compile" />
     <File name="api/Objects/StatSlot.cs" subtype="Code" buildaction="Compile" />
@@ -66,7 +65,6 @@
     <File name="api/Objects/UnitEquipmentItem.cs" subtype="Code" buildaction="Compile" />
     <File name="api/Objects/UnitEquipmentItemObj.cs" subtype="Code" buildaction="Compile" />
     <File name="api/Objects/UnitType.cs" subtype="Code" buildaction="Compile" />
-    <File name="api/Objects/IWarFoundryStagedLoadObject.cs" subtype="Code" buildaction="Compile" />
     <File name="api/Objects/WarFoundryObject.cs" subtype="Code" buildaction="Compile" />
     <File name="api/Factories/Xml" subtype="Directory" buildaction="Compile" />
     <File name="api/Factories/Xml/WarFoundryXmlSaver.cs" subtype="Code" buildaction="Compile" />
@@ -84,10 +82,11 @@
     <File name="api/Objects/Race.cs" subtype="Code" buildaction="Compile" />
     <File name="api/Objects/IWarFoundryNativeSourceObject.cs" subtype="Code" buildaction="Compile" />
     <File name="api/Objects/IWarFoundryObject.cs" subtype="Code" buildaction="Compile" />
-    <File name="api/Objects/StagedLoadingGameSystem.cs" subtype="Code" buildaction="Compile" />
     <File name="dtds/army.dtd" subtype="Code" buildaction="Nothing" />
     <File name="dtds/race.dtd" subtype="Code" buildaction="Nothing" />
     <File name="dtds/system.dtd" subtype="Code" buildaction="Nothing" />
+    <File name="api/Objects/WarFoundryStagedLoadingObject.cs" subtype="Code" buildaction="Compile" />
+    <File name="api/Objects/IWarFoundryStagedLoadObject.cs" subtype="Code" buildaction="Compile" />
   </Contents>
   <References>
     <ProjectReference type="Gac" localcopy="True" refto="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
--- a/api/Factories/AbstractNativeWarFoundryFactory.cs	Sun Jan 04 13:12:55 2009 +0000
+++ b/api/Factories/AbstractNativeWarFoundryFactory.cs	Sun Jan 04 19:24:13 2009 +0000
@@ -47,9 +47,7 @@
 		{
 			//Do nothing - just make the constructor non-public
 		}
-		
-		public abstract void CompleteLoading(IWarFoundryStagedLoadObject obj);
-		
+				
 		protected override ZipFile GetFileAsSupportedType (FileInfo file)
 		{
 			ZipFile zip = null;
--- a/api/Factories/AbstractWarFoundryFactory.cs	Sun Jan 04 13:12:55 2009 +0000
+++ b/api/Factories/AbstractWarFoundryFactory.cs	Sun Jan 04 19:24:13 2009 +0000
@@ -26,7 +26,12 @@
 namespace IBBoard.WarFoundry.API.Factories
 {
 	public abstract class AbstractWarFoundryFactory<FILE_TYPE> : IWarFoundryFactory
-	{
+	{		
+		public virtual void CompleteLoading(IWarFoundryStagedLoadObject obj)
+		{
+			//Default to doing nothing as this will probably be standard for non-native factories and some native factories
+		}
+			
 		public bool CanHandleFileFormat (FileInfo file)
 		{
 			return CheckCanHandleFileFormat(GetFileAsSupportedType(file));
--- a/api/Factories/IWarFoundryFactory.cs	Sun Jan 04 13:12:55 2009 +0000
+++ b/api/Factories/IWarFoundryFactory.cs	Sun Jan 04 19:24:13 2009 +0000
@@ -28,6 +28,14 @@
 	public interface IWarFoundryFactory
 	{
 		/// <summary>
+		/// Completes the loading of an object if it is loaded in stages.
+		/// </summary>
+		/// <param name="obj">
+		/// The <see cref="IWarFoundryStagedLoadObject"/> that should be fully loaded.
+		/// </param>
+		void CompleteLoading(IWarFoundryStagedLoadObject obj);
+		
+		/// <summary>
 		/// Checks if the factory thinks it can handle the supplied file. Checks can be performed on file extension or some basic check of file content, or some other method.
 		/// </summary>
 		/// <param name="file">
--- a/api/Factories/Xml/WarFoundryXmlFactory.cs	Sun Jan 04 13:12:55 2009 +0000
+++ b/api/Factories/Xml/WarFoundryXmlFactory.cs	Sun Jan 04 19:24:13 2009 +0000
@@ -132,7 +132,7 @@
 		{
 			string id = elem.GetAttribute("id");
 			string name = elem.GetAttribute("name");
-			GameSystem system = new StagedLoadingGameSystem(id, name, this);
+			GameSystem system = new GameSystem(id, name, this);
 			//system.SourceZipFile = file.;
 			extraData[system] = elem.OwnerDocument;
 			return system;
@@ -156,151 +156,121 @@
 			string subid = elem.GetAttribute("subid");
 			string systemID = elem.GetAttribute("system");
 			string name = elem.GetAttribute("name");
-			Race race = new StagedLoadingRace(id, subid, name, systemID, this);
+			Race race = new Race(id, subid, name, systemID, this);
 			//race.SourceZipFile = file; //TODO reference source file
 			extraData[race] = elem.OwnerDocument;
 			return race;
-		}
+		}
 
-		/*public WarFoundryObject CreateObjectFromStream(ZipFile file, Stream stream)
-		{
-			try
+		public override void CompleteLoading(IWarFoundryStagedLoadObject obj)
+		{			
+			LogNotifier.DebugFormat(GetType(), "Complete loading of {0} with ID {1}", obj.GetType().Name, obj.ID);
+							
+			if (obj is GameSystem)
+			{
+				CompleteLoading((GameSystem)obj);
+			}
+			else if (obj is Race)
 			{
-				WarFoundryObject obj = LoadFileObjectFromElement(file, elem);
+				CompleteLoading((Race)obj);
+			}
+		}
+	
+		public XmlDocument GetExtraData(IWarFoundryObject obj)
+		{
+			XmlDocument extra = null;
+			extraData.TryGetValue(obj, out extra);
+			return extra;
+		}
+		
+		public void CompleteLoading(GameSystem system)
+		{
+			if (system.IsFullyLoaded)
+			{
+				LogNotifier.DebugFormat(GetType(), "Object of type GameSystem with ID {0} is already fully loaded", system.ID);
+				return;
+			}
+			
+			XmlDocument extra = GetExtraData(system);
+			XmlNode elem = extra.LastChild;
+			
+			XmlNode catsElem = elem.FirstChild;
+			WarFoundryObject tempObj;
+			
+			foreach (XmlElement cat in catsElem.ChildNodes)
+			{
+				tempObj = CreateObjectFromElement(cat);
 				
-				if (obj != null)
-				{			
-					extraData[obj] = doc;
-					return obj;
+				if (tempObj is Category)
+				{
+					system.AddCategory((Category)tempObj);
 				}
 				else
 				{
-					throw new InvalidFileException(String.Format(Translation.GetTranslation("ErrorInvalidXmlFile", "XML file '{0}' was not a loadable XML file. Please ensure it is a valid and supported WarFoundry XML file."), file.Name));
+					LogNotifier.WarnFormat(GetType(), "Object for string {0} was not of type Category", cat.OuterXml);
 				}
-			}
-			catch (XmlSchemaException ex)
-			{
-				throw new InvalidFileException(String.Format(Translation.GetTranslation("ErrorInvalidXmlFile", "Failed to parse invalid XML data file in {0}. Error at line {1} position {2}: {3}"), file.Name, ex.LineNumber, ex.LinePosition, ex.Message.Substring(0, ex.Message.IndexOf(".")+1)), ex);
 			}
-			catch (InvalidFileException ex)
-			{
-				throw new InvalidFileException(String.Format(Translation.GetTranslation("ErrorInvalidNamedXmlFile", "XML data file in '{0}' was not a valid XML file. It should contain three child nodes - XML definition, DocType and root node."), file.Name), ex);
-			}
+						
+			XmlElement statsElem = (XmlElement)catsElem.NextSibling;					
+			LoadSystemStatsFromElement(statsElem, system);
+			string defaultStatsID = statsElem.GetAttribute("defaultStats");
+								
+			LogNotifier.DebugFormat(GetType(), "Completed loading of GameSystem with ID {0}", system.Name);
+			system.StandardSystemStatsID = defaultStatsID;
+			system.SetAsFullyLoaded();
 		}
 		
-		private WarFoundryObject LoadFileObjectFromElement(ZipFile file, XmlElement elem)
+		public void CompleteLoading(Race race)
 		{
-			WarFoundryObject obj = null;
-				
-			if (elem.Name.Equals(WarFoundryXmlElementName.SYSTEM_ELEMENT.Value))
+			if (race.IsFullyLoaded)
 			{
-				logger.Debug("Create GameSystem");
-				obj = CreateSystemFromElement(file, elem);
-			}
-			else if (elem.Name.Equals(WarFoundryXmlElementName.RACE_ELEMENT.Value))
-			{
-				logger.Debug("Create Race");
-				obj = CreateRaceFromElement(file, elem);
+				LogNotifier.DebugFormat(GetType(), "Object of type Race with ID {0} is already fully loaded", race.ID);
+				return;
 			}
 			
-			return obj;
-		}*/
-
-		public override void CompleteLoading(IWarFoundryStagedLoadObject obj)
-		{		
-			LogNotifier.DebugFormat(GetType(), "Complete loading of {0} with ID {1}", obj.GetType().Name, obj.ID);
-				
-			if (!obj.IsFullyLoaded)
-			{			
-				XmlDocument extra = extraData[obj];
-				
-				if (obj is GameSystem)
+			XmlDocument extra = GetExtraData(race);
+			XmlNode elem = extra.LastChild;
+			XmlNode colNode = elem.FirstChild;
+			
+			Dictionary<string, UnitType> unitTypes = new Dictionary<string, UnitType>();
+			
+			foreach (XmlElement node in colNode.ChildNodes)
+			{
+				UnitType type = CreateUnitTypeFromElement(node, race, race.GameSystem);
+				unitTypes.Add(type.ID, type);
+			}
+			
+			colNode = colNode.NextSibling;
+			
+			if (colNode!=null && colNode.Name == WarFoundryXmlElementName.CATEGORIES_ELEMENT.Value)
+			{
+				foreach (XmlElement node in colNode.ChildNodes)
 				{
-					GameSystem system = (GameSystem)obj;
-					XmlNode elem = extra.LastChild;
-					
-					XmlNode catsElem = elem.FirstChild;					
-					Category[] cats;
-					List<Category> catList = new List<Category>();
-					WarFoundryObject tempObj;
-					
-					foreach (XmlElement cat in catsElem.ChildNodes)
-					{
-						tempObj = CreateObjectFromElement(cat);
-						
-						if (tempObj is Category)
-						{
-							catList.Add((Category)tempObj);
-						}
-						else
-						{
-							LogNotifier.WarnFormat(GetType(), "Object for string {0} was not of type Category", cat.OuterXml);
-						}
-					}
-					
-					cats = catList.ToArray();
-					LogNotifier.DebugFormat(GetType(), "Found {0} categories", cats.Length);
-					
-					XmlElement statsElem = (XmlElement)catsElem.NextSibling;					
-					LoadSystemStatsFromElement(statsElem, system);
-					string defaultStatsID = statsElem.GetAttribute("defaultStats");
-										
-					LogNotifier.DebugFormat(GetType(), "Complete loading of {0}", system.Name);
-					system.Categories = cats;
-					system.StandardSystemStatsID = defaultStatsID;
+					race.AddCategory(CreateCategoryFromElement(node));
 				}
-				else if (obj is Race)
+				
+				colNode = colNode.NextSibling;
+			}
+				
+			Dictionary<string, EquipmentItem> raceEquipment = new Dictionary<string, EquipmentItem>();
+			
+			if (colNode!=null && colNode.Name == WarFoundryXmlElementName.RACE_EQUIPMENT_ITEMS_ELEMENT.Value)
+			{
+				foreach (XmlElement node in colNode.ChildNodes)
 				{
-					Race race = (Race)obj;
-					XmlNode elem = extra.LastChild;
-					XmlNode colNode = elem.FirstChild;
-					
-					Dictionary<string, UnitType> unitTypes = new Dictionary<string, UnitType>();
-					
-					foreach (XmlElement node in colNode.ChildNodes)
-					{
-						UnitType type = CreateUnitTypeFromElement(node, race, race.GameSystem);
-						unitTypes.Add(type.ID, type);
-					}
-					
-					colNode = colNode.NextSibling;
-					List<Category> catOverrides = new List<Category>();
-					
-					if (colNode!=null && colNode.Name == WarFoundryXmlElementName.CATEGORIES_ELEMENT.Value)
-					{
-						foreach (XmlElement node in colNode.ChildNodes)
-						{
-							catOverrides.Add(CreateCategoryFromElement(node));
-						}
-						
-						colNode = colNode.NextSibling;
-					}
-						
-					Dictionary<string, EquipmentItem> raceEquipment = new Dictionary<string, EquipmentItem>();
-					
-					if (colNode!=null && colNode.Name == WarFoundryXmlElementName.RACE_EQUIPMENT_ITEMS_ELEMENT.Value)
-					{
-						foreach (XmlElement node in colNode.ChildNodes)
-						{
-							EquipmentItem item = CreateEquipmentItemFromElement(node, race);
-							raceEquipment.Add(item.ID, item);
-						}
-					}
-					
-					Dictionary<string, Ability> raceAbilities = new Dictionary<string, Ability>();
-					//TODO: Load abilities
-					
-					LogNotifier.DebugFormat(GetType(), "Complete loading of {0}", race.Name);
-					race.Categories = catOverrides.ToArray();
-					race.SetUnitTypes(unitTypes);
-					race.SetEquipmentItems(raceEquipment);
-					race.SetAbilities(raceAbilities);
+					EquipmentItem item = CreateEquipmentItemFromElement(node, race);
+					raceEquipment.Add(item.ID, item);
 				}
 			}
-			else
-			{
-				LogNotifier.Debug(GetType(), "Object is already fully loaded");
-			}
+			
+			Dictionary<string, Ability> raceAbilities = new Dictionary<string, Ability>();
+			//TODO: Load abilities
+			
+			race.SetUnitTypes(unitTypes);
+			race.SetEquipmentItems(raceEquipment);
+			race.SetAbilities(raceAbilities);
+			race.SetAsFullyLoaded();
+			LogNotifier.DebugFormat(GetType(), "Completed loading of Race with ID {0}", race.Name);
 		}
 		
 		protected XmlDocument CreateXmlDocumentFromStream(Stream stream)
--- a/api/Objects/Army.cs	Sun Jan 04 13:12:55 2009 +0000
+++ b/api/Objects/Army.cs	Sun Jan 04 19:24:13 2009 +0000
@@ -28,11 +28,11 @@
 		public event DoubleValChangedDelegate PointsValueChanged;
 		private DoubleValChangedDelegate PointsValueChangedMethod;
 		
-		public Army(Race race, string armyName, int maxArmyPoints/*, AbstractNativeWarFoundryFactory factory*/) : this(race, armyName, maxArmyPoints, null/*, factory*/)
+		public Army(Race race, string armyName, int maxArmyPoints) : this(race, armyName, maxArmyPoints, null/*, factory*/)
 		{
 		}
 
-		public Army(Race race, string armyName, int maxArmyPoints, ZipFile file/*, AbstractNativeWarFoundryFactory factory*/) : base(armyName/*, factory*/)
+		public Army(Race race, string armyName, int maxArmyPoints, ZipFile file) : base(armyName)
 		{
 			armyRace = race;
 			Name = armyName;
--- a/api/Objects/Category.cs	Sun Jan 04 13:12:55 2009 +0000
+++ b/api/Objects/Category.cs	Sun Jan 04 19:24:13 2009 +0000
@@ -21,86 +21,12 @@
 			incVal = incrementValue;
 			incAmount = incrementAmount;
 		}
-		
-		/*public Category(XmlElement node, GameSystem gameSystem, AbstractNativeWarFoundryFactory factory) : base(factory)
-		{
-			//system = gameSystem;
-			ID = node.GetAttribute("id");
-			Name = node.GetAttribute("name");
-
-			try
-			{
-				min = int.Parse(node.GetAttribute("minimum"));
-			}
-			catch(FormatException)
-			{
-				throw new FormatException("Attribute 'minimum' of category "+id+" was not a valid number");
-			}
-
-			try
-			{
-				max = int.Parse(node.GetAttribute("maximum"));
-			}
-			catch(FormatException)
-			{
-				throw new FormatException("Attribute 'maximum' of category "+id+" was not a valid number");
-			}
-
-			string val = "";
-			val = node.GetAttribute("baseValue");
-
-			if (val!="")
-			{
-				try
-				{
-					baseValue = int.Parse(val);
-				
-				}
-				catch(FormatException)
-				{
-					throw new FormatException("Attribute 'baseValue' of category "+id+" was not a valid number");
-				}
-			}
-
-			val = node.GetAttribute("incValue");
-
-			if (val!="")
-			{
-				try
-				{
-					incValue = int.Parse(val);
-				}
-				catch(FormatException)
-				{
-					throw new FormatException("Attribute 'incValue' of category "+id+" was not a valid number");
-				}
-			}
-
-			val = node.GetAttribute("incAmount");
-
-			if (val!="")
-			{
-				try
-				{
-					incAmount = int.Parse(val);
-				}
-				catch(FormatException)
-				{
-					throw new FormatException("Attribute 'incAmount' of category "+id+" was not a valid number");
-				}
-			}
-		}*/
 
 		protected override string DefaultName()
 		{
 			return "";
 		}
 
-		/*public GameSystem GameSystem
-		{
-			get { return system; }
-		}*/
-
 		public int MinimumPoints
 		{
 			get { return minPts; }
--- a/api/Objects/GameSystem.cs	Sun Jan 04 13:12:55 2009 +0000
+++ b/api/Objects/GameSystem.cs	Sun Jan 04 19:24:13 2009 +0000
@@ -1,3 +1,21 @@
+// GameSystem.cs is a part of the IBBoard.WarFoundry.API library (referred to from here as "this program")
+// 
+// Copyright (C) 2009 IBBoard
+//
+// This program 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 3 of the License, or
+// (at your option) any later version.
+//
+// This program 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 program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
 using System;
 using System.Collections.Generic;
 using System.Xml;
@@ -11,15 +29,14 @@
 	/// <summary>
 	/// Summary description for GameSystem.
 	/// </summary>
-	public class GameSystem : WarFoundryObject // WarFoundryStagedLoadSourceObject
+	public class GameSystem : WarFoundryStagedLoadingObject
 	{
 		private bool warnOnError;
-		private Category[] categories;
-		private Dictionary<string, SystemStats> stats;
-		private string defaultStats;
-		private FileInfo sourceFile;
+		private Dictionary<string, Category> categories = new Dictionary<string,Category>();
+		private Dictionary<string, SystemStats> stats = new Dictionary<string,SystemStats>();
+		private string defaultStats;
 
-		public GameSystem(string systemID, string systemName) : base(systemID, systemName)
+		public GameSystem(string systemID, string systemName, IWarFoundryFactory creatingFactory) : base(systemID, systemName, creatingFactory)
 		{
 			stats = new Dictionary<string,SystemStats>();
 		}
@@ -32,75 +49,31 @@
 			base.CompleteLoading();
 		}*/
 		
-		public FileInfo SourceFile
+		public void AddCategory(Category cat)
 		{
-			get { return sourceFile; }
-			set { sourceFile = value; }
-		}
-		
-		public int GetCategoryCount()
-		{
-			return categories.Length;
-		}
-
-		public Category GetCategory(int index)
-		{
-			return categories[index];
+			RawCategories[cat.ID] = cat;
 		}
 
 		public Category GetCategory(string id)
 		{
-			Category categoryForID = null;
-			
-			for (int i = 0; i<Categories.Length; i++)
-			{
-				Category cat = Categories[i]; 
-				if (cat.ID == id)
-				{
-					categoryForID = cat;
-				}
-			}
-
-			return categoryForID;
+			EnsureFullyLoaded();
+			Category cat = null;
+			RawCategories.TryGetValue(id, out cat);
+			return cat;
 		}
 
 		public Category[] Categories
 		{
 			get 
-			{ 
-				return SystemCategories; 
-			}
-			set 
-			{
-				SystemCategories = value;
+			{ 
+				EnsureFullyLoaded();
+				return DictionaryToArrayConverter.Convert<string, Category>(RawCategories); 
 			}
 		}
 		
-		protected virtual Category[] SystemCategories
+		protected Dictionary<string, Category> RawCategories
 		{
-			get 
-			{
-				return RawSystemCategories;
-			}
-			set
-			{
-				RawSystemCategories = value;
-			}
-		}
-		
-		protected Category[] RawSystemCategories
-		{
-			get 
-			{ 
-				return categories; 
-			}
-			set 
-			{
-				if (value!=null)
-				{
-					categories = value;
-				}
-			}
+			get { return categories; }
 		}
 
 		public bool WarnOnError
@@ -145,6 +118,7 @@
 		{
 			get 
 			{ 
+				EnsureFullyLoaded();
 				SystemStats[] statsArray = new SystemStats[stats.Count];
 				stats.Values.CopyTo(statsArray, 0);
 				return statsArray;
@@ -153,6 +127,7 @@
 		
 		public SystemStats GetSystemStatsForID(string id)
 		{
+			EnsureFullyLoaded();
 			SystemStats statsForID;
 			stats.TryGetValue(id, out statsForID);
 			return statsForID;
@@ -184,7 +159,7 @@
 			{
 				GameSystem otherSystem = (GameSystem)obj;
 
-				return this.ID == otherSystem.ID && this.Name == otherSystem.Name && ((this.RawSystemCategories == null && otherSystem.RawSystemCategories == null) || this.RawSystemCategories.Equals(otherSystem.RawSystemCategories));
+				return this.ID == otherSystem.ID && this.Name == otherSystem.Name && ((this.RawCategories == null && otherSystem.RawCategories == null) || this.RawCategories.Equals(otherSystem.RawCategories));
 			}
 			else
 			{
@@ -194,7 +169,7 @@
 
 		public override int GetHashCode()
 		{
-			return ID.GetHashCode() + Name.GetHashCode() + (RawSystemCategories!=null ? RawSystemCategories.GetHashCode() : 0) + warnOnError.GetHashCode();
+			return ID.GetHashCode() + Name.GetHashCode() + (RawCategories!=null ? RawCategories.GetHashCode() : 0) + warnOnError.GetHashCode();
 		}
 
 		public bool UnitTypeMaxed(UnitType unitType, Army army)
--- a/api/Objects/IWarFoundryNativeSourceObject.cs	Sun Jan 04 13:12:55 2009 +0000
+++ b/api/Objects/IWarFoundryNativeSourceObject.cs	Sun Jan 04 19:24:13 2009 +0000
@@ -21,7 +21,7 @@
 using System;
 using ICSharpCode.SharpZipLib.Zip;
 
-namespace IBBoard.WarFoundry
+namespace IBBoard.WarFoundry.API.Objects
 {
 	/// <summary>
 	/// Interface for native WarFoundry objects that are the main object in a file and need to reference the file at a later date.
--- a/api/Objects/IWarFoundryObject.cs	Sun Jan 04 13:12:55 2009 +0000
+++ b/api/Objects/IWarFoundryObject.cs	Sun Jan 04 19:24:13 2009 +0000
@@ -1,31 +1,35 @@
-// IWarFoundryObject.cs
-//
-//  Copyright (C) 2008 IBBoard
-//
-// 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.
+// IWarFoundryObject is a part of the IBBoard.WarFoundry.API library (referred to from here as "this program")
 //
-// This library is distributed in the hope that it will be useful,
+// Copyright (C) 2009 IBBoard
+// 
+// This program 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 3 of the License, or
+// (at your option) any later version.
+// 
+// This program 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 
-//
-//
+// 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 program.  If not, see <http://www.gnu.org/licenses/>.
 
 using System;
+using IBBoard.WarFoundry.API.Factories;
 
-namespace IBBoard.WarFoundry
+namespace IBBoard.WarFoundry.API.Objects
 {
 	public interface IWarFoundryObject
 	{
+		/// <value>
+		/// The unique identifier for the object
+		/// </value>
 		string ID { get; set; }
-
+
+		/// <value>
+		/// The display name of the WarFoundry object
+		/// </value>
 		string Name { get; set;	}
 	}
 }
--- a/api/Objects/IWarFoundryStagedLoadObject.cs	Sun Jan 04 13:12:55 2009 +0000
+++ b/api/Objects/IWarFoundryStagedLoadObject.cs	Sun Jan 04 19:24:13 2009 +0000
@@ -15,11 +15,16 @@
 		/// <value>
 		/// Gets the <code>AbstractNativeWarFoundryFactory</code> that created the object.
 		/// </value>
-		AbstractNativeWarFoundryFactory Factory	{ get; }
+		IWarFoundryFactory Factory	{ get; }
 		
 		/// <value>
 		/// Returns <code>true</code> if the object has been fully loaded with all data, else returns <code>false</code>
 		/// </value>
 		bool IsFullyLoaded { get; }
+		
+		/// <summary>
+		/// Marks the object as fully loaded so that no more load checking is required.
+		/// </summary>
+		void SetAsFullyLoaded();
 	}
 }
--- a/api/Objects/Race.cs	Sun Jan 04 13:12:55 2009 +0000
+++ b/api/Objects/Race.cs	Sun Jan 04 19:24:13 2009 +0000
@@ -27,7 +27,7 @@
 
 namespace IBBoard.WarFoundry.API.Objects
 {
-	public class Race : WarFoundryObject
+	public class Race : WarFoundryStagedLoadingObject
 	{		
 		public static string SYSTEM_DEFAULT_RACE_ID = "GameDefault"; 
 		
@@ -35,17 +35,16 @@
 		private string systemID;
 		private GameSystem system;
 		private Dictionary<Category, Dictionary<string, UnitType>> unitTypesByCat;
-		private Dictionary<string, UnitType> unitTypes;
-		private Dictionary<string, EquipmentItem> equipment;
-		private Dictionary<string, Ability> abilities;
-		private Category[] cats;
-		private FileInfo sourceFile;
+		private Dictionary<string, UnitType> unitTypes = new Dictionary<string,UnitType>();
+		private Dictionary<string, EquipmentItem> equipment = new Dictionary<string,EquipmentItem>();
+		private Dictionary<string, Ability> abilities = new Dictionary<string,Ability>();
+		private Dictionary<string, Category> categories = new Dictionary<string,Category>();
 		
-		public Race(string raceID, string raceName, string gameSystemID) : this(raceID, "", raceName, gameSystemID)
+		public Race(string raceID, string raceName, string gameSystemID, IWarFoundryFactory creatingFactory) : this(raceID, "", raceName, gameSystemID, creatingFactory)
 		{
 		}
 		
-		public Race(string raceID, string raceSubID, string raceName, string gameSystemID) : base(raceID + (raceSubID!="" ? "_"+raceSubID : ""), raceName)
+		public Race(string raceID, string raceSubID, string raceName, string gameSystemID, IWarFoundryFactory creatingFactory) : base(raceID + (raceSubID!="" ? "_"+raceSubID : ""), raceName, creatingFactory)
 		{
 			subID = (raceSubID == null ? "" : raceSubID);
 			systemID = gameSystemID;
@@ -55,12 +54,6 @@
 		{
 			get { return subID; }
 			set { subID = (value == null ? "" : value.Trim()); }
-		}
-		
-		public FileInfo SourceFile
-		{
-			get { return sourceFile; }
-			set { sourceFile = value; }
 		}
 
 		public GameSystem GameSystem
@@ -83,6 +76,11 @@
 				
 				system = value;
 			}
+		}
+		
+		public void AddCategory(Category cat)
+		{
+			categories[cat.ID] = cat;
 		}
 
 		/// <summary>
@@ -96,74 +94,51 @@
 		/// </returns>
 		public Category GetCategory(string id)
 		{
-			Category[] categories = RaceCategories;
+			EnsureFullyLoaded();
 			Category cat = null;
-		
-			for (int i = 0; i<categories.Length; i++)
-			{
-				if (categories[i].ID == id)
-				{
-					cat = categories[i];
-					break;
-				}
+			categories.TryGetValue(id, out cat);
+			
+			if (cat == null)
+			{
+				cat = GameSystem.GetCategory(id);
 			}
-			
+						
 			return cat;
 		}
 
-		/// <summary>
-		/// Gets a category based on its index within the list. Ordering is defined by the game system definition or by the race's overrides.
-		/// </summary>
-		/// <param name="index">
-		/// A <see cref="System.Int32"/>
-		/// </param>
-		/// <returns>
-		/// A <see cref="Category"/>
-		/// </returns>
-		public virtual Category GetCategory(int index)
-		{
-			if (cats == null)
-			{
-				return GameSystem.GetCategory(index);
-			}
-			else
-			{
-				return Categories[index];
-			}
-		}
-		
 		public Category[] Categories
 		{
 			get 
-			{ 
-				return RaceCategories;
-			}
-			
-			set 
-			{
-				RaceOverrideCategories = value;
+			{ 
+				EnsureFullyLoaded();
+				Category[] cats;
+				
+				if (!HasCategoryOverrides())
+				{
+					cats = GameSystem.Categories;
+				}
+				else
+				{
+					cats = DictionaryToArrayConverter.Convert<string, Category>(categories);
+				}
+				
+				return cats;
 			}
+		}
+
+		public bool HasCategoryOverrides()
+		{
+			return categories.Count > 0;
 		}
 		
-		/*private virtual Category[] GetCategories()
-		{
-			if (cats==null)
-			{
-				return GameSystem.Categories;
-			}
-			else
-			{
-				return cats; 
-			}
-		}*/
-		
 		public void SetEquipmentItems(Dictionary<string, EquipmentItem> items)
 		{
 			equipment = items;
 		}
 
-		public virtual EquipmentItem GetEquipmentItem(string id)
-		{
+		public  EquipmentItem GetEquipmentItem(string id)
+		{
+			EnsureFullyLoaded();
 			return (EquipmentItem)equipment[id];
 		}
 		
@@ -177,25 +152,42 @@
 			}
 			
 			return items;
-		}
-
-		public virtual bool HasCategoryOverrides()
-		{
-			return cats!=null;
 		}
 		
 		public void SetUnitTypes(Dictionary<string, UnitType> types)
 		{
 			unitTypes = types;
+			unitTypesByCat = null;
 		}
 
 		public UnitType[] GetUnitTypes(Category cat)
 		{
 			if (unitTypesByCat==null)
 			{				
-				unitTypesByCat = new Dictionary<Category,Dictionary<string,UnitType>>();
+				BuildUnitTypesByCategoryCache();
+			}
+
+			Dictionary<string, UnitType> unitTypesDictionary;
+			unitTypesByCat.TryGetValue(cat, out unitTypesDictionary);
+			UnitType[] unitTypesArray;
+			
+			if (unitTypesDictionary == null)
+			{
+				unitTypesArray = new UnitType[0];
+			}
+			else
+			{
+				unitTypesArray = DictionaryToArrayConverter.Convert<string, UnitType>(unitTypesDictionary);
+			}
+			
+			return unitTypesArray;
+		}
+		
+		private void BuildUnitTypesByCategoryCache()
+		{
+			unitTypesByCat = new Dictionary<Category,Dictionary<string,UnitType>>();
 				
-				foreach (Category category in RaceCategories)
+				foreach (Category category in Categories)
 				{ 
 					unitTypesByCat.Add(category, new Dictionary<string, UnitType>());
 				}
@@ -213,18 +205,6 @@
 
 					catUnitTypes.Add(unit.ID, unit);
 				}
-			}
-
-			ICollection<UnitType> col = unitTypesByCat[cat].Values;
-			UnitType[] toRet = new UnitType[col.Count];
-			int i = 0;
-
-			foreach (UnitType type in col)
-			{
-				toRet[i++] = type;
-			}
-
-			return toRet;
 		}
 
 		public UnitType GetUnitType(string id)
@@ -255,50 +235,6 @@
 			abilities.TryGetValue(id, out ability);
 			return ability;
 		}
-		
-		protected Category[] RaceCategories
-		{
-			get
-			{
-				Category[] cats = RaceOverrideCategories;
-				
-				if (cats == null)
-				{
-					//No overrides, so load system categories
-					cats = GameSystem.Categories;
-				}
-				
-				return cats;
-			}
-		}
-		
-		protected virtual Category[] RaceOverrideCategories
-		{
-			get
-			{
-				return RaceRawOverrideCategories;
-			}
-			set
-			{
-				RaceRawOverrideCategories = value;
-			}
-		}
-		
-		protected Category[] RaceRawOverrideCategories
-		{
-			get { return cats; }
-			set
-			{
-				if (value!=null && value.Length>0)
-				{
-					cats = value;
-				}
-				else
-				{
-					cats = null;
-				}
-			}
-		}
 		
 		protected virtual Dictionary<string, UnitType> RaceUnitTypes
 		{
--- a/api/Objects/StagedLoadingGameSystem.cs	Sun Jan 04 13:12:55 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-// StagedLoadingGameSystem.cs
-//
-//  Copyright (C) 2008 IBBoard
-//
-// 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 
-//
-//
-
-using System;
-using IBBoard.WarFoundry.API.Factories;
-
-namespace IBBoard.WarFoundry.API.Objects
-{	
-	public class StagedLoadingGameSystem : GameSystem, IWarFoundryStagedLoadObject
-	{		
-		private AbstractNativeWarFoundryFactory creatingFactory;
-		
-		public StagedLoadingGameSystem(string systemID, string systemName, AbstractNativeWarFoundryFactory factory) : base(systemID, systemName)
-		{
-			creatingFactory = factory;
-		}
-		
-		public AbstractNativeWarFoundryFactory Factory
-		{
-			get { return creatingFactory; }
-		}
-		
-		public void EnsureFullyLoaded ()
-		{
-			if (!IsFullyLoaded)
-			{
-				Factory.CompleteLoading(this);
-			}
-		}
-		
-		public bool IsFullyLoaded
-		{
-			get { return RawSystemCategories != null && base.StandardSystemStatsID != null && base.SystemStats != null; }
-		}
-		
-		protected override Category[] SystemCategories
-		{
-			get
-			{
-				EnsureFullyLoaded();
-				return base.SystemCategories;
-			}
-			set { base.SystemCategories = value; }
-		}
-
-	}
-}
--- a/api/Objects/StagedLoadingRace.cs	Sun Jan 04 13:12:55 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,150 +0,0 @@
-// StagedLoadingRace.cs
-//
-//  Copyright (C) 2008 IBBoard
-//
-// 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 
-//
-//
-
-using System;
-using System.Collections.Generic;
-using IBBoard.WarFoundry.API.Factories;
-
-namespace IBBoard.WarFoundry.API.Objects
-{
-	/// <summary>
-	/// Summary description for StagedLoadingRace.
-	/// </summary>
-	public class StagedLoadingRace : Race, IWarFoundryStagedLoadObject
-	{	
-		private AbstractNativeWarFoundryFactory creatingFactory;
-		
-		public StagedLoadingRace(string raceID, string raceName, string gameSystemID, AbstractNativeWarFoundryFactory factory) : this(raceID, "", raceName, gameSystemID, factory)
-		{
-		}
-		
-		public StagedLoadingRace(string raceID, string raceSubID, string raceName, string gameSystemID, AbstractNativeWarFoundryFactory factory) : base(raceID, raceName, gameSystemID)
-		{
-			creatingFactory = factory;
-		}
-		
-		public AbstractNativeWarFoundryFactory Factory
-		{
-			get { return creatingFactory; }
-		}
-		
-		public void EnsureFullyLoaded ()
-		{
-			if (!IsFullyLoaded)
-			{
-				Factory.CompleteLoading(this);
-			}
-		}
-		
-		public bool IsFullyLoaded
-		{
-			get { return RaceRawOverrideCategories != null && RaceRawUnitTypes != null && RaceRawEquipment != null && RaceRawAbilities != null; }
-		}
-		
-		protected override Category[] RaceOverrideCategories
-		{
-			get
-			{
-				EnsureFullyLoaded();
-				return base.RaceOverrideCategories;
-			}
-		}
-		
-		protected override Dictionary<string, Ability> RaceAbilities
-		{
-			get
-			{
-				EnsureFullyLoaded();
-				return base.RaceAbilities;
-			}
-			set
-			{
-				base.RaceAbilities = value;
-			}
-		}
-		
-		protected override Dictionary<string, EquipmentItem> RaceEquipment 
-		{
-			get
-			{
-				EnsureFullyLoaded();
-				return base.RaceEquipment;
-			}
-			set
-			{
-				base.RaceEquipment = value;
-			}
-		}
-
-		protected override Dictionary<string, UnitType> RaceUnitTypes
-		{
-			get
-			{
-				EnsureFullyLoaded();
-				return base.RaceUnitTypes;
-			}
-			set
-			{
-				base.RaceUnitTypes = value;
-			}
-		}
-
-
-
-		
- 
-		
-		/*public void CompleteLoading(List<Category> categoriesList, Dictionary<string,UnitType> unitTypesList, Dictionary<string,EquipmentItem> equipmentList, Dictionary<string, Ability> abilityList)
-		{
-			logger.Debug("Preparing dictionaries");
-			
-			if (categoriesList != null && categoriesList.Count > 0)
-			{
-				cats = categoriesList.ToArray();
-			}
-			
-			logger.Debug("Loading equipment");
-			equipment = new Dictionary<string,EquipmentItem>();
-
-			foreach (string equipID in equipmentList.Keys)
-			{
-				equipment.Add(equipID, equipmentList[equipID]);
-			}
-			
-			logger.Debug("Loading abilities");
-			abilities = new Dictionary<string,Ability>();
-			
-			foreach (string abilityID in abilityList.Keys)
-			{
-				abilities.Add(abilityID, abilityList[abilityID]);
-			}
-			
-			logger.Debug("Loading units");
-			unitTypes = new Dictionary<string,UnitType>();
-			
-			foreach (string unitID in unitTypesList.Keys)
-			{
-				unitTypes.Add(unitID, unitTypesList[unitID]);
-			}
-			
-			base.CompleteLoading();
-		}*/
-	}
-}
\ No newline at end of file
--- a/api/Objects/UnitType.cs	Sun Jan 04 13:12:55 2009 +0000
+++ b/api/Objects/UnitType.cs	Sun Jan 04 19:24:13 2009 +0000
@@ -73,7 +73,6 @@
 			{
 				if (mainCat == null)
 				{
-					LogNotifier.Debug(GetType(), Name+" main category: "+mainCatID);
 					mainCat = Race.GetCategory(mainCatID);
 				}
 					
--- a/api/Objects/WarFoundryObject.cs	Sun Jan 04 13:12:55 2009 +0000
+++ b/api/Objects/WarFoundryObject.cs	Sun Jan 04 19:24:13 2009 +0000
@@ -1,4 +1,5 @@
-using System;
+using System;
+using IBBoard.WarFoundry.API.Factories;
 
 namespace IBBoard.WarFoundry.API.Objects
 {
@@ -6,11 +7,14 @@
 	/// Summary description for WarFoundryObject.
 	/// </summary>
 	public abstract class WarFoundryObject : IWarFoundryObject
-	{
-		protected string name, id;
+	{
+		protected string id;
+		protected string name;
 		public event StringValChangedDelegate NameChanged;
 		
-		protected WarFoundryObject(){}
+		protected WarFoundryObject()
+		{
+		}
 				
 		protected WarFoundryObject(string objName) : this()
 		{
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/api/Objects/WarFoundryStagedLoadingObject.cs	Sun Jan 04 19:24:13 2009 +0000
@@ -0,0 +1,79 @@
+// WarFoundryStagedLoadingObject.cs is a part of the IBBoard.WarFoundry.API library (referred to from here as "this program")
+// 
+// Copyright (C) 2009 IBBoard
+//
+// This program 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 3 of the License, or
+// (at your option) any later version.
+//
+// This program 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 program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+using System;
+using System.IO;
+using IBBoard.WarFoundry.API.Factories;
+
+namespace IBBoard.WarFoundry.API.Objects
+{	
+	public class WarFoundryStagedLoadingObject : WarFoundryObject, IWarFoundryStagedLoadObject
+	{
+		private bool isFullyLoaded;
+		private IWarFoundryFactory creatingFactory;
+		private FileInfo sourceFile;
+		
+		protected WarFoundryStagedLoadingObject(IWarFoundryFactory creatingFactory) : this (null, creatingFactory)
+		{
+		}
+				
+		protected WarFoundryStagedLoadingObject(string objName, IWarFoundryFactory creatingFactory) : this(null, objName, creatingFactory)
+		{
+		}
+		
+		protected WarFoundryStagedLoadingObject(string objId, string objName, IWarFoundryFactory creatingFactory) : base(objId, objName)
+		{
+			this.creatingFactory = creatingFactory;
+			isFullyLoaded = false;
+		}	
+		
+		public FileInfo SourceFile
+		{
+			get { return sourceFile; }
+			set { sourceFile = value; }
+		}
+		
+		public void EnsureFullyLoaded ()
+		{
+			if (!IsFullyLoaded)
+			{
+				if (Factory == null)
+				{
+					throw new InvalidOperationException("No factory set for partially loaded object with ID "+ID);
+				}
+				
+				Factory.CompleteLoading(this);
+			}
+		}
+		
+		public IWarFoundryFactory Factory
+		{
+			get { return creatingFactory; }
+		}
+		
+		public bool IsFullyLoaded
+		{
+			get { return isFullyLoaded; }
+		}
+		
+		public void SetAsFullyLoaded()
+		{
+			isFullyLoaded = true;
+		}
+	}
+}
--- a/api/WarFoundryLoader.cs	Sun Jan 04 13:12:55 2009 +0000
+++ b/api/WarFoundryLoader.cs	Sun Jan 04 19:24:13 2009 +0000
@@ -461,13 +461,7 @@
 				LoadFiles();
 			}
 			
-			systemID = systemID.ToLower();
-			
-			foreach (string key in racesTable.Keys)
-			{
-				Console.WriteLine(key);
-			}
-
+			systemID = systemID.ToLower();
 			Dictionary<string, Dictionary<string, Race>> system;
 			racesTable.TryGetValue(systemID, out system);