changeset 482:1ed2f3ab5e35

Re #419: Remove assumptions of a file-based install * Swap API to using new "loadable object" and "loadable object source" wrappers to allow file-based or memory-based loading
author IBBoard <dev@ibboard.co.uk>
date Sat, 07 Jul 2012 21:01:32 +0100
parents 81f32062c9fa
children 4d6c92744254
files API/AbstractWarFoundryLoader.cs API/Factories/AbstractNativeWarFoundryFactory.cs API/Factories/AbstractNonNativeFileExtensionWarFoundryFactory.cs API/Factories/AbstractNonNativeWarFoundryFactory.cs API/Factories/AbstractWarFoundryFactory.cs API/Factories/DummyWarFoundryFactory.cs API/Factories/IWarFoundryFactory.cs API/FileLoadFailure.cs API/Loading/ILoadableObject.cs API/Loading/ILoadableObjectSource.cs API/Loading/LoadableFileObject.cs API/Loading/LoadableObjectSourceDirectory.cs API/Loading/LoadableObjectSourceResourceSet.cs API/Loading/LoadableResourceObject.cs IBBoard.WarFoundry.API.csproj
diffstat 15 files changed, 387 insertions(+), 98 deletions(-) [+]
line wrap: on
line diff
--- a/API/AbstractWarFoundryLoader.cs	Mon Jun 25 21:04:02 2012 +0100
+++ b/API/AbstractWarFoundryLoader.cs	Sat Jul 07 21:01:32 2012 +0100
@@ -8,6 +8,7 @@
 using IBBoard.IO;
 using IBBoard.WarFoundry.API.Factories;
 using IBBoard.WarFoundry.API.Objects;
