changeset 481:81f32062c9fa

Re #419: Remove assumptions of a file-based install * Convert schemas to resources and load them from there * Remove use of "data" folder relative to exe location from default "Hacks" (GTK/WinForms GUIs can re-add it locally)
author IBBoard <dev@ibboard.co.uk>
date Mon, 25 Jun 2012 21:04:02 +0100
parents e0641e0eb86c
children 1ed2f3ab5e35
files API/Exporters/WarFoundryHtmlExporter.cs API/Factories/Xml/WarFoundryXmlFactoryUtils.cs API/WarFoundryHacks.cs IBBoard.WarFoundry.API.csproj
diffstat 4 files changed, 467 insertions(+), 491 deletions(-) [+]
line wrap: on
line diff
--- a/API/Exporters/WarFoundryHtmlExporter.cs	Wed May 23 21:00:33 2012 +0100
+++ b/API/Exporters/WarFoundryHtmlExporter.cs	Mon Jun 25 21:04:02 2012 +0100
@@ -12,378 +12,380 @@
 using IBBoard.Xml;
 using IBBoard.WarFoundry.API.Objects;
 using IBBoard.WarFoundry.API.Util;
+using System.Reflection;
 
 namespace IBBoard.WarFoundry.API.Exporters
 {
-	/// <summary>
-	/// Custom exporter that exports an army as a basic HTML file
-	/// </summary>
-	public class WarFoundryHtmlExporter : IWarFoundryExporter
-	{
-		private static WarFoundryHtmlExporter exporter;
-		private delegate string GetStatCellTextDelegate(Stat stat);
-		
-		public static WarFoundryHtmlExporter GetDefault()
-		{
-			if (exporter == null)
-			{
-				exporter = new WarFoundryHtmlExporter();
-			}
-			
-			return exporter;
-		}
-		
-		private WarFoundryHtmlExporter()
-		{
-			//Hide constructor
-		}
-		
-		public void ExportArmy(Army army, string path)
-		{
-			XmlDocument doc = new XmlDocument();
-			CustomXmlResolver resolver = new CustomXmlResolver();
-			Uri localUri = new Uri("file://" + IBBoard.Constants.ExecutablePath + "/schemas/xhtml1-strict.dtd");
-			resolver.AddMapping("-//W3C//DTD XHTML 1.0 Strict//EN", localUri);
-			resolver.AddMapping("http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd", localUri);
-			doc.XmlResolver = resolver;
-			doc.AppendChild(doc.CreateDocumentType("html", "-//W3C//DTD XHTML 1.0 Strict//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd", null));
-			XmlElement html = doc.CreateElement("html");
-			doc.AppendChild(html);
-			XmlElement head = doc.CreateElement("head");
-			html.AppendChild(head);
-			XmlElement title = doc.CreateElement("title");
-			title.InnerXml = army.Name;
-			head.AppendChild(title);
-			XmlElement metaCharset = doc.CreateElement("meta");
-			metaCharset.SetAttribute("http-equiv", "Content-Type");
-			metaCharset.SetAttribute("content", "text/html;charset=UTF-8");
-			head.AppendChild(metaCharset);
-			XmlElement style = doc.CreateElement("style");
-			style.InnerText = "table, th, td { border: 1px solid #000; border-spacing: 0; border-collapse: collapse; margin: 0 }\n"
-				+"table table { width: 100%; border-width: 0; margin: -2px }\n"
-				+"table table td { border-width:0 1px }";
-			head.AppendChild(style);
-			XmlElement body = doc.CreateElement("body");
-			html.AppendChild(body);
-			XmlElement header = doc.CreateElement("h1");
-			header.InnerText = Translation.GetTranslation("armyHtmlOutputBodyHeader", "{0} - {1}pts", army.Name, army.Points);
-			body.AppendChild(header);
-			
-			foreach (XmlElement table in CreateTables(army, doc))
-			{
-				if (!IsTableOnlyHeader(table))
-				{
-					body.AppendChild(table);
-				}
-			}
-
-			File.WriteAllText(path, doc.OuterXml);
-		}
-
-		private bool IsTableOnlyHeader(XmlElement table)
-		{
-			return table.ChildNodes.Count == 1;
-		}
-		
-		private XmlElement[] CreateTables(Army army, XmlDocument doc)
-		{
-			Dictionary<string, XmlElement> tables = new Dictionary<string, XmlElement>();
-			
-			foreach (SystemStats statSets in army.GameSystem.SystemStats)
-			{
-				tables[statSets.ID] = CreateTable(statSets, doc);
-			}
-			
-			foreach (Unit unit in army.GetUnits())
-			{
-				CreateUnitRow(unit, tables[GetFirstStatType(unit)]);
-			}
-			
-			return DictionaryUtils.ToArray(tables);
-		}
-
-		private static string GetFirstStatType(Unit unit)
+		/// <summary>
+		/// Custom exporter that exports an army as a basic HTML file
+		/// </summary>
+		public class WarFoundryHtmlExporter : IWarFoundryExporter
 		{
-			string[] unitStatIDs = unit.UnitStatsArrayIDs;
-			return GetFirstStatType(unitStatIDs);
-		}
-		
-		public static string GetFirstStatType(string[] unitStatIDs)
-		{
-			return unitStatIDs[0];
-		}
+				private static WarFoundryHtmlExporter exporter;
+				private delegate string GetStatCellTextDelegate(Stat stat);
 		
-		private XmlElement CreateTable(SystemStats stats, XmlDocument doc)
-		{
-			XmlElement table = doc.CreateElement("table");
-			XmlElement headerRow = doc.CreateElement("tr");
-			table.AppendChild(headerRow);
-			XmlElement name = doc.CreateElement("th");
-			name.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitName", "name");
-			headerRow.AppendChild(name);
+				public static WarFoundryHtmlExporter GetDefault()
+				{
+						if (exporter == null)
+						{
+								exporter = new WarFoundryHtmlExporter();
+						}
 			
-			XmlElement unitTypeName = doc.CreateElement("th");
-			unitTypeName.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitTypeName", "type name");
-			headerRow.AppendChild(unitTypeName);
-			
-			foreach (StatSlot stat in stats.StatSlots)
-			{
-				XmlElement statHeader = doc.CreateElement("th");
-				statHeader.InnerText = stat.Name;
-				headerRow.AppendChild(statHeader);
-			}
+						return exporter;
+				}
+		
+				private WarFoundryHtmlExporter()
+				{
+						//Hide constructor
+				}
+		
+				public void ExportArmy(Army army, string path)
+				{
+						XmlDocument doc = new XmlDocument();
+						XmlResourceResolver resolver = new XmlResourceResolver(Assembly.GetExecutingAssembly());
+						resolver.AddMapping("-//W3C//ENTITIES Latin 1 for XHTML//EN", "IBBoard.API.schemas.xhtml1-lat1.ent");
+						resolver.AddMapping("http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent", "IBBoard.API.schemas.xhtml1-lat1.ent");
+						resolver.AddMapping("-//W3C//ENTITIES Symbols for XHTML//EN", "IBBoard.API.schemas.xhtml1-symbol.ent");
+						resolver.AddMapping("http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent", "IBBoard.API.schemas.xhtml1-symbol.ent");
+						resolver.AddMapping("-//W3C//ENTITIES Special for XHTML//EN", "IBBoard.API.schemas.xhtml1-special.ent");
+						resolver.AddMapping("http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent", "IBBoard.API.schemas.xhtml1-special.ent");
+						resolver.AddMapping("-//W3C//DTD XHTML 1.0 Strict//EN", "IBBoard.WarFoundry.schemas.xhtml1-strict.dtd");
+						resolver.AddMapping("http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd", "IBBoard.WarFoundry.schemas.xhtml1-strict.dtd");
+						doc.XmlResolver = resolver;
+						doc.AppendChild(doc.CreateDocumentType("html", "-//W3C//DTD XHTML 1.0 Strict//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd", null));
+						XmlElement html = doc.CreateElement("html");
+						doc.AppendChild(html);
+						XmlElement head = doc.CreateElement("head");
+						html.AppendChild(head);
+						XmlElement title = doc.CreateElement("title");
+						title.InnerXml = army.Name;
+						head.AppendChild(title);
+						XmlElement metaCharset = doc.CreateElement("meta");
+						metaCharset.SetAttribute("http-equiv", "Content-Type");
+						metaCharset.SetAttribute("content", "text/html;charset=UTF-8");
+						head.AppendChild(metaCharset);
+						XmlElement style = doc.CreateElement("style");
+						style.InnerText = "table, th, td { border: 1px solid #000; border-spacing: 0; border-collapse: collapse; margin: 0 }\n"
+								+ "table table { width: 100%; border-width: 0; margin: -2px }\n"
+								+ "table table td { border-width:0 1px }";
+						head.AppendChild(style);
+						XmlElement body = doc.CreateElement("body");
+						html.AppendChild(body);
+						XmlElement header = doc.CreateElement("h1");
+						header.InnerText = Translation.GetTranslation("armyHtmlOutputBodyHeader", "{0} - {1}pts", army.Name, army.Points);
+						body.AppendChild(header);
 			
-			XmlElement notes = doc.CreateElement("th");
-			notes.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitNotes", "name");;
-			headerRow.AppendChild(notes);
-			
-			XmlElement points = doc.CreateElement("th");
-			points.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitPoints", "name");;
-			headerRow.AppendChild(points);
-			
-			return table;
-		}
+						foreach (XmlElement table in CreateTables(army, doc))
+						{
+								if (!IsTableOnlyHeader(table))
+								{
+										body.AppendChild(table);
+								}
+						}
+
+						File.WriteAllText(path, doc.OuterXml);
+				}
+
+				private bool IsTableOnlyHeader(XmlElement table)
+				{
+						return table.ChildNodes.Count == 1;
+				}
 		
-		private XmlElement CreateUnitRow(Unit unit, XmlElement tableElem)
-		{
-			XmlDocument doc = tableElem.OwnerDocument;
-			XmlElement row = doc.CreateElement("tr");
-			tableElem.AppendChild(row);
-			Stat[][] memberStats = unit.UnitStatsArraysWithName;
-			string[] statTypeIDs = unit.UnitStatsArrayIDs;
-			string defaultStatType = GetFirstStatType(statTypeIDs);
-			int statRowCount = 0;
-			bool hasOther = false;
+				private XmlElement[] CreateTables(Army army, XmlDocument doc)
+				{
+						Dictionary<string, XmlElement> tables = new Dictionary<string, XmlElement>();
+			
+						foreach (SystemStats statSets in army.GameSystem.SystemStats)
+						{
+								tables[statSets.ID] = CreateTable(statSets, doc);
+						}
+			
+						foreach (Unit unit in army.GetUnits())
+						{
+								CreateUnitRow(unit, tables[GetFirstStatType(unit)]);
+						}
 			
-			foreach (string statTypeID in statTypeIDs)
-			{
-				if (statTypeID.Equals(defaultStatType))
+						return DictionaryUtils.ToArray(tables);
+				}
+
+				private static string GetFirstStatType(Unit unit)
 				{
-					statRowCount++;
+						string[] unitStatIDs = unit.UnitStatsArrayIDs;
+						return GetFirstStatType(unitStatIDs);
+				}
+		
+				public static string GetFirstStatType(string[] unitStatIDs)
+				{
+						return unitStatIDs[0];
 				}
-				else if (!hasOther)
+		
+				private XmlElement CreateTable(SystemStats stats, XmlDocument doc)
 				{
-					statRowCount++;
-					hasOther = true;
-				}
-			}
+						XmlElement table = doc.CreateElement("table");
+						XmlElement headerRow = doc.CreateElement("tr");
+						table.AppendChild(headerRow);
+						XmlElement name = doc.CreateElement("th");
+						name.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitName", "name");
+						headerRow.AppendChild(name);
+			
+						XmlElement unitTypeName = doc.CreateElement("th");
+						unitTypeName.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitTypeName", "type name");
+						headerRow.AppendChild(unitTypeName);
 			
-			XmlElement name = doc.CreateElement("td");
-			name.InnerText = unit.Name;
-			SetRowSpan(name, statRowCount);
-			row.AppendChild(name);
-			CreateStatsBlock(row, memberStats, statTypeIDs);
-
-			StringBuilder sb = new StringBuilder();
-			UnitEquipmentItem[] unitEquipment = unit.GetEquipment();
+						foreach (StatSlot stat in stats.StatSlots)
+						{
+								XmlElement statHeader = doc.CreateElement("th");
+								statHeader.InnerText = stat.Name;
+								headerRow.AppendChild(statHeader);
+						}
 			
-			if (unitEquipment.Length > 0)
-			{
-				bool addSeparator = false;
-				
-				foreach (UnitEquipmentItem equip in unitEquipment)
+						XmlElement notes = doc.CreateElement("th");
+						notes.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitNotes", "name");
+						;
+						headerRow.AppendChild(notes);
+			
+						XmlElement points = doc.CreateElement("th");
+						points.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitPoints", "name");
+						;
+						headerRow.AppendChild(points);
+			
+						return table;
+				}
+		
+				private XmlElement CreateUnitRow(Unit unit, XmlElement tableElem)
 				{
-					if (!addSeparator)
-					{
-						addSeparator = true;
-					}
-					else
-					{
-						sb.Append(", ");
-					}
+						XmlDocument doc = tableElem.OwnerDocument;
+						XmlElement row = doc.CreateElement("tr");
+						tableElem.AppendChild(row);
+						Stat[][] memberStats = unit.UnitStatsArraysWithName;
+						string[] statTypeIDs = unit.UnitStatsArrayIDs;
+						string defaultStatType = GetFirstStatType(statTypeIDs);
+						int statRowCount = 0;
+						bool hasOther = false;
+			
+						foreach (string statTypeID in statTypeIDs)
+						{
+								if (statTypeID.Equals(defaultStatType))
+								{
+										statRowCount++;
+								} else if (!hasOther)
+								{
+										statRowCount++;
+										hasOther = true;
+								}
+						}
+			
+						XmlElement name = doc.CreateElement("td");
+						name.InnerText = unit.Name;
+						SetRowSpan(name, statRowCount);
+						row.AppendChild(name);
+						CreateStatsBlock(row, memberStats, statTypeIDs);
 
-					string amountString;
-					double amount = UnitEquipmentUtil.GetEquipmentAmount(unit, equip);
-
-					if (UnitEquipmentUtil.GetEquipmentAmountIsRatio(unit, equip))
-					{
-
-						if (amount == 100)
+						StringBuilder sb = new StringBuilder();
+						UnitEquipmentItem[] unitEquipment = unit.GetEquipment();
+			
+						if (unitEquipment.Length > 0)
 						{
-							amountString = GetEquipmentAmountAllTranslation(unit);
-						}
-						else
-						{
-							int number = UnitEquipmentUtil.GetEquipmentAmountTaken(unit, equip);
-							amountString = GetEquipmentAmountRatioTranslation(amount, number);
-						}
-					}
-					else
-					{
-						if (amount == -1)
-						{
-							amountString = GetEquipmentAmountAllTranslation(unit);
-						}
-						else
-						{
-							amountString = GetEquipmentAmountNumberTranslation((int)amount);
-						}
-					}
-
-					sb.Append(Translation.GetTranslation("armyHtmlExportEquipAmountRatio", "{0} for {1}", equip.Name, amountString));
-				}
+								bool addSeparator = false;
 				
-				sb.Append(". ");
-			}
-			
-			ICollection<Ability> abilities = unit.Abilities;
-			
-			if (abilities.Count > 0)
-			{
-				bool addSeparator = false;
-				
-				foreach (Ability ability in abilities)
-				{
-					if (!addSeparator)
-					{
-						addSeparator = true;
-					}
-					else
-					{
-						sb.Append(", ");
-					}
-					
-					sb.Append(ability.Name);
-				}
+								foreach (UnitEquipmentItem equip in unitEquipment)
+								{
+										if (!addSeparator)
+										{
+												addSeparator = true;
+										} else
+										{
+												sb.Append(", ");
+										}
+
+										string amountString;
+										double amount = UnitEquipmentUtil.GetEquipmentAmount(unit, equip);
+
+										if (UnitEquipmentUtil.GetEquipmentAmountIsRatio(unit, equip))
+										{
+
+												if (amount == 100)
+												{
+														amountString = GetEquipmentAmountAllTranslation(unit);
+												} else
+												{
+														int number = UnitEquipmentUtil.GetEquipmentAmountTaken(unit, equip);
+														amountString = GetEquipmentAmountRatioTranslation(amount, number);
+												}
+										} else
+										{
+												if (amount == -1)
+												{
+														amountString = GetEquipmentAmountAllTranslation(unit);
+												} else
+												{
+														amountString = GetEquipmentAmountNumberTranslation((int)amount);
+												}
+										}
+
+										sb.Append(Translation.GetTranslation("armyHtmlExportEquipAmountRatio", "{0} for {1}", equip.Name, amountString));
+								}
 				
-				sb.Append(". ");
-			}
+								sb.Append(". ");
+						}
 			
-			XmlElement notes = doc.CreateElement("td");
-			notes.InnerText = sb.ToString();
-			SetRowSpan(notes, statRowCount);
-			row.AppendChild(notes);
+						ICollection<Ability> abilities = unit.Abilities;
 			
-			XmlElement points = doc.CreateElement("td");
-			points.InnerText = unit.Points.ToString();
-			SetRowSpan(points, statRowCount);
-			row.AppendChild(points);
+						if (abilities.Count > 0)
+						{
+								bool addSeparator = false;
+				
+								foreach (Ability ability in abilities)
+								{
+										if (!addSeparator)
+										{
+												addSeparator = true;
+										} else
+										{
+												sb.Append(", ");
+										}
+					
+										sb.Append(ability.Name);
+								}
+				
+								sb.Append(". ");
+						}
 			
-			return row;
-		}
-		
-		private static void SetRowSpan(XmlElement xmlElement, int statRowCount)
-		{
-			if (statRowCount > 1)
-			{
-				xmlElement.SetAttribute("rowspan", statRowCount.ToString());
-			}
-		}
+						XmlElement notes = doc.CreateElement("td");
+						notes.InnerText = sb.ToString();
+						SetRowSpan(notes, statRowCount);
+						row.AppendChild(notes);
+			
+						XmlElement points = doc.CreateElement("td");
+						points.InnerText = unit.Points.ToString();
+						SetRowSpan(points, statRowCount);
+						row.AppendChild(points);
+			
+						return row;
+				}
 		
-		private void CreateStatsBlock(XmlElement unitRow, Stat[][] memberStats, string[] statTypeIDs)
-		{
-			XmlDocument doc = unitRow.OwnerDocument;
-			string defaultStatType = GetFirstStatType(statTypeIDs);
-			
-			Stat[] defaultStatLine = memberStats[0];
-			int defaultStatLineCount = defaultStatLine.Length;
-			AddStatCell(defaultStatLine[0].SlotValueString, unitRow);
-			
-			for (int i = 1; i < defaultStatLineCount; i++)
-			{
-				string statText = GetDefaultStatCellText(defaultStatLine[i]);
-				AddStatCell(statText, unitRow);
-			}
-			
-			int statCount = statTypeIDs.Length;
-			
-			if (statCount > 1)
-			{
-				XmlElement unitTable = (XmlElement)unitRow.ParentNode;
-				Dictionary<string, XmlElement> statParents = CreateStatsParentElements(statTypeIDs, unitTable);
-				
-				for (int i = 1; i < statCount; i++)
+				private static void SetRowSpan(XmlElement xmlElement, int statRowCount)
+				{
+						if (statRowCount > 1)
+						{
+								xmlElement.SetAttribute("rowspan", statRowCount.ToString());
+						}
+				}
+		
+				private void CreateStatsBlock(XmlElement unitRow, Stat[][] memberStats, string[] statTypeIDs)
 				{
-					Stat[] statLine = memberStats[i];
-					string statTypeID = statTypeIDs[i];
-					XmlElement tableElement = DictionaryUtils.GetValue(statParents, statTypeID);
-					int statLineCount = statLine.Length;
-					XmlElement statRow = doc.CreateElement("tr");
-					tableElement.AppendChild(statRow);
-					GetStatCellTextDelegate statCellTextDelegate = (statTypeID.Equals(defaultStatType) ? new GetStatCellTextDelegate(GetDefaultStatCellText) : new GetStatCellTextDelegate(GetOtherStatCellText));
-					AddStatCell(statLine[0].SlotValueString, statRow);
+						XmlDocument doc = unitRow.OwnerDocument;
+						string defaultStatType = GetFirstStatType(statTypeIDs);
+			
+						Stat[] defaultStatLine = memberStats[0];
+						int defaultStatLineCount = defaultStatLine.Length;
+						AddStatCell(defaultStatLine[0].SlotValueString, unitRow);
+			
+						for (int i = 1; i < defaultStatLineCount; i++)
+						{
+								string statText = GetDefaultStatCellText(defaultStatLine[i]);
+								AddStatCell(statText, unitRow);
+						}
+			
+						int statCount = statTypeIDs.Length;
+			
+						if (statCount > 1)
+						{
+								XmlElement unitTable = (XmlElement)unitRow.ParentNode;
+								Dictionary<string, XmlElement> statParents = CreateStatsParentElements(statTypeIDs, unitTable);
 				
-					for (int j = 1; j < statLineCount; j++)
-					{
-						string statText = statCellTextDelegate(statLine[j]);
-						AddStatCell(statText, statRow);
-					}
-				}
+								for (int i = 1; i < statCount; i++)
+								{
+										Stat[] statLine = memberStats[i];
+										string statTypeID = statTypeIDs[i];
+										XmlElement tableElement = DictionaryUtils.GetValue(statParents, statTypeID);
+										int statLineCount = statLine.Length;
+										XmlElement statRow = doc.CreateElement("tr");
+										tableElement.AppendChild(statRow);
+										GetStatCellTextDelegate statCellTextDelegate = (statTypeID.Equals(defaultStatType) ? new GetStatCellTextDelegate(GetDefaultStatCellText) : new GetStatCellTextDelegate(GetOtherStatCellText));
+										AddStatCell(statLine[0].SlotValueString, statRow);
 				
-				if (statParents.Count > 1)
-				{
-					AddOtherUnitStatTables(statParents, unitTable, defaultStatLineCount);
+										for (int j = 1; j < statLineCount; j++)
+										{
+												string statText = statCellTextDelegate(statLine[j]);
+												AddStatCell(statText, statRow);
+										}
+								}
+				
+								if (statParents.Count > 1)
+								{
+										AddOtherUnitStatTables(statParents, unitTable, defaultStatLineCount);
+								}
+						}
 				}
-			}
-		}
 		