+using IBBoard.WarFoundry.API.Loading;
 
 namespace IBBoard.WarFoundry.API
 {
@@ -16,7 +17,7 @@
 	/// </summary>
 	public abstract class AbstractWarFoundryLoader
 	{
-		private ICollection<DirectoryInfo> directories;
+		private ICollection<ILoadableObjectSource> directories;
 		private ICollection<INativeWarFoundryFactory> factories;
 		private ICollection<INonNativeWarFoundryFactory> nonNativeFactories;
 		private Dictionary<IWarFoundryFactory, SimpleSet<IWarFoundryObject>> loadedObjects;
@@ -28,8 +29,8 @@
 
 		protected AbstractWarFoundryLoader()
 		{
-			directories = new List<DirectoryInfo>();
-			directories.Add(new DirectoryInfo(WarFoundryLoader.DEFAULT_USER_DATA_DIR));
+			directories = new List<ILoadableObjectSource>();
+			directories.Add(new LoadableObjectSourceDirectory(WarFoundryLoader.DEFAULT_USER_DATA_DIR));
 			factories = new List<INativeWarFoundryFactory>();
 			nonNativeFactories = new List<INonNativeWarFoundryFactory>();
 			loadedObjects = new Dictionary<IWarFoundryFactory,SimpleSet<IWarFoundryObject>>();
@@ -41,11 +42,23 @@
 		/// <param name="directory">
 		/// The <see cref="DirectoryInfo"/> to add to the list for searching for data files
 		/// </param>
+		[Obsolete("Use load sources")]
 		public void AddLoadDirectory(DirectoryInfo directory)
 		{
-			if (!directories.Contains(directory))
+			AddLoadSource(new LoadableObjectSourceDirectory(directory));
+		}
+
+		/// <summary>
+		/// Adds a load source to the set of sources that objects will be loaded from
+		/// </summary>
+		/// <param name='loadSource'>
+		/// The load source.
+		/// </param>
+		public void AddLoadSource(ILoadableObjectSource loadSource)
+		{
+			if (!directories.Contains(loadSource))
 			{
-				directories.Add(directory);
+				directories.Add(loadSource);
 			}
 		}
 		
@@ -55,11 +68,17 @@
 		/// <param name="directory">
 		/// A <see cref="DirectoryInfo"/>
 		/// </param>
+		[Obsolete("Use load sources")]
 		public void RemoveLoadDirectory(DirectoryInfo directory)
+		{			
+			RemoveLoadSource(new LoadableObjectSourceDirectory(directory));
+		}
+
+		public void RemoveLoadSource(ILoadableObjectSource loadSource)
 		{
-			if (directories.Contains(directory))
+			if (directories.Contains(loadSource))
 			{
-				directories.Remove(directory);
+				directories.Remove(loadSource);
 			}
 		}
 		
@@ -129,8 +148,8 @@
 		{
 			OnFileLoadingStarted();
 			PrepareForFileLoad();
-			Dictionary<FileInfo, IWarFoundryFactory> loadableRaces = new Dictionary<FileInfo, IWarFoundryFactory>();
-			Dictionary<FileInfo, IWarFoundryFactory> loadableGameSystems = new Dictionary<FileInfo, IWarFoundryFactory>();
+			Dictionary<ILoadableObject, IWarFoundryFactory> loadableRaces = new Dictionary<ILoadableObject, IWarFoundryFactory>();
+			Dictionary<ILoadableObject, IWarFoundryFactory> loadableGameSystems = new Dictionary<ILoadableObject, IWarFoundryFactory>();
 			List<FileLoadFailure> failedLoads = FillLoadableFiles(loadableRaces, loadableGameSystems);
 			failedLoads.AddRange(LoadGameSystems(loadableGameSystems));
 			failedLoads.AddRange(LoadRaces(loadableRaces));
@@ -165,29 +184,24 @@
 			//Do nothing special
 		}
 
-		private List<FileLoadFailure> FillLoadableFiles(Dictionary<FileInfo, IWarFoundryFactory> loadableRaces, Dictionary<FileInfo, IWarFoundryFactory> loadableGameSystems)
+		private List<FileLoadFailure> FillLoadableFiles(Dictionary<ILoadableObject, IWarFoundryFactory> loadableRaces, Dictionary<ILoadableObject, IWarFoundryFactory> loadableGameSystems)
 		{			
 			List<FileLoadFailure> fails = new List<FileLoadFailure>();
 			
-			foreach (DirectoryInfo directory in directories)
+			foreach (ILoadableObjectSource directory in directories)
 			{
-				directory.Refresh();
-
-				if (directory.Exists)
-				{
-					List<FileLoadFailure> directoryFails = FillLoadableFilesForDirectory(loadableRaces, loadableGameSystems, directory);
-					fails.AddRange(directoryFails);
-				}
+				List<FileLoadFailure> directoryFails = FillLoadableFilesForDirectory(loadableRaces, loadableGameSystems, directory);
+				fails.AddRange(directoryFails);
 			}
 			
 			return fails;
 		}
 
-		private List<FileLoadFailure> FillLoadableFilesForDirectory(Dictionary<FileInfo, IWarFoundryFactory> loadableRaces, Dictionary<FileInfo, IWarFoundryFactory> loadableGameSystems, DirectoryInfo directory)
+		private List<FileLoadFailure> FillLoadableFilesForDirectory(Dictionary<ILoadableObject, IWarFoundryFactory> loadableRaces, Dictionary<ILoadableObject, IWarFoundryFactory> loadableGameSystems, ILoadableObjectSource directory)
 		{
 			List<FileLoadFailure> fails = new List<FileLoadFailure>();
 		
-			foreach (FileInfo file in directory.GetFiles())
+			foreach (ILoadableObject file in directory.GetLoadableObjects())
 			{
 				IWarFoundryFactory factory = GetGameSystemLoadingFactoryForFile(file);
 				
@@ -210,16 +224,11 @@
 					}
 				}
 			}
-			
-			foreach (DirectoryInfo subdir in directory.GetDirectories())
-			{
-				fails.AddRange(FillLoadableFilesForDirectory(loadableRaces, loadableGameSystems, subdir));
-			}
 
 			return fails;
 		}
 
-		private IWarFoundryFactory GetGameSystemLoadingFactoryForFile(FileInfo file)
+		private IWarFoundryFactory GetGameSystemLoadingFactoryForFile(ILoadableObject file)
 		{
 			IWarFoundryFactory loadingFactory = null;
 			
@@ -247,7 +256,7 @@
 			return loadingFactory;
 		}
 
-		private IWarFoundryFactory GetRaceLoadingFactoryForFile(FileInfo file)
+		private IWarFoundryFactory GetRaceLoadingFactoryForFile(ILoadableObject file)
 		{
 			IWarFoundryFactory loadingFactory = null;
 			
@@ -275,12 +284,12 @@
 			return loadingFactory;
 		}
 
-		private List<FileLoadFailure> LoadGameSystems(Dictionary<FileInfo, IWarFoundryFactory> gameSystemFiles)
+		private List<FileLoadFailure> LoadGameSystems(Dictionary<ILoadableObject, IWarFoundryFactory> gameSystemFiles)
 		{
 			List<FileLoadFailure> fails = new List<FileLoadFailure>();
 
 			
-			foreach (FileInfo file in gameSystemFiles.Keys)
+			foreach (ILoadableObject file in gameSystemFiles.Keys)
 			{
 				FileLoadFailure failure = null;
 				
@@ -307,11 +316,11 @@
 			return fails;
 		}
 
-		private List<FileLoadFailure> LoadRaces(Dictionary<FileInfo, IWarFoundryFactory> raceFiles)
+		private List<FileLoadFailure> LoadRaces(Dictionary<ILoadableObject, IWarFoundryFactory> raceFiles)
 		{
 			List<FileLoadFailure> fails = new List<FileLoadFailure>();
 			
-			foreach (FileInfo file in raceFiles.Keys)
+			foreach (ILoadableObject file in raceFiles.Keys)
 			{
 				FileLoadFailure failure = null;
 				
@@ -338,7 +347,7 @@
 			return fails;
 		}
 
-		private bool LoadObject(FileInfo file, IWarFoundryFactory factory)
+		private bool LoadObject(ILoadableObject file, IWarFoundryFactory factory)
 		{
 			factory.RaceLoaded += StoreRace;
 			factory.GameSystemLoaded += StoreGameSystem;
@@ -351,13 +360,18 @@
 		/// 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
+		/// A <see cref="ILoadableObject"/> for the file to attempt to load
 		/// </param>
 		/// <returns>
 		/// An ICollection of IWarFoundryObjects loaded from <code>file</code>
 		/// </returns>
 		public ICollection<IWarFoundryObject> LoadFile(FileInfo file)
 		{
+			return LoadObject(new LoadableFileObject(file));
+		}
+
+		public ICollection<IWarFoundryObject> LoadObject(ILoadableObject file)
+		{
 			IWarFoundryFactory loadFactory = null;
 			ICollection<IWarFoundryObject> objs = LoadFileWithNonNativeFactories(file, out loadFactory);
 				
@@ -378,7 +392,7 @@
 			return objs;
 		}
 
-		private ICollection<IWarFoundryObject> LoadFileWithNonNativeFactories(FileInfo file, out IWarFoundryFactory loadFactory)
+		private ICollection<IWarFoundryObject> LoadFileWithNonNativeFactories(ILoadableObject file, out IWarFoundryFactory loadFactory)
 		{
 			ICollection<IWarFoundryObject> objs = null;
 			loadFactory = null;
@@ -405,7 +419,7 @@
 			return objs;
 		}
 
-		private ICollection<IWarFoundryObject> LoadFileWithNativeFactories(FileInfo file, out IWarFoundryFactory loadFactory)
+		private ICollection<IWarFoundryObject> LoadFileWithNativeFactories(ILoadableObject file, out IWarFoundryFactory loadFactory)
 		{
 			ICollection<IWarFoundryObject> objs = null;
 			loadFactory = null;
@@ -626,6 +640,11 @@
 
 		public Army LoadArmy(FileInfo file)
 		{
+			return LoadArmy(new LoadableFileObject(file));
+		}
+
+		public Army LoadArmy(ILoadableObject file)
+		{
 			IWarFoundryFactory factory = GetArmyLoadingFactoryForFile(file);			
 			Army loadedArmy = null;
 			
@@ -648,7 +667,7 @@
 			return loadedArmy;
 		}
 
-		private IWarFoundryFactory GetArmyLoadingFactoryForFile(FileInfo file)
+		private IWarFoundryFactory GetArmyLoadingFactoryForFile(ILoadableObject file)
 		{
 			IWarFoundryFactory loadingFactory = null;
 			
--- a/API/Factories/AbstractNativeWarFoundryFactory.cs	Mon Jun 25 21:04:02 2012 +0100
+++ b/API/Factories/AbstractNativeWarFoundryFactory.cs	Sat Jul 07 21:01:32 2012 +0100
@@ -14,6 +14,7 @@
 using IBBoard.WarFoundry.API.Objects;
 using ICSharpCode.SharpZipLib.Zip;
 using IBBoard.Collections;
+using IBBoard.WarFoundry.API.Loading;
 
 namespace IBBoard.WarFoundry.API.Factories
 {
@@ -27,16 +28,17 @@
 			//Do nothing - just make the constructor non-public
 		}
 				
-		protected override ZipFile GetFileAsSupportedType(FileInfo file)
+		protected override ZipFile GetFileAsSupportedType(ILoadableObject file)
 		{
-			ZipFile zip = null;
-			string ext = file.Extension.ToLower();
+			ZipFile zip = null;
+			string name = file.Name;
+			string ext = file.Name.Substring(name.LastIndexOf('.')).ToLower();
 
 			if (ext == ".race" || ext == ".army" || ext == ".system")
 			{
 				try
 				{
-					zip = new ZipFile(file.FullName);
+					zip = new ZipFile(file.GetStream());
 				}
 				catch (ZipException)
 				{
--- a/API/Factories/AbstractNonNativeFileExtensionWarFoundryFactory.cs	Mon Jun 25 21:04:02 2012 +0100
+++ b/API/Factories/AbstractNonNativeFileExtensionWarFoundryFactory.cs	Sat Jul 07 21:01:32 2012 +0100
@@ -5,10 +5,11 @@
 using System.Collections.Generic;
 using System.IO;
 using IBBoard.WarFoundry.API.Objects;
+using IBBoard.WarFoundry.API.Loading;
 
 namespace IBBoard.WarFoundry.API.Factories
 {	
-	public abstract class AbstractNonNativeFileExtensionWarFoundryFactory : AbstractNonNativeWarFoundryFactory<FileInfo>
+	public abstract class AbstractNonNativeFileExtensionWarFoundryFactory : AbstractNonNativeWarFoundryFactory<ILoadableObject>
 	{
 		protected abstract string ArmyFileExtension { get; }
 
@@ -16,38 +17,38 @@
 
 		protected abstract string GameSystemFileExtension { get; }
 		
-		protected override bool CheckCanHandleFileFormat(FileInfo file)
+		protected override bool CheckCanHandleFileFormat(ILoadableObject file)
 		{
 			return CheckCanHandleFileAsArmy(file) || CheckCanHandleFileAsRace(file) || CheckCanHandleFileAsGameSystem(file);
 		}
 		
-		protected override bool CheckCanHandleFileAsArmy(FileInfo file)
+		protected override bool CheckCanHandleFileAsArmy(ILoadableObject file)
 		{
 			return ArmyFileExtension != null && file.Name.ToLower().EndsWith(ArmyFileExtension);
 		}
 		
-		protected override bool CheckCanHandleFileAsRace(FileInfo file)
+		protected override bool CheckCanHandleFileAsRace(ILoadableObject file)
 		{
 			return RaceFileExtension != null && file.Name.ToLower().EndsWith(RaceFileExtension);
 		}
 		
-		protected override bool CheckCanHandleFileAsGameSystem(FileInfo file)
+		protected override bool CheckCanHandleFileAsGameSystem(ILoadableObject file)
 		{
 			return GameSystemFileExtension != null && file.Name.ToLower().EndsWith(GameSystemFileExtension);
 		}
 		
-		protected override FileInfo GetFileAsSupportedType(FileInfo file)
+		protected override ILoadableObject GetFileAsSupportedType(ILoadableObject file)
 		{
 			return file;
 		}
 		
-		protected abstract Army CreateArmyFromFile(FileInfo file);
+		protected abstract Army CreateArmyFromFile(ILoadableObject file);
 
-		protected abstract Race CreateRaceFromFile(FileInfo file);
+		protected abstract Race CreateRaceFromFile(ILoadableObject file);
 
-		protected abstract GameSystem CreateGameSystemFromFile(FileInfo file);
+		protected abstract GameSystem CreateGameSystemFromFile(ILoadableObject file);
 		
-		protected override ICollection<IWarFoundryObject> DoCreateObjectsFromFile(FileInfo file)
+		protected override ICollection<IWarFoundryObject> DoCreateObjectsFromFile(ILoadableObject file)
 		{
 			IWarFoundryObject obj = null;
 			
--- a/API/Factories/AbstractNonNativeWarFoundryFactory.cs	Mon Jun 25 21:04:02 2012 +0100
+++ b/API/Factories/AbstractNonNativeWarFoundryFactory.cs	Sat Jul 07 21:01:32 2012 +0100
@@ -10,6 +10,6 @@
 {
 	public abstract class AbstractNonNativeWarFoundryFactory<FILE_TYPE> : AbstractWarFoundryFactory<FILE_TYPE>, INonNativeWarFoundryFactory
 	{
-		public abstract string NonNativeDataType { get;	}
+		public abstract string NonNativeDataType { get; }
 	}
 }
--- a/API/Factories/AbstractWarFoundryFactory.cs	Mon Jun 25 21:04:02 2012 +0100
+++ b/API/Factories/AbstractWarFoundryFactory.cs	Sat Jul 07 21:01:32 2012 +0100
@@ -6,6 +6,7 @@
 using System.IO;
 using System.Collections.Generic;
 using IBBoard.WarFoundry.API.Objects;
+using IBBoard.WarFoundry.API.Loading;
 
 namespace IBBoard.WarFoundry.API.Factories
 {
@@ -23,7 +24,7 @@
 			obj.SetAsFullyLoaded();
 		}
 
-		public bool CanHandleFileFormat (FileInfo file)
+		public bool CanHandleFileFormat(ILoadableObject file)
 		{
 			FILE_TYPE typedFile = GetFileAsSupportedType(file);
 			bool canHandle = typedFile != null && CheckCanHandleFileFormat(typedFile);
@@ -36,7 +37,7 @@
 			return canHandle;
 		}
 
-		public bool CanHandleFileAsRace(FileInfo file)
+		public bool CanHandleFileAsRace(ILoadableObject file)
 		{
 			FILE_TYPE typedFile = GetFileAsSupportedType(file);
 			bool canHandle = typedFile != null && CheckCanHandleFileAsRace(typedFile);
@@ -49,7 +50,7 @@
 			return canHandle;
 		}
 
-		public bool CanHandleFileAsGameSystem(FileInfo file)
+		public bool CanHandleFileAsGameSystem(ILoadableObject file)
 		{
 			FILE_TYPE typedFile = GetFileAsSupportedType(file);
 			bool canHandle = typedFile != null && CheckCanHandleFileAsGameSystem(typedFile);
@@ -62,7 +63,7 @@
 			return canHandle;
 		}
 
-		public bool CanHandleFileAsArmy(FileInfo file)
+		public bool CanHandleFileAsArmy(ILoadableObject file)
 		{
 			FILE_TYPE typedFile = GetFileAsSupportedType(file);
 			bool canHandle = typedFile != null && CheckCanHandleFileAsArmy(typedFile);
@@ -81,16 +82,16 @@
 		}
 		
 		/// <summary>
-		/// Converts the <see cref="FileInfo"/> object in to the appropriate type for this class so that it can perform its checks. If no conversion is required (the test can be performed on a <see cref="FileInfo"/> object) the object should be returned with no modification. 
+		/// Converts the <see cref="ILoadableObject"/> object in to the appropriate type for this class so that it can perform its checks. If no conversion is required (the test can be performed on a <see cref="ILoadableObject"/> object) the object should be returned with no modification. 
 		/// If the file is not of supported type the <code>null</code> should be returned.
 		/// </summary>
 		/// <param name="file">
-		/// A <see cref="FileInfo"/> to get the supported source object from.
+		/// A <see cref="ILoadableObject"/> to get the supported source object from.
 		/// </param>
 		/// <returns>
-		/// An object of type <see cref="FILE_TYPE"/> that has been converted from the input <see cref="FileInfo"/> object, or <code>null</code> if the conversion cannot be made.
+		/// An object of type <see cref="FILE_TYPE"/> that has been converted from the input <see cref="ILoadableObject"/> object, or <code>null</code> if the conversion cannot be made.
 		/// </returns>
-		protected abstract FILE_TYPE GetFileAsSupportedType(FileInfo file);
+		protected abstract FILE_TYPE GetFileAsSupportedType(ILoadableObject file);
 		
 		/// <summary>
 		/// Checks whether the factory thinks it can load data from the file in its paramaterised type.
@@ -137,7 +138,7 @@
 		protected abstract bool CheckCanHandleFileAsArmy(FILE_TYPE file);
 		
 		
-		public ICollection<IWarFoundryObject> CreateObjectsFromFile(FileInfo file)
+		public ICollection<IWarFoundryObject> CreateObjectsFromFile(ILoadableObject file)
 		{
 			return DoCreateObjectsFromFile(GetFileAsSupportedType(file));
 		}
--- a/API/Factories/DummyWarFoundryFactory.cs	Mon Jun 25 21:04:02 2012 +0100
+++ b/API/Factories/DummyWarFoundryFactory.cs	Sat Jul 07 21:01:32 2012 +0100
@@ -6,6 +6,7 @@
 using IBBoard.WarFoundry.API.Objects;
 using System.IO;
 using System.Collections.Generic;
+using IBBoard.WarFoundry.API.Loading;
 
 namespace IBBoard.WarFoundry.API.Factories
 {
@@ -33,27 +34,27 @@
 			obj.SetAsFullyLoaded();
 		}
 
-		public bool CanHandleFileFormat(FileInfo file)
+		public bool CanHandleFileFormat(ILoadableObject file)
 		{
 			return false;
 		}
 
-		public bool CanHandleFileAsRace(FileInfo file)
+		public bool CanHandleFileAsRace(ILoadableObject file)
 		{
 			return false;
 		}
 
-		public bool CanHandleFileAsGameSystem(FileInfo file)
+		public bool CanHandleFileAsGameSystem(ILoadableObject file)
 		{
 			return false;
 		}
 
-		public bool CanHandleFileAsArmy(FileInfo file)
+		public bool CanHandleFileAsArmy(ILoadableObject file)
 		{
 			return false;
 		}
 
-		public ICollection<IWarFoundryObject> CreateObjectsFromFile(FileInfo file)
+		public ICollection<IWarFoundryObject> CreateObjectsFromFile(ILoadableObject file)
 		{
 			return new List<IWarFoundryObject>();
 		}
--- a/API/Factories/IWarFoundryFactory.cs	Mon Jun 25 21:04:02 2012 +0100
+++ b/API/Factories/IWarFoundryFactory.cs	Sat Jul 07 21:01:32 2012 +0100
@@ -6,6 +6,7 @@
 using System.IO;
 using System.Collections.Generic;
 using IBBoard.WarFoundry.API.Objects;
+using IBBoard.WarFoundry.API.Loading;
 
 namespace IBBoard.WarFoundry.API.Factories
 {
@@ -29,45 +30,45 @@
 		/// 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 <see cref="FileInfo"/> for the file to check support for.
+		/// A <see cref="ILoadableObject"/> for the file to check support for.
 		/// </param>
 		/// <returns>
 		/// <code>true</code> if the file appears to be supported for loading by this factory, else returns <code>false</code>
 		/// </returns>
-		bool CanHandleFileFormat(FileInfo file);
+		bool CanHandleFileFormat(ILoadableObject file);
 
 		/// <summary>
 		/// Checks if the factory thinks it can handle the supplied file as a Race. Checks can be performed on file extension or some basic check of file content, or some other method.
 		/// </summary>
 		/// <param name="file">
-		/// A <see cref="FileInfo"/> for the file to check support for as a file containing Race information.
+		/// A <see cref="ILoadableObject"/> for the file to check support for as a file containing Race information.
 		/// </param>
 		/// <returns>
 		/// <code>true</code> if the file appears to be supported for loading by this factory as a Race, else returns <code>false</code>
 		/// </returns>
-		bool CanHandleFileAsRace(FileInfo file);
+		bool CanHandleFileAsRace(ILoadableObject file);
 
 		/// <summary>
 		/// Checks if the factory thinks it can handle the supplied file as a GameSystem. Checks can be performed on file extension or some basic check of file content, or some other method.
 		/// </summary>
 		/// <param name="file">
-		/// A <see cref="FileInfo"/> for the file to check support for as a file containing GameSystem information.
+		/// A <see cref="ILoadableObject"/> for the file to check support for as a file containing GameSystem information.
 		/// </param>
 		/// <returns>
 		/// <code>true</code> if the file appears to be supported for loading by this factory as a GameSystem, else returns <code>false</code>
 		/// </returns>
-		bool CanHandleFileAsGameSystem(FileInfo file);
+		bool CanHandleFileAsGameSystem(ILoadableObject file);
 
 		/// <summary>
 		/// Checks if the factory thinks it can handle the supplied file as a Army. Checks can be performed on file extension or some basic check of file content, or some other method.
 		/// </summary>
 		/// <param name="file">
-		/// A <see cref="FileInfo"/> for the file to check support for as a file containing Army information.
+		/// A <see cref="ILoadableObject"/> for the file to check support for as a file containing Army information.
 		/// </param>
 		/// <returns>
 		/// <code>true</code> if the file appears to be supported for loading by this factory as a Army, else returns <code>false</code>
 		/// </returns>
-		bool CanHandleFileAsArmy(FileInfo file);
+		bool CanHandleFileAsArmy(ILoadableObject file);
 		
 		/// <summary>
 		/// Reads the data from the supplied file and returns it as a collection of loadable objects. In addition, it fires the appropriate XxxLoaded event
@@ -76,11 +77,11 @@
 		/// May throw a <see cref=" IBBoard.IO.InvalidFileException"/> if the file is supported by the Factory but the content is invalid.
 		/// </summary>
 		/// <param name="file">
-		/// A <see cref="FileInfo"/> for the file to load data from
+		/// A <see cref="ILoadableObject"/> for the file to load data from
 		/// </param>
 		/// <returns>
 		/// A <see cref="ICollection`1"/> of <see cref="IWarFoundryObject"/>s that were loaded from the file
 		/// </returns>
-		ICollection<IWarFoundryObject> CreateObjectsFromFile(FileInfo file);
+		ICollection<IWarFoundryObject> CreateObjectsFromFile(ILoadableObject file);
 	}
 }
--- a/API/FileLoadFailure.cs	Mon Jun 25 21:04:02 2012 +0100
+++ b/API/FileLoadFailure.cs	Sat Jul 07 21:01:32 2012 +0100
@@ -6,6 +6,7 @@
 using System.IO;
 using IBBoard.Lang;
 using IBBoard.WarFoundry.API.Factories;
+using IBBoard.WarFoundry.API.Loading;
 
 namespace IBBoard.WarFoundry.API
 {
@@ -14,7 +15,7 @@
 	/// </summary>
 	public class FileLoadFailure
 	{
-		private FileInfo failedFile;
+		private ILoadableObject failedFile;
 		private IWarFoundryFactory loadingFactory;
 		private string defaultMessage;
 		private string messageTranslationID;
@@ -25,7 +26,7 @@
 		/// Constructor for a failed file load where no factory was found. Translatable messages can be providied through a <code>translationID</code> or skipped by passing <code>null</code>.
 		/// </summary>
 		/// <param name="file">
-		/// The <see cref="FileInfo"/> that failed to load
+		/// The <see cref="ILoadableObject"/> that failed to load
 		/// </param>
 		/// <param name="message">
 		/// A message about the failure in English - used as a default fall-back message.
@@ -33,7 +34,7 @@
 		/// <param name="translationID">
 		/// The ID of a translation for the message.
 		/// </param>
-		public FileLoadFailure(FileInfo file, string message, string translationID) : this (file, null, message, "")
+		public FileLoadFailure(ILoadableObject file, string message, string translationID) : this (file, null, message, "")
 		{
 		}
 		
@@ -41,7 +42,7 @@
 		/// Constructor for a failed file load where a factory was identified as supporting the file but failed to load it. Translatable messages can be providied through a <code>translationID</code> or skipped by passing <code>null</code>.
 		/// </summary>
 		/// <param name="file">
-		/// The <see cref="FileInfo"/> that failed to load
+		/// The <see cref="ILoadableObject"/> that failed to load
 		/// </param>
 		/// <param name="factory">
 		/// The <see cref="IWarFoundryFactory"/> that failed to load the file
@@ -52,7 +53,7 @@
 		/// <param name="translationID">
 		/// The ID of a translation for the message.
 		/// </param>
-		public FileLoadFailure(FileInfo file, IWarFoundryFactory factory, string message, string translationID) : this(file, factory, message, translationID, null)
+		public FileLoadFailure(ILoadableObject file, IWarFoundryFactory factory, string message, string translationID) : this(file, factory, message, translationID, null)
 		{
 		}
 		
@@ -60,7 +61,7 @@
 		/// Constructor for a failed file load where a factory was identified as supporting the file but an exception occurred while loading it. Translatable messages can be providied through a <code>translationID</code> or skipped by passing <code>null</code>.
 		/// </summary>
 		/// <param name="file">
-		/// The <see cref="FileInfo"/> that failed to load
+		/// The <see cref="ILoadableObject"/> that failed to load
 		/// </param>
 		/// <param name="factory">
 		/// The <see cref="IWarFoundryFactory"/> that failed to load the file
@@ -74,7 +75,7 @@
 		/// <param name="exception">
 		/// The <see cref="Exception"/> that occurred to cause the load to fail
 		/// </param>
-		public FileLoadFailure(FileInfo file, IWarFoundryFactory factory, string message, string translationID, Exception exception)
+		public FileLoadFailure(ILoadableObject file, IWarFoundryFactory factory, string message, string translationID, Exception exception)
 		{
 			failedFile = file;
 			loadingFactory = factory;
@@ -83,7 +84,7 @@
 			cause = exception;
 		}
 
-		public FileInfo FailedFile
+		public ILoadableObject FailedFile
 		{
 			get
 			{
@@ -97,14 +98,14 @@
 			{
 				if (message == null)
 				{
-					string fileName = FailedFile.FullName;
+					string fileName = FailedFile.Name;
 					string factoryType = (loadingFactory == null ? "" : loadingFactory.GetType().Name);
 					if (String.IsNullOrEmpty(messageTranslationID))
 					{
 						message = String.Format(defaultMessage, fileName, factoryType);
-				 	}
+					}
 					else
-				 	{
+					{
 						message = Translation.GetTranslation(messageTranslationID, defaultMessage, fileName, factoryType);
 					}
 				}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/API/Loading/ILoadableObject.cs	Sat Jul 07 21:01:32 2012 +0100
@@ -0,0 +1,27 @@
+// This file (ILoadableObject.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2012 IBBoard
+//
+// The file and the library/program it is in are licensed and distributed, without warranty, under the GNU Affero GPL license, either version 3 of the License or (at your option) any later version. Please see COPYING for more information and the full license.
+using System;
+using System.IO;
+
+namespace IBBoard.WarFoundry.API.Loading
+{
+	/// <summary>
+	/// Interface for objects that are the source of WarFoundryObjects that can be loaded by a Factory.
+	/// Examples include files and streams.
+	/// </summary>
+	public interface ILoadableObject
+	{
+		string Name { get; }
+
+		/// <summary>
+		/// Gets the stream for the loadable object that the WarFoundryObject can be loaded from. This stream will be
+		/// created each time the method is called and must be disposed of by the calling function.
+		/// </summary>
+		/// <returns>
+		/// The stream for the source.
+		/// </returns>
+		Stream GetStream();
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/API/Loading/ILoadableObjectSource.cs	Sat Jul 07 21:01:32 2012 +0100
@@ -0,0 +1,21 @@
+// This file (ILoadableObjectSource.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2012 IBBoard
+//
+// The file and the library/program it is in are licensed and distributed, without warranty, under the GNU Affero GPL license, either version 3 of the License or (at your option) any later version. Please see COPYING for more information and the full license.
+using System;
+using System.Collections.Generic;
+
+
+namespace IBBoard.WarFoundry.API.Loading
+{
+	public interface ILoadableObjectSource
+	{
+		/// <summary>
+		/// Gets the loadable objects that this source can provide
+		/// </summary>
+		/// <returns>
+		/// The loadable objects.
+		/// </returns>
+		ICollection<ILoadableObject> GetLoadableObjects();
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/API/Loading/LoadableFileObject.cs	Sat Jul 07 21:01:32 2012 +0100
@@ -0,0 +1,37 @@
+// This file (LoadableFileObject.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2012 IBBoard
+//
+// The file and the library/program it is in are licensed and distributed, without warranty, under the GNU Affero GPL license, either version 3 of the License or (at your option) any later version. Please see COPYING for more information and the full license.
+using System;
+using System.IO;
+
+namespace IBBoard.WarFoundry.API.Loading
+{
+	public class LoadableFileObject : ILoadableObject
+	{
+		private FileInfo file;
+
+		public LoadableFileObject(string filePath) : this(new FileInfo(filePath))
+		{
+			//Do nothing extra
+		}
+
+		public LoadableFileObject(FileInfo file)
+		{
+			this.file = file;
+		}
+
+		public string Name
+		{
+			get
+			{
+				return file.FullName;
+			}
+		}
+
+		public Stream GetStream()
+		{
+			return new FileStream(file.FullName, FileMode.Open);
+		}
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/API/Loading/LoadableObjectSourceDirectory.cs	Sat Jul 07 21:01:32 2012 +0100
@@ -0,0 +1,75 @@
+// This file (LoadableObjectSourceDirectory.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2012 IBBoard
+//
+// The file and the library/program it is in are licensed and distributed, without warranty, under the GNU Affero GPL license, either version 3 of the License or (at your option) any later version. Please see COPYING for more information and the full license.
+using System;
+using System.IO;
+using System.Collections.Generic;
+
+namespace IBBoard.WarFoundry.API.Loading
+{
+	public class LoadableObjectSourceDirectory : ILoadableObjectSource
+	{
+		private DirectoryInfo dir;
+
+		public LoadableObjectSourceDirectory(string directory) : this(new DirectoryInfo(directory))
+		{
+			//Do nothing extra
+		}
+
+		public LoadableObjectSourceDirectory(DirectoryInfo directory)
+		{
+			dir = directory;
+		}
+
+		public ICollection<ILoadableObject> GetLoadableObjects()
+		{
+			ICollection<ILoadableObject> loadables;
+			dir.Refresh();
+
+			if (dir.Exists)
+			{
+				loadables = GetLoadableObjectsFromDirectory(dir);
+			}
+			else
+			{
+				loadables = new List<ILoadableObject>();
+			}
+
+			return loadables;
+		}
+
+		private ICollection<ILoadableObject> GetLoadableObjectsFromDirectory(DirectoryInfo directory)
+		{
+			List<ILoadableObject> loadables = new List<ILoadableObject>();
+
+			foreach (FileInfo file in directory.GetFiles())
+			{
+				loadables.Add(new LoadableFileObject(file));
+			}
+
+			foreach (DirectoryInfo subdir in directory.GetDirectories())
+			{
+				loadables.AddRange(GetLoadableObjectsFromDirectory(subdir));
+			}
+
+			return loadables;
+		}
+
+		public override bool Equals(object obj)
+		{			
+			if (obj == null || !obj.GetType().Equals(GetType()))
+			{
+				return false;
+			}
+
+			LoadableObjectSourceDirectory other = (LoadableObjectSourceDirectory)obj;
+			return this.dir.FullName.Equals(other.dir.FullName);
+		}
+
+		public override int GetHashCode()
+		{
+			return dir.FullName.GetHashCode() + 17;
+		}
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/API/Loading/LoadableObjectSourceResourceSet.cs	Sat Jul 07 21:01:32 2012 +0100
@@ -0,0 +1,61 @@
+// This file (LoadableObjectSourceResourceSet.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2012 IBBoard
+//
+// The file and the library/program it is in are licensed and distributed, without warranty, under the GNU Affero GPL license, either version 3 of the License or (at your option) any later version. Please see COPYING for more information and the full license.
+using System;
+using System.Reflection;
+using System.Collections.Generic;
+
+namespace IBBoard.WarFoundry.API.Loading
+{
+	public class LoadableObjectSourceResourceSet : ILoadableObjectSource
+	{
+		private ICollection<ILoadableObject> loadables;
+		private Assembly assm;
+		private string[] resIDs;
+
+		public LoadableObjectSourceResourceSet(Assembly assembly, params string[] resourceIDs)
+		{
+			assm = assembly;
+			resIDs = resourceIDs;
+		}
+
+		public ICollection<ILoadableObject> GetLoadableObjects()
+		{
+			if (loadables == null)
+			{
+				loadables = new List<ILoadableObject>();
+
+				foreach (string resourceID in resIDs)
+				{
+					loadables.Add(new LoadableResourceObject(assm, resourceID));
+				}
+			}
+
+			return loadables;
+		}
+
+		public override bool Equals(object obj)
+		{
+			if (obj == null || !obj.GetType().Equals(GetType()))
+			{
+				return false;
+			}
+
+			LoadableObjectSourceResourceSet other = (LoadableObjectSourceResourceSet)obj;
+			return this.assm == other.assm && Arrays.Difference(this.resIDs, other.resIDs).Length == 0;
+		}
+
+		public override int GetHashCode()
+		{
+			int hash = assm.GetHashCode();
+
+			foreach (string resID in resIDs)
+			{
+				hash += resID.GetHashCode();
+			}
+
+			return hash;
+		}
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/API/Loading/LoadableResourceObject.cs	Sat Jul 07 21:01:32 2012 +0100
@@ -0,0 +1,35 @@
+// This file (LoadableResourceObject.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2012 IBBoard
+//
+// The file and the library/program it is in are licensed and distributed, without warranty, under the GNU Affero GPL license, either version 3 of the License or (at your option) any later version. Please see COPYING for more information and the full license.
+using System;
+using System.Reflection;
+using System.IO;
+
+namespace IBBoard.WarFoundry.API.Loading
+{
+	public class LoadableResourceObject : ILoadableObject
+	{
+		private Assembly assm;
+		private string resID;
+
+		public LoadableResourceObject(Assembly srcAssembly, string resourceID)
+		{
+			assm = srcAssembly;
+			resID = resourceID;
+		}
+		
+		public string Name
+		{
+			get
+			{
+				return String.Format("{0}: {1}", assm.FullName, resID);
+			}
+		}
+		
+		public Stream GetStream()
+		{
+			return assm.GetManifestResourceStream(resID);
+		}
+	}
+}
+
--- a/IBBoard.WarFoundry.API.csproj	Mon Jun 25 21:04:02 2012 +0100
+++ b/IBBoard.WarFoundry.API.csproj	Sat Jul 07 21:01:32 2012 +0100
@@ -33,9 +33,9 @@
     <BootstrapperEnabled>true</BootstrapperEnabled>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
-    <DebugSymbols>true</DebugSymbols>
+    <DebugSymbols>True</DebugSymbols>
     <DebugType>full</DebugType>
-    <Optimize>false</Optimize>
+    <Optimize>False</Optimize>
     <OutputPath>bin\Debug\</OutputPath>
     <DefineConstants>DEBUG;TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
@@ -43,7 +43,7 @@
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
     <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
+    <Optimize>True</Optimize>
     <OutputPath>bin\Release\</OutputPath>
     <DefineConstants>TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
@@ -162,6 +162,12 @@
     <Compile Include="API\Objects\Requirement\RaceRequiresNoMoreThanNUnitsRequirement.cs" />
     <Compile Include="API\Objects\Requirement\Context\AddingContext.cs" />
     <Compile Include="API\Objects\Requirement\Context\AddingToParentContext.cs" />
+    <Compile Include="API\Loading\ILoadableObject.cs" />
+    <Compile Include="API\Loading\LoadableFileObject.cs" />
+    <Compile Include="API\Loading\LoadableResourceObject.cs" />
+    <Compile Include="API\Loading\ILoadableObjectSource.cs" />
+    <Compile Include="API\Loading\LoadableObjectSourceDirectory.cs" />
+    <Compile Include="API\Loading\LoadableObjectSourceResourceSet.cs" />
   </ItemGroup>
   <ItemGroup>
     <Reference Include="System.Xml" />
@@ -197,34 +203,35 @@
   </ProjectExtensions>
   <ItemGroup>
     <Folder Include="API\Objects\Requirement\Context\" />
+    <Folder Include="API\Loading\" />
   </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="schemas\army.xsd">
-      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
+      <Gettext-ScanForTranslations>False</Gettext-ScanForTranslations>
     </EmbeddedResource>
     <EmbeddedResource Include="schemas\race.xsd">
-      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
+      <Gettext-ScanForTranslations>False</Gettext-ScanForTranslations>
     </EmbeddedResource>
     <EmbeddedResource Include="schemas\system.xsd">
-      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
+      <Gettext-ScanForTranslations>False</Gettext-ScanForTranslations>
     </EmbeddedResource>
     <EmbeddedResource Include="schemas\warfoundry-cats.xsd">
-      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
+      <Gettext-ScanForTranslations>False</Gettext-ScanForTranslations>
     </EmbeddedResource>
     <EmbeddedResource Include="schemas\warfoundry-core.xsd">
-      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
+      <Gettext-ScanForTranslations>False</Gettext-ScanForTranslations>
     </EmbeddedResource>
     <EmbeddedResource Include="schemas\xhtml1-strict.dtd">
-      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
+      <Gettext-ScanForTranslations>False</Gettext-ScanForTranslations>
     </EmbeddedResource>
     <EmbeddedResource Include="schemas\xhtml-lat1.ent">
-      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
+      <Gettext-ScanForTranslations>False</Gettext-ScanForTranslations>
     </EmbeddedResource>
     <EmbeddedResource Include="schemas\xhtml-special.ent">
-      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
+      <Gettext-ScanForTranslations>False</Gettext-ScanForTranslations>
     </EmbeddedResource>
     <EmbeddedResource Include="schemas\xhtml-symbol.ent">
-      <Gettext-ScanForTranslations>false</Gettext-ScanForTranslations>
+      <Gettext-ScanForTranslations>False</Gettext-ScanForTranslations>
     </EmbeddedResource>
   </ItemGroup>
 </Project>
\ No newline at end of file