-		private static void AddOtherUnitStatTables(Dictionary<string, XmlElement> statParents, XmlElement unitTable, int defaultStatLineCount)
-		{
-			XmlDocument doc = unitTable.OwnerDocument;
-			XmlElement otherStatsRow = doc.CreateElement("tr");
-			unitTable.AppendChild(otherStatsRow);
-			XmlElement otherStatsCell = doc.CreateElement("td");
-			otherStatsCell.SetAttribute("colspan", defaultStatLineCount.ToString());
-			otherStatsRow.AppendChild(otherStatsCell);
+				private static void AddOtherUnitStatTables(Dictionary<string, XmlElement> statParents, XmlElement unitTable, int defaultStatLineCount)
+				{
+						XmlDocument doc = unitTable.OwnerDocument;
+						XmlElement otherStatsRow = doc.CreateElement("tr");
+						unitTable.AppendChild(otherStatsRow);
+						XmlElement otherStatsCell = doc.CreateElement("td");
+						otherStatsCell.SetAttribute("colspan", defaultStatLineCount.ToString());
+						otherStatsRow.AppendChild(otherStatsCell);
 			
-			foreach (XmlElement tableElem in statParents.Values)
-			{
-				if (tableElem != unitTable)
-				{
-					otherStatsCell.AppendChild(tableElem);
+						foreach (XmlElement tableElem in statParents.Values)
+						{
+								if (tableElem != unitTable)
+								{
+										otherStatsCell.AppendChild(tableElem);
+								}
+						}
 				}
-			}
-		}
 
-		private Dictionary<string, XmlElement> CreateStatsParentElements(string[] statTypeIDs, XmlElement parentTable)
-		{
-			Dictionary<string, XmlElement> statParents = new Dictionary<string, XmlElement>();
-			XmlDocument doc = parentTable.OwnerDocument;
-			string defaultStatTypeID = GetFirstStatType(statTypeIDs);
-			statParents[defaultStatTypeID] = parentTable;
-			
-			foreach (string statTypeID in statTypeIDs)
-			{
-				if (!statParents.ContainsKey(statTypeID))
+				private Dictionary<string, XmlElement> CreateStatsParentElements(string[] statTypeIDs, XmlElement parentTable)
 				{
-					XmlElement tableElement = doc.CreateElement("table");
-					statParents[statTypeID] = tableElement;
-				}
-			}
+						Dictionary<string, XmlElement> statParents = new Dictionary<string, XmlElement>();
+						XmlDocument doc = parentTable.OwnerDocument;
+						string defaultStatTypeID = GetFirstStatType(statTypeIDs);
+						statParents[defaultStatTypeID] = parentTable;
 			
-			return statParents;
-		}
+						foreach (string statTypeID in statTypeIDs)
+						{
+								if (!statParents.ContainsKey(statTypeID))
+								{
+										XmlElement tableElement = doc.CreateElement("table");
+										statParents[statTypeID] = tableElement;
+								}
+						}
+			
+						return statParents;
+				}
 
-		private string GetDefaultStatCellText(Stat stat)
-		{
-			return Translation.GetTranslation("armyHtmlExportDefaultStatCellText", "{0}", stat.SlotValueString, stat.ParentSlotName);
-		}
+				private string GetDefaultStatCellText(Stat stat)
+				{
+						return Translation.GetTranslation("armyHtmlExportDefaultStatCellText", "{0}", stat.SlotValueString, stat.ParentSlotName);
+				}
 
-		private string GetOtherStatCellText(Stat stat)
-		{
-			return Translation.GetTranslation("armyHtmlExportOtherStatCellText", "{1}: {0}", stat.SlotValueString, stat.ParentSlotName);
-		}
+				private string GetOtherStatCellText(Stat stat)
+				{
+						return Translation.GetTranslation("armyHtmlExportOtherStatCellText", "{1}: {0}", stat.SlotValueString, stat.ParentSlotName);
+				}
 		
-		private static void AddStatCell(string statValue, XmlElement row)
-		{
-			XmlElement statCell = row.OwnerDocument.CreateElement("td");
-			statCell.InnerText = statValue;
-			row.AppendChild(statCell);
-		}
+				private static void AddStatCell(string statValue, XmlElement row)
+				{
+						XmlElement statCell = row.OwnerDocument.CreateElement("td");
+						statCell.InnerText = statValue;
+						row.AppendChild(statCell);
+				}
 		
-		private string GetEquipmentAmountRatioTranslation (double amount, int number)
-		{
-			return Translation.GetTranslation ("armyHtmlExportEquipAmountPercentage", "{0}% ({1})", amount, number);
-		}
+				private string GetEquipmentAmountRatioTranslation(double amount, int number)
+				{
+						return Translation.GetTranslation("armyHtmlExportEquipAmountPercentage", "{0}% ({1})", amount, number);
+				}
 		
-		private string GetEquipmentAmountNumberTranslation(int amount)
-		{
-			return Translation.GetTranslation("armyHtmlExportEquipAmountNumber", "{0}", amount);
-		}
+				private string GetEquipmentAmountNumberTranslation(int amount)
+				{
+						return Translation.GetTranslation("armyHtmlExportEquipAmountNumber", "{0}", amount);
+				}
 		
-		private string GetEquipmentAmountAllTranslation(Unit unit)
-		{
-			return Translation.GetTranslation("armyHtmlExportEquipAmountAll", "all ({1})", 100, unit.Size);
+				private string GetEquipmentAmountAllTranslation(Unit unit)
+				{
+						return Translation.GetTranslation("armyHtmlExportEquipAmountAll", "all ({1})", 100, unit.Size);
+				}
 		}
-	}
 }
--- a/API/Factories/Xml/WarFoundryXmlFactoryUtils.cs	Wed May 23 21:00:33 2012 +0100
+++ b/API/Factories/Xml/WarFoundryXmlFactoryUtils.cs	Mon Jun 25 21:04:02 2012 +0100
@@ -8,142 +8,123 @@
 using System.Xml.Schema;
 using IBBoard.WarFoundry.API.Objects;
 using IBBoard.IO;
+using IBBoard.Xml;
+using System.Reflection;
 
 namespace IBBoard.WarFoundry.API.Factories.Xml
 {
-	/// <summary>
-	/// A collection of useful utility methods for loading WarFoundry data from XML files
-	/// </summary>
-	public class WarFoundryXmlFactoryUtils
-	{
-		public static readonly string NS_BASE = "http://ibboard.co.uk/warfoundry/";
-		private static XmlReaderSettings settings;
-		private static XmlNamespaceManager nsManager;
-		
-		public static XmlNodeList SelectNodes(XmlNode element, string xpathQuery)
-		{
-			return element.SelectNodes(xpathQuery, GetNamespaceManager());
-		}
-		
-		public static XmlNode SelectSingleNode(XmlNode element, string xpathQuery)
-		{
-			return element.SelectSingleNode(xpathQuery, GetNamespaceManager());
-		}
-		
-		public static XmlElement SelectSingleElement(XmlNode element, string xpathQuery)
-		{
-			XmlNode node = SelectSingleNode(element, xpathQuery);
-			return (node is XmlElement) ? (XmlElement) node : null;
-		}
-				
-		public static XmlNamespaceManager GetNamespaceManager()
-		{
-			if (nsManager == null)
-			{
-				nsManager = new XmlNamespaceManager(new NameTable());
-				nsManager.AddNamespace("core", NS_BASE + "core");
-				nsManager.AddNamespace("cat", NS_BASE + "cats");
-				nsManager.AddNamespace("race", NS_BASE + "race");
-				nsManager.AddNamespace("system", NS_BASE + "system");
-				nsManager.AddNamespace("army", NS_BASE + "army");
-			}
-			
-			return nsManager;
-		}
-		
 		/// <summary>
-		/// Lazy-getter for XML reader settings. May throw a <see cref="InvalidDataException"/> if there is a problem with the translation schema.
+		/// A collection of useful utility methods for loading WarFoundry data from XML files
 		/// </summary>
-		/// <returns>
-		/// A <see cref="XmlReaderSettings"/> with the default values for validating the translation document against the translation schema
-		/// </returns>
-		public static XmlReaderSettings GetReaderSettings()
+		public class WarFoundryXmlFactoryUtils
 		{
-			if (settings == null)
-			{
-				settings = new XmlReaderSettings();
-				settings.ValidationType = ValidationType.Schema;
-				//settings.ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings;
-				settings.ProhibitDtd = true;
-				settings.ValidationEventHandler+= new ValidationEventHandler(ValidationEventMethod);
-				XmlSchemaSet cache = new XmlSchemaSet();
-				string path =  Path.Combine(IBBoard.Constants.ExecutablePath, "schemas");
-				AddSchemaToCache(cache, NS_BASE + "core", Path.Combine(path, "warfoundry-core.xsd"));
-				AddSchemaToCache(cache, NS_BASE + "cats", Path.Combine(path, "warfoundry-cats.xsd"));
-				AddSchemaToCache(cache, NS_BASE + "race", Path.Combine(path, "race.xsd"));
-				AddSchemaToCache(cache, NS_BASE + "system", Path.Combine(path, "system.xsd"));
-				AddSchemaToCache(cache, NS_BASE + "army", Path.Combine(path, "army.xsd"));
-				settings.Schemas.Add(cache);
-				settings.Schemas.CompilationSettings.EnableUpaCheck = false;
-			}
+				public static readonly string NS_BASE = "http://ibboard.co.uk/warfoundry/";
+				private static XmlReaderSettings settings;
+				private static XmlNamespaceManager nsManager;
+		
+				public static XmlNodeList SelectNodes(XmlNode element, string xpathQuery)
+				{
+						return element.SelectNodes(xpathQuery, GetNamespaceManager());
+				}
+		
+				public static XmlNode SelectSingleNode(XmlNode element, string xpathQuery)
+				{
+						return element.SelectSingleNode(xpathQuery, GetNamespaceManager());
+				}
+		
+				public static XmlElement SelectSingleElement(XmlNode element, string xpathQuery)
+				{
+						XmlNode node = SelectSingleNode(element, xpathQuery);
+						return (node is XmlElement) ? (XmlElement)node : null;
+				}
+				
+				public static XmlNamespaceManager GetNamespaceManager()
+				{
+						if (nsManager == null)
+						{
+								nsManager = new XmlNamespaceManager(new NameTable());
+								nsManager.AddNamespace("core", NS_BASE + "core");
+								nsManager.AddNamespace("cat", NS_BASE + "cats");
+								nsManager.AddNamespace("race", NS_BASE + "race");
+								nsManager.AddNamespace("system", NS_BASE + "system");
+								nsManager.AddNamespace("army", NS_BASE + "army");
+						}
 			
-			return settings;
-		}
+						return nsManager;
+				}
 		
-		private static void ValidationEventMethod(object sender, ValidationEventArgs e)
-		{
-			if (e.Severity == XmlSeverityType.Error)
-			{
-				throw new InvalidFileException("Problem validating against schema for WarFoundry data: " + e.Message, e.Exception);
-			}
-			else
-			{
-				//TODO: Fire some kind of warning event
-			}
-		}
+				/// <summary>
+				/// Lazy-getter for XML reader settings. May throw a <see cref="InvalidDataException"/> if there is a problem with the translation schema.
+				/// </summary>
+				/// <returns>
+				/// A <see cref="XmlReaderSettings"/> with the default values for validating the translation document against the translation schema
+				/// </returns>
+				public static XmlReaderSettings GetReaderSettings()
+				{
+						if (settings == null)
+						{
+								settings = new XmlReaderSettings();
+								settings.ValidationType = ValidationType.Schema;
+								//settings.ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings;
+								settings.ProhibitDtd = true;
+								settings.ValidationEventHandler += new ValidationEventHandler(ValidationEventMethod);
+								XmlSchemaSet cache = new XmlSchemaSet();
+								string path = "IBBoard.WarFoundry.schemas.";
+								Assembly assm = Assembly.GetExecutingAssembly();
+								XmlTools.AddSchemaToSetFromResource(cache, NS_BASE + "core", assm, path + "warfoundry-core.xsd");
+								XmlTools.AddSchemaToSetFromResource(cache, NS_BASE + "cats", assm, path + "warfoundry-cats.xsd");
+								XmlTools.AddSchemaToSetFromResource(cache, NS_BASE + "race", assm, path + "race.xsd");
+								XmlTools.AddSchemaToSetFromResource(cache, NS_BASE + "system", assm, path + "system.xsd");
+								XmlTools.AddSchemaToSetFromResource(cache, NS_BASE + "army", assm, path + "army.xsd");
+								settings.Schemas.Add(cache);
+								settings.Schemas.CompilationSettings.EnableUpaCheck = false;
+						}
+			
+						return settings;
+				}
 		
-		private static void AddSchemaToCache(XmlSchemaSet cache, string xmlNamespace, string schemaLocation)
-		{
-			try
-			{
-				cache.Add(xmlNamespace, schemaLocation);
-			}
-			catch (IOException)
-			{
-				//TODO: Warn on schema failure
-			}
-			catch (XmlSchemaException)
-			{
-				//TODO: Warn on schema failure
-			}
-			catch (XmlException)
-			{
-				//TODO: Warn on schema failure
-			}
-		}
+				private static void ValidationEventMethod(object sender, ValidationEventArgs e)
+				{
+						if (e.Severity == XmlSeverityType.Error)
+						{
+								throw new InvalidFileException("Problem validating against schema for WarFoundry data: " + e.Message, e.Exception);
+						} else
+						{
+								//TODO: Fire some kind of warning event
+						}
+				}
 		
-		public static XmlDocument CreateXmlDocumentFromStream(Stream stream)
-		{
-			XmlDocument doc = new XmlDocument();
-			XmlReader reader = XmlReader.Create(stream, GetReaderSettings());
+				public static XmlDocument CreateXmlDocumentFromStream(Stream stream)
+				{
+						XmlDocument doc = new XmlDocument();
+						XmlReader reader = XmlReader.Create(stream, GetReaderSettings());
 			
-			try
-			{
-				doc.Load(reader);
-			}
+						try
+						{
+								doc.Load(reader);
+						}
 			//Don't catch XMLSchemaExceptions - let them get thrown out
 			finally
-			{
-				reader.Close();
-			}
+						{
+								reader.Close();
+						}
 
-			return doc;
-		}
+						return doc;
+				}
 		
-		public static bool CanCompleteLoading(IWarFoundryStagedLoadObject obj)
-		{
-			bool canLoad = true;			
+				public static bool CanCompleteLoading(IWarFoundryStagedLoadObject obj)
+				{
+						bool canLoad = true;			
 			
-			if (obj.IsFullyLoaded)
-			{
-				canLoad = false;
-			}
-			else if (obj.IsLoading)
-			{
-				canLoad = false;
-			}
+						if (obj.IsFullyLoaded)
+						{
+								canLoad = false;
+						} else if (obj.IsLoading)
+						{
+								canLoad = false;
+						}
 			
-			return canLoad;
+						return canLoad;
+				}
 		}
-	}
 }
--- a/API/WarFoundryHacks.cs	Wed May 23 21:00:33 2012 +0100
+++ b/API/WarFoundryHacks.cs	Mon Jun 25 21:04:02 2012 +0100
@@ -19,7 +19,7 @@
 		public static void Initialise()
 		{
 			//Set default data path - should be a preference
-			WarFoundryLoader.GetDefault().AddLoadDirectory(new DirectoryInfo(Path.Combine(Constants.ExecutablePath, "data")));
+			//WarFoundryLoader.GetDefault().AddLoadDirectory(new DirectoryInfo(Path.Combine(Constants.ExecutablePath, "data")));
 			//Make sure we have at least one loader - should be controlled by plugins
 			WarFoundryLoader.GetDefault().RegisterFactory(WarFoundryXmlFactory.GetFactory());
 			//Make sure we have a way to save files - should be controlled by plugins
--- a/IBBoard.WarFoundry.API.csproj	Wed May 23 21:00:33 2012 +0100
+++ b/IBBoard.WarFoundry.API.csproj	Mon Jun 25 21:04:02 2012 +0100
@@ -62,42 +62,6 @@
     <None Include="COPYING" />
     <Compile Include="API\Exporters\WarFoundryXMLWithXSLExporter.cs" />
     <Compile Include="AssemblyInfo.cs" />
-    <None Include="schemas\army.xsd">
-      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None>
-    <None Include="schemas\race.xsd">
-      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None>
-    <None Include="schemas\system.xsd">
-      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None>
-    <None Include="schemas\warfoundry-cats.xsd">
-      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None>
-    <None Include="schemas\warfoundry-core.xsd">
-      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None>
-    <None Include="schemas\xhtml1-strict.dtd">
-      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None>
-    <None Include="schemas\xhtml-lat1.ent">
-      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None>
-    <None Include="schemas\xhtml-special.ent">
-      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None>
-    <None Include="schemas\xhtml-symbol.ent">
-      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None>
     <Compile Include="API\Objects\ICostedWarFoundryObject.cs" />
     <Compile Include="API\Commands\CreateAndAddUnitCommand.cs" />
     <Compile Include="API\Commands\RemoveUnitCommand.cs" />
@@ -234,4 +198,33 @@
   <ItemGroup>
     <Folder Include="API\Objects\Requirement\Context\" />
   </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="schemas\army.xsd">
+      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
+    </EmbeddedResource>
+    <EmbeddedResource Include="schemas\race.xsd">
+      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
+    </EmbeddedResource>
+    <EmbeddedResource Include="schemas\system.xsd">
+      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
+    </EmbeddedResource>
+    <EmbeddedResource Include="schemas\warfoundry-cats.xsd">
+      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
+    </EmbeddedResource>
+    <EmbeddedResource Include="schemas\warfoundry-core.xsd">
+      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
+    </EmbeddedResource>
+    <EmbeddedResource Include="schemas\xhtml1-strict.dtd">
+      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
+    </EmbeddedResource>
+    <EmbeddedResource Include="schemas\xhtml-lat1.ent">
+      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
+    </EmbeddedResource>
+    <EmbeddedResource Include="schemas\xhtml-special.ent">
+      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
+    </EmbeddedResource>
+    <EmbeddedResource Include="schemas\xhtml-symbol.ent">
+      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
+    </EmbeddedResource>
+  </ItemGroup>
 </Project>
\ No newline at end of file