context: #region Header
  // **********
  // ServUO - Docs.cs
  // **********
  #endregion
  
  #region References
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.IO;
  using System.Linq;
  using System.Reflection;
  using System.Text;
  
  using Server.Commands.Generic;
  using Server.Engines.BulkOrders;
  using Server.Items;
  using Server.Network;
  #endregion
  
  namespace Server.Commands
  {
  	public class Docs
  	{
  		public static void Initialize()
  		{
  			CommandSystem.Register("DocGen", AccessLevel.Administrator, DocGen_OnCommand);
  		}
  
  		[Usage("DocGen")]
  		[Description("Generates RunUO documentation.")]
  		private static void DocGen_OnCommand(CommandEventArgs e)
  		{
  			World.Broadcast(0x35, true, "Documentation is being generated, please wait.");
  			Console.WriteLine("Documentation is being generated, please wait.");
  
  			NetState.FlushAll();
  			NetState.Pause();
  
  			var startTime = DateTime.UtcNow;
  
  			var generated = Document();
  
  			var endTime = DateTime.UtcNow;
  
  			NetState.Resume();
  
  			if (generated)
  			{
  				World.Broadcast(
  					0x35,
  					true,
  					"Documentation has been completed. The entire process took {0:F1} seconds.",
  					(endTime - startTime).TotalSeconds);
  				Console.WriteLine("Documentation complete.");
  			}
  			else
  			{
  				World.Broadcast(
  					0x35,
  					true,
  					"Docmentation failed: Documentation directories are locked and in use. Please close all open files and directories and try again.");
  				Console.WriteLine("Documentation failed.");
  			}
  		}
  
  		private class MemberComparer : IComparer
  		{
  			public int Compare(object x, object y)
  			{
  				if (x == y)
  				{
  					return 0;
  				}
  
  				var aCtor = x as ConstructorInfo;
  				var bCtor = y as ConstructorInfo;
  
  				var aProp = x as PropertyInfo;
  				var bProp = y as PropertyInfo;
  
  				var aMethod = x as MethodInfo;
  				var bMethod = y as MethodInfo;
  
  				var aStatic = GetStaticFor(aCtor, aProp, aMethod);
  				var bStatic = GetStaticFor(bCtor, bProp, bMethod);
  
  				if (aStatic && !bStatic)
  				{
  					return -1;
  				}
  
  				if (!aStatic && bStatic)
  				{
  					return 1;
  				}
  
  				var v = 0;
  
  				if (aCtor != null)
  				{
  					if (bCtor == null)
  					{
  						v = -1;
  					}
  				}
  				else if (bCtor != null)
  				{
  					v = 1;
  				}
  				else if (aProp != null)
  				{
  					if (bProp == null)
  					{
  						v = -1;
  					}
  				}
  				else if (bProp != null)
  				{
  					v = 1;
  				}
  
  				if (v == 0)
  				{
  					v = String.Compare(
  						GetNameFrom(aCtor, aProp, aMethod),
  						GetNameFrom(bCtor, bProp, bMethod),
  						StringComparison.Ordinal);
  				}
  
  				if (v == 0 && aCtor != null && bCtor != null)
  				{
  					v = aCtor.GetParameters().Length.CompareTo(bCtor.GetParameters().Length);
  				}
  				else if (v == 0 && aMethod != null && bMethod != null)
  				{
  					v = aMethod.GetParameters().Length.CompareTo(bMethod.GetParameters().Length);
  				}
  
  				return v;
  			}
  
  			private static bool GetStaticFor(ConstructorInfo ctor, PropertyInfo prop, MethodInfo method)
  			{
  				if (ctor != null)
  				{
  					return ctor.IsStatic;
  				}
  
  				if (method != null)
  				{
  					return method.IsStatic;
  				}
  
  				if (prop != null)
  				{
  					var getMethod = prop.GetGetMethod();
  					var setMethod = prop.GetGetMethod();
  
  					return (getMethod != null && getMethod.IsStatic) || (setMethod != null && setMethod.IsStatic);
  				}
  
  				return false;
  			}
  
  			private static string GetNameFrom(ConstructorInfo ctor, PropertyInfo prop, MethodInfo method)
  			{
  				if (ctor != null)
  				{
  					if (ctor.DeclaringType != null)
  					{
  						return ctor.DeclaringType.Name;
  					}
  
  					return ctor.Name;
  				}
  
  				if (prop != null)
  				{
  					return prop.Name;
  				}
  
  				if (method != null)
  				{
  					return method.Name;
  				}
  
  				return "";
  			}
  		}
  
  		private class TypeComparer : IComparer<TypeInfo>
  		{
  			public int Compare(TypeInfo x, TypeInfo y)
  			{
  				if (x == null && y == null)
  				{
  					return 0;
  				}
  
  				if (x == null)
  				{
  					return -1;
  				}
  
  				if (y == null)
  				{
  					return 1;
  				}
  
  				return String.Compare(x.TypeName, y.TypeName, StringComparison.Ordinal);
  			}
  		}
  
  		private class TypeInfo
  		{
  			public readonly Type m_Type;
  			public readonly Type m_BaseType;
  			public readonly Type m_Declaring;
  			public List<TypeInfo> m_Derived, m_Nested;
  			public readonly Type[] m_Interfaces;
  			private readonly string m_FileName;
  			private readonly string m_TypeName;
  			private readonly string m_LinkName;
  
  			public TypeInfo(Type type)
  			{
  				m_Type = type;
  
  				m_BaseType = type.BaseType;
  				m_Declaring = type.DeclaringType;
  				m_Interfaces = type.GetInterfaces();
  
  				FormatGeneric(m_Type, out m_TypeName, out m_FileName, out m_LinkName);
  
  				//				Console.WriteLine( ">> inline typeinfo: "+m_TypeName );
  				//				m_TypeName = GetGenericTypeName( m_Type );
  				//				m_FileName = Docs.GetFileName( "docs/types/", GetGenericTypeName( m_Type, "-", "-" ), ".html" );
  				//				m_Writer = Docs.GetWriter( "docs/types/", m_FileName );
  			}
  
  			public string FileName { get { return m_FileName; } }
  			public string TypeName { get { return m_TypeName; } }
  
  			public string LinkName(string dirRoot)
  			{
  				return m_LinkName.Replace("@directory@", dirRoot);
  			}
  		}
  
  		#region FileSystem
  		private static readonly char[] ReplaceChars = "<>".ToCharArray();
  
  		public static string GetFileName(string root, string name, string ext)
  		{
  			if (name.IndexOfAny(ReplaceChars) >= 0)
  			{
  				var sb = new StringBuilder(name);
  
  				foreach (var c in ReplaceChars)
  				{
  					sb.Replace(c, '-');
  				}
  
  				name = sb.ToString();
  			}
  
  			var index = 0;
  			var file = String.Concat(name, ext);
  
  			while (File.Exists(Path.Combine(root, file)))
  			{
  				file = String.Concat(name, ++index, ext);
  			}
  
  			return file;
  		}
  
  		private static readonly string m_RootDirectory = Path.GetDirectoryName(Environment.GetCommandLineArgs()[0]);
  
  		private static void EnsureDirectory(string path)
  		{
  			path = Path.Combine(m_RootDirectory, path);
  
  			if (!Directory.Exists(path))
  			{
  				Directory.CreateDirectory(path);
  			}
  		}
  
  		private static void DeleteDirectory(string path)
  		{
  			path = Path.Combine(m_RootDirectory, path);
  
  			if (Directory.Exists(path))
  			{
  				Directory.Delete(path, true);
  			}
  		}
  
  		private static StreamWriter GetWriter(string root, string name)
  		{
  			return new StreamWriter(Path.Combine(Path.Combine(m_RootDirectory, root), name));
  		}
  
  		private static StreamWriter GetWriter(string path)
  		{
  			return new StreamWriter(Path.Combine(m_RootDirectory, path));
  		}
  		#endregion
  
  		#region GetPair
  		private static readonly string[,] m_Aliases =
  		{
  			{"System.Object", "<span style=\"color: blue;\">object</span>"},
  			{"System.String", "<span style=\"color: blue;\">string</span>"},
  			{"System.Boolean", "<span style=\"color: blue;\">bool</span>"},
  			{"System.Byte", "<span style=\"color: blue;\">byte</span>"},
  			{"System.SByte", "<span style=\"color: blue;\">sbyte</span>"},
  			{"System.Int16", "<span style=\"color: blue;\">short</span>"},
  			{"System.UInt16", "<span style=\"color: blue;\">ushort</span>"},
  			{"System.Int32", "<span style=\"color: blue;\">int</span>"},
  			{"System.UInt32", "<span style=\"color: blue;\">uint</span>"},
  			{"System.Int64", "<span style=\"color: blue;\">long</span>"},
  			{"System.UInt64", "<span style=\"color: blue;\">ulong</span>"},
  			{"System.Single", "<span style=\"color: blue;\">float</span>"},
  			{"System.Double", "<span style=\"color: blue;\">double</span>"},
  			{"System.Decimal", "<span style=\"color: blue;\">decimal</span>"},
  			{"System.Char", "<span style=\"color: blue;\">char</span>"},
  			{"System.Void", "<span style=\"color: blue;\">void</span>"}
  		};
  
  		private static readonly int m_AliasLength = m_Aliases.GetLength(0);
  
  		public static string GetPair(Type varType, string name, bool ignoreRef)
  		{
  			var prepend = "";
  			var append = new StringBuilder();
  
  			var realType = varType;
  
  			if (varType.IsByRef)
  			{
  				if (!ignoreRef)
  				{
  					prepend = RefString;
  				}
  
  				realType = varType.GetElementType();
  			}
  
  			if (realType.IsPointer)
  			{
  				if (realType.IsArray)
  				{
  					append.Append('*');
  
  					do
  					{
  						append.Append('[');
  
  						for (var i = 1; i < realType.GetArrayRank(); ++i)
  						{
  							append.Append(',');
  						}
  
  						append.Append(']');
  
  						realType = realType.GetElementType();
  					}
  					while (realType.IsArray);
  
  					append.Append(' ');
  				}
  				else
  				{
  					realType = realType.GetElementType();
  					append.Append(" *");
  				}
  			}
  			else if (realType.IsArray)
  			{
  				do
  				{
  					append.Append('[');
  
  					for (var i = 1; i < realType.GetArrayRank(); ++i)
  					{
  						append.Append(',');
  					}
  
  					append.Append(']');
  
  					realType = realType.GetElementType();
  				}
  				while (realType.IsArray);
  
  				append.Append(' ');
  			}
  			else
  			{
  				append.Append(' ');
  			}
  
  			var fullName = realType.FullName;
  			string aliased = null; // = realType.Name;
  
  			TypeInfo info = null;
  			m_Types.TryGetValue(realType, out info);
  
  			if (info != null)
  			{
  				aliased = "<!-- DBG-0 -->" + info.LinkName(null);
  				//aliased = String.Format( "<a href=\"{0}\">{1}</a>", info.m_FileName, info.m_TypeName );
  			}
  			else
  			{
  				//FormatGeneric( );
  				if (realType.IsGenericType)
  				{
  					string typeName, fileName, linkName;
  					FormatGeneric(realType, out typeName, out fileName, out linkName);
  					linkName = linkName.Replace("@directory@", null);
  					aliased = linkName;
  				}
  				else
  				{
  					for (var i = 0; i < m_AliasLength; ++i)
  					{
  						if (m_Aliases[i, 0] == fullName)
  						{
  							aliased = m_Aliases[i, 1];
  							break;
  						}
  					}
  				}
  
  				if (aliased == null)
  				{
  					aliased = realType.Name;
  				}
  			}
  
  			var retval = String.Concat(prepend, aliased, append, name);
  			//Console.WriteLine(">> getpair: "+retval);
  			return retval;
  		}
  		#endregion
  
  		private static Dictionary<Type, TypeInfo> m_Types;
  		private static Dictionary<string, List<TypeInfo>> m_Namespaces;
  
  		#region Root documentation
  		private static bool Document()
  		{
  			try
  			{
  				DeleteDirectory("docs/");
  			}
  			catch
  			{
  				return false;
  			}
  
  			EnsureDirectory("docs/");
  			EnsureDirectory("docs/namespaces/");
  			EnsureDirectory("docs/types/");
  			EnsureDirectory("docs/bods/");
  
  			GenerateStyles();
  			GenerateIndex();
  
  			DocumentCommands();
  			DocumentKeywords();
  			DocumentBodies();
  
  			DocumentBulkOrders();
  
  			m_Types = new Dictionary<Type, TypeInfo>();
  			m_Namespaces = new Dictionary<string, List<TypeInfo>>();
  
  			var assemblies = new List<Assembly>
  			{
  				Core.Assembly
  			};
  
  			assemblies.AddRange(ScriptCompiler.Assemblies);
  
  			var asms = assemblies.ToArray();
  
  			foreach (var a in asms)
  			{
  				LoadTypes(a, asms);
  			}
  
  			DocumentLoadedTypes();
  			DocumentConstructableObjects();
  
  			return true;
  		}
  
  		private static void AddIndexLink(StreamWriter html, string filePath, string label, string desc)
  		{
  			html.WriteLine("      <h2><a href=\"{0}\" title=\"{1}\">{2}</a></h2>", filePath, desc, label);
  		}
  
  		private static void GenerateStyles()
  		{
  			using (var css = GetWriter("docs/", "styles.css"))
  			{
  				css.WriteLine("body { background-color: #FFFFFF; font-family: verdana, arial; font-size: 11px; }");
  				css.WriteLine("a { color: #28435E; }");
  				css.WriteLine("a:hover { color: #4878A9; }");
  				css.WriteLine("td.header { background-color: #9696AA; font-weight: bold; font-size: 12px; }");
  				css.WriteLine("td.lentry { background-color: #D7D7EB; width: 10%; }");
  				css.WriteLine("td.rentry { background-color: #FFFFFF; width: 90%; }");
  				css.WriteLine("td.entry { background-color: #FFFFFF; }");
  				css.WriteLine("td { font-size: 11px; }");
  				css.WriteLine(".tbl-border { background-color: #46465A; }");
  
  				css.WriteLine("td.ir {{ background-color: #{0:X6}; }}", Iron);
  				css.WriteLine("td.du {{ background-color: #{0:X6}; }}", DullCopper);
  				css.WriteLine("td.sh {{ background-color: #{0:X6}; }}", ShadowIron);
  				css.WriteLine("td.co {{ background-color: #{0:X6}; }}", Copper);
  				css.WriteLine("td.br {{ background-color: #{0:X6}; }}", Bronze);
  				css.WriteLine("td.go {{ background-color: #{0:X6}; }}", Gold);
  				css.WriteLine("td.ag {{ background-color: #{0:X6}; }}", Agapite);
  				css.WriteLine("td.ve {{ background-color: #{0:X6}; }}", Verite);
  				css.WriteLine("td.va {{ background-color: #{0:X6}; }}", Valorite);
  
  				css.WriteLine("td.cl {{ background-color: #{0:X6}; }}", Cloth);
  				css.WriteLine("td.pl {{ background-color: #{0:X6};  }}", Plain);
  				css.WriteLine("td.sp {{ background-color: #{0:X6}; }}", Core.AOS ? SpinedAOS : SpinedLBR);
  				css.WriteLine("td.ho {{ background-color: #{0:X6}; }}", Core.AOS ? HornedAOS : HornedLBR);
  				css.WriteLine("td.ba {{ background-color: #{0:X6}; }}", Core.AOS ? BarbedAOS : BarbedLBR);
  			}
  		}
  
  		private static void GenerateIndex()
  		{
  			using (var html = GetWriter("docs/", "index.html"))
  			{
  				html.WriteLine("<!DOCTYPE html>");
  				html.WriteLine("<html>");
  				html.WriteLine("   <head>");
  				html.WriteLine("      <title>RunUO Documentation - Index</title>");
  				html.WriteLine("      <style type=\"text/css\">");
  				html.WriteLine("      body { background-color: white; font-family: Tahoma; color: #000000; }");
  				html.WriteLine("      a, a:visited { color: #000000; }");
  				html.WriteLine("      a:active, a:hover { color: #808080; }");
  				html.WriteLine("      </style>");
  				html.WriteLine("      <link rel=\"stylesheet\" type=\"text/css\" href=\"styles.css\" />");
  				html.WriteLine("   </head>");
  				html.WriteLine("   <body>");
  
  				AddIndexLink(
  					html,
  					"commands.html",
  					"Commands",
  					"Every available command. This contains command name, usage, aliases, and description.");
  				AddIndexLink(
  					html,
  					"objects.html",
  					"Constructable Objects",
  					"Every constructable item or npc. This contains object name and usage. Hover mouse over parameters to see type description.");
  				AddIndexLink(
  					html,
  					"keywords.html",
  					"Speech Keywords",
  					"Lists speech keyword numbers and associated match patterns. These are used in some scripts for multi-language matching of client speech.");
  				AddIndexLink(
  					html,
  					"bodies.html",
  					"Body List",
  					"Every usable body number and name. Table is generated from a UO:3D client datafile. If you do not have UO:3D installed, this may be blank.");
  				AddIndexLink(
  					html,
  					"overview.html",
  					"Class Overview",
  					"Scripting reference. Contains every class type and contained methods in the core and scripts.");
  				AddIndexLink(
  					html,
  					"bods/bod_smith_rewards.html",
  					"Bulk Order Rewards: Smithing",
  					"Reference table for large and small smithing bulk order deed rewards.");
  				AddIndexLink(
  					html,
  					"bods/bod_tailor_rewards.html",
  					"Bulk Order Rewards: Tailoring",
  					"Reference table for large and small tailoring bulk order deed rewards.");
  
  				html.WriteLine("   </body>");
  				html.WriteLine("</html>");
  			}
  		}
  		#endregion
  
  		#region BODs
  		private const int Iron = 0xCCCCDD;
  		private const int DullCopper = 0xAAAAAA;
  		private const int ShadowIron = 0x777799;
  		private const int Copper = 0xDDCC99;
  		private const int Bronze = 0xAA8866;
  		private const int Gold = 0xDDCC55;
  		private const int Agapite = 0xDDAAAA;
  		private const int Verite = 0x99CC77;
  		private const int Valorite = 0x88AABB;
  
  		private const int Cloth = 0xDDDDDD;
  		private const int Plain = 0xCCAA88;
  		private const int SpinedAOS = 0x99BBBB;
  		private const int HornedAOS = 0xCC8888;
  		private const int BarbedAOS = 0xAABBAA;
  		private const int SpinedLBR = 0xAA8833;
  		private const int HornedLBR = 0xBBBBAA;
  		private const int BarbedLBR = 0xCCAA88;
  
  		private static void DocumentBulkOrders()
  		{
  			using (var html = GetWriter("docs/bods/", "bod_smith_rewards.html"))
  			{
  				html.WriteLine("<!DOCTYPE html>");
  				html.WriteLine("<html>");
  				html.WriteLine("   <head>");
  				html.WriteLine("      <title>RunUO Documentation - Bulk Orders - Smith Rewards</title>");
  				html.WriteLine("      <style type=\"text/css\">");
  				html.WriteLine("      body { background-color: white; font-family: Tahoma; color: #000000; }");
  				html.WriteLine("      a, a:visited { color: #000000; }");
  				html.WriteLine("      a:active, a:hover { color: #808080; }");
  				html.WriteLine("      </style>");
  				html.WriteLine("      <link rel=\"stylesheet\" type=\"text/css\" href=\"../styles.css\" />");
  				html.WriteLine("   </head>");
  				html.WriteLine("   <body>");
  
  				SmallBOD sbod = new SmallSmithBOD();
  
  				sbod.Type = typeof(Katana);
  				sbod.Material = BulkMaterialType.None;
  				sbod.AmountMax = 10;
  
  				WriteSmithBODHeader(html, "(Small) Weapons");
  				sbod.RequireExceptional = false;
  				DocumentSmithBOD(html, sbod.ComputeRewards(true), "10, 15, 20: Normal", sbod.Material);
  				sbod.RequireExceptional = true;
  				DocumentSmithBOD(html, sbod.ComputeRewards(true), "10, 15, 20: Exceptional", sbod.Material);
  				WriteSmithBODFooter(html);
  
  				html.WriteLine("      <br /><br />");
  				html.WriteLine("      <br /><br />");
  
  				sbod.Type = typeof(PlateArms);
  
  				WriteSmithBODHeader(html, "(Small) Armor: Normal");
  
  				sbod.RequireExceptional = false;
  				for (var mat = BulkMaterialType.None; mat <= BulkMaterialType.Valorite; ++mat)
  				{
  					sbod.Material = mat;
  					sbod.AmountMax = 10;
  					DocumentSmithBOD(html, sbod.ComputeRewards(true), "10, 15, 20", sbod.Material);
  				}
  
  				WriteSmithBODFooter(html);
  
  				html.WriteLine("      <br /><br />");
  
  				WriteSmithBODHeader(html, "(Small) Armor: Exceptional");
  
  				sbod.RequireExceptional = true;
  				for (var mat = BulkMaterialType.None; mat <= BulkMaterialType.Valorite; ++mat)
  				{
  					sbod.Material = mat;
  
  					for (var amt = 15; amt <= 20; amt += 5)
  					{
  						sbod.AmountMax = amt;
  						DocumentSmithBOD(html, sbod.ComputeRewards(true), amt == 20 ? "20" : "10, 15", sbod.Material);
  					}
  				}
  
  				WriteSmithBODFooter(html);
  
  				html.WriteLine("      <br /><br />");
  				html.WriteLine("      <br /><br />");
  
  				sbod.Delete();
  
  				WriteSmithLBOD(html, "Ringmail", LargeBulkEntry.LargeRing);
  				WriteSmithLBOD(html, "Chainmail", LargeBulkEntry.LargeChain);
  				WriteSmithLBOD(html, "Platemail", LargeBulkEntry.LargePlate);
  
  				html.WriteLine("   </body>");
  				html.WriteLine("</html>");
  			}
  
  			using (var html = GetWriter("docs/bods/", "bod_tailor_rewards.html"))
  			{
  				html.WriteLine("<!DOCTYPE html>");
  				html.WriteLine("<html>");
  				html.WriteLine("   <head>");
  				html.WriteLine("      <title>RunUO Documentation - Bulk Orders - Tailor Rewards</title>");
  				html.WriteLine("      <style type=\"text/css\">");
  				html.WriteLine("      body { background-color: white; font-family: Tahoma; color: #000000; }");
  				html.WriteLine("      a, a:visited { color: #000000; }");
  				html.WriteLine("      a:active, a:hover { color: #808080; }");
  				html.WriteLine("      </style>");
  				html.WriteLine("      <link rel=\"stylesheet\" type=\"text/css\" href=\"../styles.css\" />");
  				html.WriteLine("   </head>");
  				html.WriteLine("   <body>");
  
  				SmallBOD sbod = new SmallTailorBOD();
  
  				WriteTailorBODHeader(html, "Small Bulk Order");
  
  				html.WriteLine("         <tr>");
  				html.WriteLine("            <td style=\"width: 850px;\" colspan=\"21\" class=\"entry\"><b>Regular: 10, 15</b></td>");
  				html.WriteLine("         </tr>");
  
  				sbod.AmountMax = 10;
  				sbod.RequireExceptional = false;
  
  				sbod.Type = typeof(SkullCap);
  				sbod.Material = BulkMaterialType.None;
  				DocumentTailorBOD(html, sbod.ComputeRewards(true), "10, 15", sbod.Material, sbod.Type);
  
  				sbod.Type = typeof(LeatherCap);
  				for (var mat = BulkMaterialType.None; mat <= BulkMaterialType.Barbed; ++mat)
  				{
  					if (mat >= BulkMaterialType.DullCopper && mat <= BulkMaterialType.Valorite)
  					{
  						continue;
  					}
  
  					sbod.Material = mat;
  					DocumentTailorBOD(html, sbod.ComputeRewards(true), "10, 15", sbod.Material, sbod.Type);
  				}
  
  				html.WriteLine("         <tr>");
  				html.WriteLine("            <td style=\"width: 850px;\" colspan=\"21\" class=\"entry\"><b>Regular: 20</b></td>");
  				html.WriteLine("         </tr>");
  
  				sbod.AmountMax = 20;
  				sbod.RequireExceptional = false;
  
  				sbod.Type = typeof(SkullCap);
  				sbod.Material = BulkMaterialType.None;
  				DocumentTailorBOD(html, sbod.ComputeRewards(true), "20", sbod.Material, sbod.Type);
  
  				sbod.Type = typeof(LeatherCap);
  				for (var mat = BulkMaterialType.None; mat <= BulkMaterialType.Barbed; ++mat)
  				{
  					if (mat >= BulkMaterialType.DullCopper && mat <= BulkMaterialType.Valorite)
  					{
  						continue;
  					}
  
  					sbod.Material = mat;
  					DocumentTailorBOD(html, sbod.ComputeRewards(true), "20", sbod.Material, sbod.Type);
  				}
  
  				html.WriteLine("         <tr>");
  				html.WriteLine(
  					"            <td style=\"width: 850px;\" colspan=\"21\" class=\"entry\"><b>Exceptional: 10, 15</b></td>");
  				html.WriteLine("         </tr>");
  
  				sbod.AmountMax = 10;
  				sbod.RequireExceptional = true;
  
  				sbod.Type = typeof(SkullCap);
  				sbod.Material = BulkMaterialType.None;
  				DocumentTailorBOD(html, sbod.ComputeRewards(true), "10, 15", sbod.Material, sbod.Type);
  
  				sbod.Type = typeof(LeatherCap);
  				for (var mat = BulkMaterialType.None; mat <= BulkMaterialType.Barbed; ++mat)
  				{
  					if (mat >= BulkMaterialType.DullCopper && mat <= BulkMaterialType.Valorite)
  					{
  						continue;
  					}
  
  					sbod.Material = mat;
  					DocumentTailorBOD(html, sbod.ComputeRewards(true), "10, 15", sbod.Material, sbod.Type);
  				}
  
  				html.WriteLine("         <tr>");
  				html.WriteLine("            <td style=\"width: 850px;\" colspan=\"21\" class=\"entry\"><b>Exceptional: 20</b></td>");
  				html.WriteLine("         </tr>");
  
  				sbod.AmountMax = 20;
  				sbod.RequireExceptional = true;
  
  				sbod.Type = typeof(SkullCap);
  				sbod.Material = BulkMaterialType.None;
  				DocumentTailorBOD(html, sbod.ComputeRewards(true), "20", sbod.Material, sbod.Type);
  
  				sbod.Type = typeof(LeatherCap);
  				for (var mat = BulkMaterialType.None; mat <= BulkMaterialType.Barbed; ++mat)
  				{
  					if (mat >= BulkMaterialType.DullCopper && mat <= BulkMaterialType.Valorite)
  					{
  						continue;
  					}
  
  					sbod.Material = mat;
  					DocumentTailorBOD(html, sbod.ComputeRewards(true), "20", sbod.Material, sbod.Type);
  				}
  
  				WriteTailorBODFooter(html);
  
  				html.WriteLine("      <br /><br />");
  				html.WriteLine("      <br /><br />");
  
  				sbod.Delete();
  
  				WriteTailorLBOD(html, "Large Bulk Order: 4-part", LargeBulkEntry.Gypsy, true, true);
  				WriteTailorLBOD(html, "Large Bulk Order: 5-part", LargeBulkEntry.TownCrier, true, true);
  				WriteTailorLBOD(html, "Large Bulk Order: 6-part", LargeBulkEntry.MaleLeatherSet, false, true);
  
  				html.WriteLine("   </body>");
  				html.WriteLine("</html>");
  			}
  		}
  
  		#region Tailor Bods
  		private static void WriteTailorLBOD(
  			StreamWriter html,
  			string name,
  			SmallBulkEntry[] entries,
  			bool expandCloth,
  			bool expandPlain)
  		{
  			WriteTailorBODHeader(html, name);
  
  			LargeBOD lbod = new LargeTailorBOD();
  
  			lbod.Entries = LargeBulkEntry.ConvertEntries(lbod, entries);
  
  			var type = entries[0].Type;
  
  			var showCloth = !(type.IsSubclassOf(typeof(BaseArmor)) || type.IsSubclassOf(typeof(BaseShoes)));
  
  			html.WriteLine("         <tr>");
  			html.WriteLine("            <td style=\"width: 850px;\" colspan=\"21\" class=\"entry\"><b>Regular</b></td>");
  			html.WriteLine("         </tr>");
  
  			lbod.RequireExceptional = false;
  			lbod.AmountMax = 10;
  
  			if (showCloth)
  			{
  				lbod.Material = BulkMaterialType.None;
  
  				if (expandCloth)
  				{
  					lbod.AmountMax = 10;
  					DocumentTailorBOD(html, lbod.ComputeRewards(true), "10, 15", lbod.Material, type);
  					lbod.AmountMax = 20;
  					DocumentTailorBOD(html, lbod.ComputeRewards(true), "20", lbod.Material, type);
  				}
  				else
  				{
  					lbod.AmountMax = 10;
  					DocumentTailorBOD(html, lbod.ComputeRewards(true), "10, 15, 20", lbod.Material, type);
  				}
  			}
  
  			lbod.Material = BulkMaterialType.None;
  
  			if (expandPlain)
  			{
  				lbod.AmountMax = 10;
  				DocumentTailorBOD(html, lbod.ComputeRewards(true), "10, 15, 20", lbod.Material, typeof(LeatherCap));
  				lbod.AmountMax = 20;
  				DocumentTailorBOD(html, lbod.ComputeRewards(true), "20", lbod.Material, typeof(LeatherCap));
  			}
  			else
  			{
  				lbod.AmountMax = 10;
  				DocumentTailorBOD(html, lbod.ComputeRewards(true), "10, 15, 20", lbod.Material, typeof(LeatherCap));
  			}
  
  			for (var mat = BulkMaterialType.Spined; mat <= BulkMaterialType.Barbed; ++mat)
  			{
  				lbod.Material = mat;
  				lbod.AmountMax = 10;
  				DocumentTailorBOD(html, lbod.ComputeRewards(true), "10, 15", lbod.Material, type);
  				lbod.AmountMax = 20;
  				DocumentTailorBOD(html, lbod.ComputeRewards(true), "20", lbod.Material, type);
  			}
  
  			html.WriteLine("         <tr>");
  			html.WriteLine("            <td style=\"width: 850px;\" colspan=\"21\" class=\"entry\"><b>Exceptional</b></td>");
  			html.WriteLine("         </tr>");
  
  			lbod.RequireExceptional = true;
  			lbod.AmountMax = 10;
  
  			if (showCloth)
  			{
  				lbod.Material = BulkMaterialType.None;
  
  				if (expandCloth)
  				{
  					lbod.AmountMax = 10;
  					DocumentTailorBOD(html, lbod.ComputeRewards(true), "10, 15", lbod.Material, type);
  					lbod.AmountMax = 20;
  					DocumentTailorBOD(html, lbod.ComputeRewards(true), "20", lbod.Material, type);
  				}
  				else
  				{
  					lbod.AmountMax = 10;
  					DocumentTailorBOD(html, lbod.ComputeRewards(true), "10, 15, 20", lbod.Material, type);
  				}
  			}
  
  			lbod.Material = BulkMaterialType.None;
  
  			if (expandPlain)
  			{
  				lbod.AmountMax = 10;
  				DocumentTailorBOD(html, lbod.ComputeRewards(true), "10, 15, 20", lbod.Material, typeof(LeatherCap));
  				lbod.AmountMax = 20;
  				DocumentTailorBOD(html, lbod.ComputeRewards(true), "20", lbod.Material, typeof(LeatherCap));
  			}
  			else
  			{
  				lbod.AmountMax = 10;
  				DocumentTailorBOD(html, lbod.ComputeRewards(true), "10, 15, 20", lbod.Material, typeof(LeatherCap));
  			}
  
  			for (var mat = BulkMaterialType.Spined; mat <= BulkMaterialType.Barbed; ++mat)
  			{
  				lbod.Material = mat;
  				lbod.AmountMax = 10;
  				DocumentTailorBOD(html, lbod.ComputeRewards(true), "10, 15", lbod.Material, type);
  				lbod.AmountMax = 20;
  				DocumentTailorBOD(html, lbod.ComputeRewards(true), "20", lbod.Material, type);
  			}
  
  			WriteTailorBODFooter(html);
  
  			html.WriteLine("      <br /><br />");
  			html.WriteLine("      <br /><br />");
  		}
  
  		private static void WriteTailorBODHeader(StreamWriter html, string title)
  		{
  			html.WriteLine("      <table style=\"width: 850px;\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\">");
  			html.WriteLine("      <tr><td class=\"tbl-border\">");
  			html.WriteLine("      <table border=\"0\" style=\"width: 850px;\" cellpadding=\"0\" cellspacing=\"1\">");
  			html.WriteLine("         <tr>");
  			html.WriteLine(
  				"            <td style=\"width: 250px;\" rowspan=\"2\" class=\"entry\"><center>{0}</center></td>",
  				title);
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><a href=\"http://www.runuo.com/images/bodreward_cloth_full.jpg\"><img src=\"http://www.runuo.com/images/bodreward_cloth_1.jpg\" alt=\"Colored Cloth (Level 1)\" border=\"0\"></a></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><a href=\"http://www.runuo.com/images/bodreward_cloth_full.jpg\"><img src=\"http://www.runuo.com/images/bodreward_cloth_2.jpg\" alt=\"Colored Cloth (Level 2)\" border=\"0\"></a></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><a href=\"http://www.runuo.com/images/bodreward_cloth_full.jpg\"><img src=\"http://www.runuo.com/images/bodreward_cloth_3.jpg\" alt=\"Colored Cloth (Level 3)\" border=\"0\"></a></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><a href=\"http://www.runuo.com/images/bodreward_cloth_full.jpg\"><img src=\"http://www.runuo.com/images/bodreward_cloth_4.jpg\" alt=\"Colored Cloth (Level 4)\" border=\"0\"></a></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><a href=\"http://www.runuo.com/images/bodreward_cloth_full.jpg\"><img src=\"http://www.runuo.com/images/bodreward_cloth_5.jpg\" alt=\"Colored Cloth (Level 5)\" border=\"0\"></a></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><a href=\"http://www.runuo.com/images/bodreward_sandals_full.jpg\"><img src=\"http://www.runuo.com/images/bodreward_sandals.jpg\" alt=\"Colored Sandals\" border=\"0\"></a></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 100px;\" colspan=\"4\" class=\"entry\"><center>Power Scrolls</center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_smallhides.jpg\" alt=\"Small Stretched Hide\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_mediumhides.jpg\" alt=\"Medium Stretched Hide\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_lighttapestry.jpg\" alt=\"Light Flower Tapestry\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_darktapestry.jpg\" alt=\"Dark Flower Tapestry\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_brownbearrug.jpg\" alt=\"Brown Bear Rug\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_polarbearrug.jpg\" alt=\"Polar Bear Rug\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_clothingbless.jpg\" alt=\"Clothing Bless Deed\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 75px;\" colspan=\"3\" class=\"entry\"><center>Runic Kits</center></td>");
  			html.WriteLine("         </tr>");
  			html.WriteLine("         <tr>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+5</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+10</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+15</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+20</small></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_runic_spined.jpg\" alt=\"Runic Sewing Kit: Spined\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_runic_horned.jpg\" alt=\"Runic Sewing Kit: Horned\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_runic_barbed.jpg\" alt=\"Runic Sewing Kit: Barbed\"></center></td>");
  			html.WriteLine("         </tr>");
  		}
  
  		private static void WriteTailorBODFooter(StreamWriter html)
  		{
  			html.WriteLine("         <tr>");
  			html.WriteLine("            <td style=\"width: 250px;\" rowspan=\"2\" class=\"entry\">&nbsp;</td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><a href=\"http://www.runuo.com/images/bodreward_cloth_full.jpg\"><img src=\"http://www.runuo.com/images/bodreward_cloth_1.jpg\" alt=\"Colored Cloth (Level 1)\" border=\"0\"></a></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><a href=\"http://www.runuo.com/images/bodreward_cloth_full.jpg\"><img src=\"http://www.runuo.com/images/bodreward_cloth_2.jpg\" alt=\"Colored Cloth (Level 2)\" border=\"0\"></a></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><a href=\"http://www.runuo.com/images/bodreward_cloth_full.jpg\"><img src=\"http://www.runuo.com/images/bodreward_cloth_3.jpg\" alt=\"Colored Cloth (Level 3)\" border=\"0\"></a></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><a href=\"http://www.runuo.com/images/bodreward_cloth_full.jpg\"><img src=\"http://www.runuo.com/images/bodreward_cloth_4.jpg\" alt=\"Colored Cloth (Level 4)\" border=\"0\"></a></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><a href=\"http://www.runuo.com/images/bodreward_cloth_full.jpg\"><img src=\"http://www.runuo.com/images/bodreward_cloth_5.jpg\" alt=\"Colored Cloth (Level 5)\" border=\"0\"></a></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><a href=\"http://www.runuo.com/images/bodreward_sandals_full.jpg\"><img src=\"http://www.runuo.com/images/bodreward_sandals.jpg\" alt=\"Colored Sandals\" border=\"0\"></a></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+5</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+10</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+15</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+20</small></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_smallhides.jpg\" alt=\"Small Stretched Hide\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_mediumhides.jpg\" alt=\"Medium Stretched Hide\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_lighttapestry.jpg\" alt=\"Light Flower Tapestry\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_darktapestry.jpg\" alt=\"Dark Flower Tapestry\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_brownbearrug.jpg\" alt=\"Brown Bear Rug\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_polarbearrug.jpg\" alt=\"Polar Bear Rug\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_clothingbless.jpg\" alt=\"Clothing Bless Deed\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_runic_spined.jpg\" alt=\"Runic Sewing Kit: Spined\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_runic_horned.jpg\" alt=\"Runic Sewing Kit: Horned\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_runic_barbed.jpg\" alt=\"Runic Sewing Kit: Barbed\"></center></td>");
  			html.WriteLine("         </tr>");
  			html.WriteLine("         <tr>");
  			html.WriteLine(
  				"            <td style=\"width: 100px;\" colspan=\"4\" class=\"entry\"><center>Power Scrolls</center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 75px;\" colspan=\"3\" class=\"entry\"><center>Runic Kits</center></td>");
  			html.WriteLine("         </tr>");
  			html.WriteLine("      </table></td></tr></table>");
  		}
  
  		private static void DocumentTailorBOD(
  			StreamWriter html,
  			IEnumerable<Item> items,
  			string amt,
  			BulkMaterialType material,
  			Type type)
  		{
  			var rewards = new bool[20];
  
  			foreach (var item in items)
  			{
  				if (item is Sandals)
  				{
  					rewards[5] = true;
  				}
  				else if (item is SmallStretchedHideEastDeed || item is SmallStretchedHideSouthDeed)
  				{
  					rewards[10] = rewards[11] = true;
  				}
  				else if (item is MediumStretchedHideEastDeed || item is MediumStretchedHideSouthDeed)
  				{
  					rewards[10] = rewards[11] = true;
  				}
  				else if (item is LightFlowerTapestryEastDeed || item is LightFlowerTapestrySouthDeed)
  				{
  					rewards[12] = rewards[13] = true;
  				}
  				else if (item is DarkFlowerTapestryEastDeed || item is DarkFlowerTapestrySouthDeed)
  				{
  					rewards[12] = rewards[13] = true;
  				}
  				else if (item is BrownBearRugEastDeed || item is BrownBearRugSouthDeed)
  				{
  					rewards[14] = rewards[15] = true;
  				}
  				else if (item is PolarBearRugEastDeed || item is PolarBearRugSouthDeed)
  				{
  					rewards[14] = rewards[15] = true;
  				}
  				else if (item is ClothingBlessDeed)
  				{
  					rewards[16] = true;
  				}
  				else if (item is PowerScroll)
  				{
  					var ps = (PowerScroll)item;
  
  					if (ps.Value == 105.0)
  					{
  						rewards[6] = true;
  					}
  					else if (ps.Value == 110.0)
  					{
  						rewards[7] = true;
  					}
  					else if (ps.Value == 115.0)
  					{
  						rewards[8] = true;
  					}
  					else if (ps.Value == 120.0)
  					{
  						rewards[9] = true;
  					}
  				}
  				else if (item is UncutCloth)
  				{
  					if (item.Hue == 0x483 || item.Hue == 0x48C || item.Hue == 0x488 || item.Hue == 0x48A)
  					{
  						rewards[0] = true;
  					}
  					else if (item.Hue == 0x495 || item.Hue == 0x48B || item.Hue == 0x486 || item.Hue == 0x485)
  					{
  						rewards[1] = true;
  					}
  					else if (item.Hue == 0x48D || item.Hue == 0x490 || item.Hue == 0x48E || item.Hue == 0x491)
  					{
  						rewards[2] = true;
  					}
  					else if (item.Hue == 0x48F || item.Hue == 0x494 || item.Hue == 0x484 || item.Hue == 0x497)
  					{
  						rewards[3] = true;
  					}
  					else
  					{
  						rewards[4] = true;
  					}
  				}
  				item.Delete();
  			}
  
  			string style = null;
  			string name = null;
  
  			switch (material)
  			{
  				case BulkMaterialType.None:
  				{
  					if (type.IsSubclassOf(typeof(BaseArmor)) || type.IsSubclassOf(typeof(BaseShoes)))
  					{
  						style = "pl";
  						name = "Plain";
  					}
  					else
  					{
  						style = "cl";
  						name = "Cloth";
  					}
  
  					break;
  				}
  				case BulkMaterialType.Spined:
  					style = "sp";
  					name = "Spined";
  					break;
  				case BulkMaterialType.Horned:
  					style = "ho";
  					name = "Horned";
  					break;
  				case BulkMaterialType.Barbed:
  					style = "ba";
  					name = "Barbed";
  					break;
  			}
  
  			html.WriteLine("         <tr>");
  			html.WriteLine(
  				"            <td style=\"width: 250px;\" class=\"entry\">&nbsp;- {0} <span style=\"font-size: 1pt;\">{1}</span></td>",
  				name,
  				amt);
  
  			var index = 0;
  
  			while (index < 20)
  			{
  				if (rewards[index])
  				{
  					html.WriteLine("            <td style=\"width: 25px;\" class=\"{0}\"><center><b>X</b></center></td>", style);
  					++index;
  				}
  				else
  				{
  					var count = 0;
  
  					while (index < 20 && !rewards[index])
  					{
  						++count;
  						++index;
  
  						if (index == 5 || index == 6 || index == 10 || index == 17)
  						{
  							break;
  						}
  					}
  
  					html.WriteLine(
  						"            <td width=\"{0}\"{1} class=\"entry\">&nbsp;</td>",
  						count * 25,
  						count == 1 ? "" : String.Format(" colspan=\"{0}\"", count));
  				}
  			}
  
  			html.WriteLine("         </tr>");
  		}
  		#endregion
  
  		#region Smith Bods
  		private static void WriteSmithLBOD(StreamWriter html, string name, SmallBulkEntry[] entries)
  		{
  			LargeBOD lbod = new LargeSmithBOD();
  
  			lbod.Entries = LargeBulkEntry.ConvertEntries(lbod, entries);
  
  			WriteSmithBODHeader(html, String.Format("(Large) {0}: Normal", name));
  
  			lbod.RequireExceptional = false;
  			for (var mat = BulkMaterialType.None; mat <= BulkMaterialType.Valorite; ++mat)
  			{
  				lbod.Material = mat;
  				lbod.AmountMax = 10;
  				DocumentSmithBOD(html, lbod.ComputeRewards(true), "10, 15, 20", lbod.Material);
  			}
  
  			WriteSmithBODFooter(html);
  
  			html.WriteLine("      <br /><br />");
  
  			WriteSmithBODHeader(html, String.Format("(Large) {0}: Exceptional", name));
  
  			lbod.RequireExceptional = true;
  			for (var mat = BulkMaterialType.None; mat <= BulkMaterialType.Valorite; ++mat)
  			{
  				lbod.Material = mat;
  
  				for (var amt = 15; amt <= 20; amt += 5)
  				{
  					lbod.AmountMax = amt;
  					DocumentSmithBOD(html, lbod.ComputeRewards(true), amt == 20 ? "20" : "10, 15", lbod.Material);
  				}
  			}
  
  			WriteSmithBODFooter(html);
  
  			html.WriteLine("      <br /><br />");
  			html.WriteLine("      <br /><br />");
  		}
  
  		private static void WriteSmithBODHeader(StreamWriter html, string title)
  		{
  			html.WriteLine("      <table style=\"width: 850px;\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\">");
  			html.WriteLine("      <tr><td class=\"tbl-border\">");
  			html.WriteLine("      <table border=\"0\" style=\"width: 850px;\" cellpadding=\"0\" cellspacing=\"1\">");
  			html.WriteLine("         <tr>");
  			html.WriteLine(
  				"            <td style=\"width: 250px;\" rowspan=\"2\" class=\"entry\"><center>{0}</center></td>",
  				title);
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_sturdytool.jpg\" alt=\"Sturdy Pickaxe/Shovel (150 uses)\"></center></td>");
  			html.WriteLine("            <td style=\"width: 75px;\" colspan=\"3\" class=\"entry\"><center>Gloves</center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_gargaxe.jpg\" alt=\"Gargoyles Pickaxe (100 uses)\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_prospectortool.jpg\" alt=\"Prospectors Tool (50 uses)\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_powder.jpg\" alt=\"Powder of Temperament (10 uses)\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_anvil.jpg\" alt=\"Colored Anvil\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 100px;\" colspan=\"4\" class=\"entry\"><center>Power Scrolls</center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 200px;\" colspan=\"8\" class=\"entry\"><center>Runic Hammers</center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 100px;\" colspan=\"4\" class=\"entry\"><center>Ancient Hammers</center></td>");
  			html.WriteLine("         </tr>");
  			html.WriteLine("         <tr>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+1</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+3</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+5</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+5</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+10</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+15</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+20</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"du\"><center><small>Du</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"sh\"><center><small>Sh</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"co\"><center><small>Co</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"br\"><center><small>Br</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"go\"><center><small>Go</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"ag\"><center><small>Ag</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"ve\"><center><small>Ve</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"va\"><center><small>Va</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+10</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+15</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+30</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+60</small></center></td>");
  			html.WriteLine("         </tr>");
  		}
  
  		private static void WriteSmithBODFooter(StreamWriter html)
  		{
  			html.WriteLine("         <tr>");
  			html.WriteLine("            <td style=\"width: 250px;\" rowspan=\"2\" class=\"entry\">&nbsp;</td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_sturdytool.jpg\" alt=\"Sturdy Pickaxe/Shovel (150 uses)\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" class=\"entry\"><center><small>+1</small></center>&nbsp;</td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" class=\"entry\"><center><small>+3</small></center>&nbsp;</td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" class=\"entry\"><center><small>+5</small></center>&nbsp;</td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_gargaxe.jpg\" alt=\"Gargoyles Pickaxe (100 uses)\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_prospectortool.jpg\" alt=\"Prospectors Tool (50 uses)\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_powder.jpg\" alt=\"Powder of Temperament (10 uses)\"></center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 25px;\" rowspan=\"2\" class=\"entry\"><center><img src=\"http://www.runuo.com/images/bodreward_anvil.jpg\" alt=\"Colored Anvil\"></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+5</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+10</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+15</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+20</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"du\"><center><small>Du</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"sh\"><center><small>Sh</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"co\"><center><small>Co</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"br\"><center><small>Br</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"go\"><center><small>Go</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"ag\"><center><small>Ag</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"ve\"><center><small>Ve</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"va\"><center><small>Va</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+10</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+15</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+30</small></center></td>");
  			html.WriteLine("            <td style=\"width: 25px;\" class=\"entry\"><center><small>+60</small></center></td>");
  			html.WriteLine("         </tr>");
  			html.WriteLine("         <tr>");
  			html.WriteLine("            <td style=\"width: 75px;\" colspan=\"3\" class=\"entry\"><center>Gloves</center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 100px;\" colspan=\"4\" class=\"entry\"><center>Power Scrolls</center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 200px;\" colspan=\"8\" class=\"entry\"><center>Runic Hammers</center></td>");
  			html.WriteLine(
  				"            <td style=\"width: 100px;\" colspan=\"4\" class=\"entry\"><center>Ancient Hammers</center></td>");
  			html.WriteLine("         </tr>");
  			html.WriteLine("      </table></td></tr></table>");
  		}
  
  		private static void DocumentSmithBOD(
  			StreamWriter html,
  			IEnumerable<Item> items,
  			string amt,
  			BulkMaterialType material)
  		{
  			var rewards = new bool[12];
  
  			foreach (var item in items)
  			{
  				if (item is SturdyPickaxe || item is SturdyShovel)
  				{
  					rewards[0] = true;
  				}
  				else if (item is GargoylesPickaxe)
  				{
  					rewards[1] = true;
  				}
  				else if (item is ProspectorsTool)
  				{
  					rewards[2] = true;
  				}
  				else if (item is ColoredAnvil)
  				{
  					rewards[3] = true;
  				}
  				else if (item is PowerScroll)
  				{
  					var ps = (PowerScroll)item;
  
  					if (ps.Value == 105.0)
  					{
  						rewards[4] = true;
  					}
  					else if (ps.Value == 110.0)
  					{
  						rewards[5] = true;
  					}
  					else if (ps.Value == 115.0)
  					{
  						rewards[6] = true;
  					}
  					else if (ps.Value == 120.0)
  					{
  						rewards[7] = true;
  					}
  				}
  				else if (item is AncientSmithyHammer)
  				{
  					var ash = (AncientSmithyHammer)item;
  
  					if (ash.Bonus == 10)
  					{
  						rewards[8] = true;
  					}
  					else if (ash.Bonus == 15)
  					{
  						rewards[9] = true;
  					}
  					else if (ash.Bonus == 30)
  					{
  						rewards[10] = true;
  					}
  					else if (ash.Bonus == 60)
  					{
  						rewards[11] = true;
  					}
  				}
  
  				item.Delete();
  			}
  
  			string style = null;
  			string name = null;
  
  			switch (material)
  			{
  				case BulkMaterialType.None:
  					style = "ir";
  					name = "Iron";
  					break;
  				case BulkMaterialType.DullCopper:
  					style = "du";
  					name = "Dull Copper";
  					break;
  				case BulkMaterialType.ShadowIron:
  					style = "sh";
  					name = "Shadow Iron";
  					break;
  				case BulkMaterialType.Copper:
  					style = "co";
  					name = "Copper";
  					break;
  				case BulkMaterialType.Bronze:
  					style = "br";
  					name = "Bronze";
  					break;
  				case BulkMaterialType.Gold:
  					style = "go";
  					name = "Gold";
  					break;
  				case BulkMaterialType.Agapite:
  					style = "ag";
  					name = "Agapite";
  					break;
  				case BulkMaterialType.Verite:
  					style = "ve";
  					name = "Verite";
  					break;
  				case BulkMaterialType.Valorite:
  					style = "va";
  					name = "Valorite";
  					break;
  			}
  
  			html.WriteLine("         <tr>");
  			html.WriteLine(
  				"            <td style=\"width: 250px\" class=\"entry\">{0} <span style=\"font-size: 1pt\">{1}</span></td>",
  				name,
  				amt);
  
  			var index = 0;
  
  			while (index < 12)
  			{
  				if (rewards[index])
  				{
  					html.WriteLine("            <td style=\"width: 25px;\" class=\"{0}\"><center><b>X</b></center></td>", style);
  					++index;
  				}
  				else
  				{
  					var count = 0;
  
  					while (index < 12 && !rewards[index])
  					{
  						++count;
  						++index;
  
  						if (index == 4 || index == 8 || index == 12 || index == 20)
  						{
  							break;
  						}
  					}
  
  					html.WriteLine(
  						"            <td style=\"width: {0}px;\"{1} class=\"entry\">&nbsp;</td>",
  						count * 13,
  						count == 1 ? "" : String.Format(" colspan=\"{0}\"", count));
  				}
  			}
  
  			html.WriteLine("         </tr>");
  		}
  		#endregion
  
  		#endregion
  
  		#region Bodies
  		public static List<BodyEntry> LoadBodies()
  		{
  			var list = new List<BodyEntry>();
  
  			var path = Path.Combine(Core.BaseDirectory, "Data/models.txt");
  
  			if (File.Exists(path))
  			{
  				using (var ip = new StreamReader(path))
  				{
  					string line;
  
  					while ((line = ip.ReadLine()) != null)
  					{
  						line = line.Trim();
  
  						if (line.Length == 0 || line.StartsWith("#"))
  						{
  							continue;
  						}
  
  						var split = line.Split('\t');
  
  						if (split.Length < 3)
  						{
  							continue;
  						}
  
  						int body;
  
  						if (!Int32.TryParse(split[0], out body))
  						{
  							continue;
  						}
  
  						ModelBodyType type;
  
  						if (!Enum.TryParse(split[1], out type))
  						{
  							type = ModelBodyType.Invalid;
  						}
  
  						var name = String.Join(" ", split.Skip(2).Select(n => !String.IsNullOrWhiteSpace(n) ? n : "unknown"));
  
  						var entry = new BodyEntry(body, type, name);
  
  						if (!list.Contains(entry))
  						{
  							list.Add(entry);
  						}
  					}
  				}
  			}
  
  			return list;
  		}
  
  		private static void DocumentBodies()
  		{
  			var list = LoadBodies();
  
  			using (var html = GetWriter("docs/", "bodies.html"))
  			{
  				html.WriteLine("<!DOCTYPE html>");
  				html.WriteLine("<html>");
  				html.WriteLine("   <head>");
  				html.WriteLine("      <title>RunUO Documentation - Body List</title>");
  				html.WriteLine("      <style type=\"text/css\">");
  				html.WriteLine("      body { background-color: white; font-family: Tahoma; color: #000000; }");
  				html.WriteLine("      a, a:visited { color: #000000; }");
  				html.WriteLine("      a:active, a:hover { color: #808080; }");
  				html.WriteLine("      table { width: 100%; }");
  				html.WriteLine("      td.header { width: 100%; }");
  				html.WriteLine("      </style>");
  				html.WriteLine("      <link rel=\"stylesheet\" type=\"text/css\" href=\"styles.css\" />");
  				html.WriteLine("   </head>");
  				html.WriteLine("   <body>");
  				html.WriteLine("      <a name=\"Top\" />");
  				html.WriteLine("      <h4><a href=\"index.html\">Back to the index</a></h4>");
  
  				if (list.Count > 0)
  				{
  					html.WriteLine("      <h2>Body List</h2>");
  
  					list.Sort(new BodyEntrySorter());
  
  					var lastType = ModelBodyType.Invalid;
  
  					foreach (var entry in list)
  					{
  						var type = entry.BodyType;
  
  						if (type != lastType)
  						{
  							if (lastType != ModelBodyType.Invalid)
  							{
  								html.WriteLine("      </table></td></tr></table><br />");
  							}
  
  							lastType = type;
  
  							html.WriteLine("      <a name=\"{0}\" />", type);
  
  							switch (type)
  							{
  								case ModelBodyType.Monsters:
  									html.WriteLine(
  										"      <b>Monsters</b> | <a href=\"#Sea\">Sea</a> | <a href=\"#Animals\">Animals</a> | <a href=\"#Human\">Human</a> | <a href=\"#Equipment\">Equipment</a><br /><br />");
  									break;
  								case ModelBodyType.Sea:
  									html.WriteLine(
  										"      <a href=\"#Top\">Monsters</a> | <b>Sea</b> | <a href=\"#Animals\">Animals</a> | <a href=\"#Human\">Human</a> | <a href=\"#Equipment\">Equipment</a><br /><br />");
  									break;
  								case ModelBodyType.Animals:
  									html.WriteLine(
  										"      <a href=\"#Top\">Monsters</a> | <a href=\"#Sea\">Sea</a> | <b>Animals</b> | <a href=\"#Human\">Human</a> | <a href=\"#Equipment\">Equipment</a><br /><br />");
  									break;
  								case ModelBodyType.Human:
  									html.WriteLine(
  										"      <a href=\"#Top\">Monsters</a> | <a href=\"#Sea\">Sea</a> | <a href=\"#Animals\">Animals</a> | <b>Human</b> | <a href=\"#Equipment\">Equipment</a><br /><br />");
  									break;
  								case ModelBodyType.Equipment:
  									html.WriteLine(
  										"      <a href=\"#Top\">Monsters</a> | <a href=\"#Sea\">Sea</a> | <a href=\"#Animals\">Animals</a> | <a href=\"#Human\">Human</a> | <b>Equipment</b><br /><br />");
  									break;
  							}
  
  							html.WriteLine("      <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">");
  							html.WriteLine("      <tr><td class=\"tbl-border\">");
  							html.WriteLine("      <table cellpadding=\"4\" cellspacing=\"1\">");
  							html.WriteLine("         <tr><td colspan=\"2\" class=\"header\">{0}</td></tr>", type);
  						}
  
  						html.WriteLine(
  							"         <tr><td class=\"lentry\">{0}</td><td class=\"rentry\">{1}</td></tr>",
  							entry.Body.BodyID,
  							entry.Name);
  					}
  
  					html.WriteLine("      </table>");
  				}
  				else
  				{
  					html.WriteLine("      This feature requires a UO:3D installation.");
  				}
  
  				html.WriteLine("   </body>");
  				html.WriteLine("</html>");
  			}
  		}
  		#endregion
  
  		#region Speech
  		private static void DocumentKeywords()
  		{
  			var tables = LoadSpeechFile();
  
  			using (var html = GetWriter("docs/", "keywords.html"))
  			{
  				html.WriteLine("<!DOCTYPE html>");
  				html.WriteLine("<html>");
  				html.WriteLine("   <head>");
  				html.WriteLine("      <title>RunUO Documentation - Speech Keywords</title>");
  				html.WriteLine("      <style type=\"text/css\">");
  				html.WriteLine("      body { background-color: white; font-family: Tahoma; color: #000000; }");
  				html.WriteLine("      a, a:visited { color: #000000; }");
  				html.WriteLine("      a:active, a:hover { color: #808080; }");
  				html.WriteLine("      table { width: 100%; }");
  				html.WriteLine("      </style>");
  				html.WriteLine("      <link rel=\"stylesheet\" type=\"text/css\" href=\"styles.css\" />");
  				html.WriteLine("   </head>");
  				html.WriteLine("   <body>");
  				html.WriteLine("      <h4><a href=\"index.html\">Back to the index</a></h4>");
  				html.WriteLine("      <h2>Speech Keywords</h2>");
  
  				for (var p = 0; p < 1 && p < tables.Count; ++p)
  				{
  					var table = tables[p];
  
  					if (p > 0)
  					{
  						html.WriteLine("      <br />");
  					}
  
  					html.WriteLine("      <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">");
  					html.WriteLine("      <tr><td class=\"tbl-border\">");
  					html.WriteLine("      <table cellpadding=\"4\" cellspacing=\"1\">");
  					html.WriteLine("         <tr><td class=\"header\">Number</td><td class=\"header\">Text</td></tr>");
  
  					var list = new List<SpeechEntry>(table.Values);
  					list.Sort(new SpeechEntrySorter());
  
  					foreach (var entry in list)
  					{
  						html.Write("         <tr><td class=\"lentry\">0x{0:X4}</td><td class=\"rentry\">", entry.Index);
  
  						entry.Strings.Sort(); //( new EnglishPrioStringSorter() );
  
  						for (var j = 0; j < entry.Strings.Count; ++j)
  						{
  							if (j > 0)
  							{
  								html.Write("<br />");
  							}
  
  							var v = entry.Strings[j];
  
  							foreach (var c in v)
  							{
  								switch (c)
  								{
  									case '<':
  										html.Write("&lt;");
  										break;
  									case '>':
  										html.Write("&gt;");
  										break;
  									case '&':
  										html.Write("&amp;");
  										break;
  									case '"':
  										html.Write("&quot;");
  										break;
  									case '\'':
  										html.Write("&apos;");
  										break;
  									default:
  									{
  										if (c >= 0x20 && c < 0x80)
  										{
  											html.Write(c);
  										}
  										else
  										{
  											html.Write("&#{0};", (int)c);
  										}
  									}
  										break;
  								}
  							}
  						}
  
  						html.WriteLine("</td></tr>");
  					}
  
  					html.WriteLine("      </table></td></tr></table>");
  				}
  
  				html.WriteLine("   </body>");
  				html.WriteLine("</html>");
  			}
  		}
  
  		private class SpeechEntry
  		{
  			private readonly int m_Index;
  			private readonly List<string> m_Strings;
  
  			public int Index { get { return m_Index; } }
  			public List<string> Strings { get { return m_Strings; } }
  
  			public SpeechEntry(int index)
  			{
  				m_Index = index;
  				m_Strings = new List<string>();
  			}
  		}
  
  		private class SpeechEntrySorter : IComparer<SpeechEntry>
  		{
  			public int Compare(SpeechEntry x, SpeechEntry y)
  			{
  				return x.Index.CompareTo(y.Index);
  			}
  		}
  
  		private static List<Dictionary<int, SpeechEntry>> LoadSpeechFile()
  		{
  			var tables = new List<Dictionary<int, SpeechEntry>>();
  			var lastIndex = -1;
  
  			Dictionary<int, SpeechEntry> table = null;
  
  			var path = Core.FindDataFile("speech.mul");
  
  			if (File.Exists(path))
  			{
  				using (var ip = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
  				{
  					var bin = new BinaryReader(ip);
  
  					while (bin.PeekChar() >= 0)
  					{
  						var index = (bin.ReadByte() << 8) | bin.ReadByte();
  						var length = (bin.ReadByte() << 8) | bin.ReadByte();
  						var text = Encoding.UTF8.GetString(bin.ReadBytes(length)).Trim();
  
  						if (text.Length == 0)
  						{
  							continue;
  						}
  
  						if (table == null || lastIndex > index)
  						{
  							if (index == 0 && text == "*withdraw*")
  							{
  								tables.Insert(0, table = new Dictionary<int, SpeechEntry>());
  							}
  							else
  							{
  								tables.Add(table = new Dictionary<int, SpeechEntry>());
  							}
  						}
  
  						lastIndex = index;
  
  						SpeechEntry entry;
  						table.TryGetValue(index, out entry);
  
  						if (entry == null)
  						{
  							table[index] = entry = new SpeechEntry(index);
  						}
  
  						entry.Strings.Add(text);
  					}
  				}
  			}
  
  			return tables;
  		}
  		#endregion
  
  		#region Commands
  		public class DocCommandEntry
  		{
  			private readonly AccessLevel m_AccessLevel;
  			private readonly string m_Name;
  			private readonly string[] m_CmdAliases;
  			private readonly string m_Usage;
  			private readonly string m_Description;
  
  			public AccessLevel AccessLevel { get { return m_AccessLevel; } }
  			public string Name { get { return m_Name; } }
  			public string[] Aliases { get { return m_CmdAliases; } }
  			public string Usage { get { return m_Usage; } }
  			public string Description { get { return m_Description; } }
  
  			public DocCommandEntry(AccessLevel accessLevel, string name, string[] aliases, string usage, string description)
  			{
  				m_AccessLevel = accessLevel;
  				m_Name = name;
  				m_CmdAliases = aliases;
  				m_Usage = usage;
  				m_Description = description;
  			}
  		}
  
  		public class CommandEntrySorter : IComparer<DocCommandEntry>
  		{
  			public int Compare(DocCommandEntry a, DocCommandEntry b)
  			{
  				var v = b.AccessLevel.CompareTo(a.AccessLevel);
  
  				if (v == 0)
  				{
  					v = String.Compare(a.Name, b.Name, StringComparison.Ordinal);
  				}
  
  				return v;
  			}
  		}
  
  		private static void DocumentCommands()
  		{
  			using (var html = GetWriter("docs/", "commands.html"))
  			{
  				html.WriteLine("<!DOCTYPE html>");
  				html.WriteLine("<html>");
  				html.WriteLine("   <head>");
  				html.WriteLine("      <title>RunUO Documentation - Commands</title>");
  				html.WriteLine("      <style type=\"text/css\">");
  				html.WriteLine("      body { background-color: white; font-family: Tahoma; color: #000000; }");
  				html.WriteLine("      a, a:visited { color: #000000; }");
  				html.WriteLine("      a:active, a:hover { color: #808080; }");
  				html.WriteLine("      table { width: 100%; }");
  				html.WriteLine("      td.header { width: 100%; }");
  				html.WriteLine("      </style>");
  				html.WriteLine("      <link rel=\"stylesheet\" type=\"text/css\" href=\"styles.css\" />");
  				html.WriteLine("   </head>");
  				html.WriteLine("   <body>");
  				html.WriteLine("      <a name=\"Top\" />");
  				html.WriteLine("      <h4><a href=\"index.html\">Back to the index</a></h4>");
  				html.WriteLine("      <h2>Commands</h2>");
  
  				var commands = new List<CommandEntry>(CommandSystem.Entries.Values);
  				var list = new List<DocCommandEntry>();
  
  				commands.Sort();
  				commands.Reverse();
  				Clean(commands);
  
  				foreach (var e in commands)
  				{
  					var mi = e.Handler.Method;
  
  					var attrs = mi.GetCustomAttributes(typeof(UsageAttribute), false);
  
  					if (attrs.Length == 0)
  					{
  						continue;
  					}
  
  					var usage = attrs[0] as UsageAttribute;
  
  					attrs = mi.GetCustomAttributes(typeof(DescriptionAttribute), false);
  
  					if (attrs.Length == 0)
  					{
  						continue;
  					}
  
  					var desc = attrs[0] as DescriptionAttribute;
  
  					if (usage == null || desc == null)
  					{
  						continue;
  					}
  
  					attrs = mi.GetCustomAttributes(typeof(AliasesAttribute), false);
  
  					var aliases = (attrs.Length == 0 ? null : attrs[0] as AliasesAttribute);
  
  					var descString = desc.Description.Replace("<", "&lt;").Replace(">", "&gt;");
  
  					list.Add(
  						aliases == null
  							? new DocCommandEntry(e.AccessLevel, e.Command, null, usage.Usage, descString)
  							: new DocCommandEntry(e.AccessLevel, e.Command, aliases.Aliases, usage.Usage, descString));
  				}
  
  				foreach (var command in TargetCommands.AllCommands)
  				{
  					var usage = command.Usage;
  					var desc = command.Description;
  
  					if (usage == null || desc == null)
  					{
  						continue;
  					}
  
  					var cmds = command.Commands;
  					var cmd = cmds[0];
  					var aliases = new string[cmds.Length - 1];
  
  					for (var j = 0; j < aliases.Length; ++j)
  					{
  						aliases[j] = cmds[j + 1];
  					}
  
  					desc = desc.Replace("<", "&lt;").Replace(">", "&gt;");
  
  					if (command.Supports != CommandSupport.Single)
  					{
  						var sb = new StringBuilder(50 + desc.Length);
  
  						sb.Append("Modifiers: ");
  
  						if ((command.Supports & CommandSupport.Global) != 0)
  						{
  							sb.Append("<i><a href=\"#Global\">Global</a></i>, ");
  						}
  
  						if ((command.Supports & CommandSupport.Online) != 0)
  						{
  							sb.Append("<i><a href=\"#Online\">Online</a></i>, ");
  						}
  
  						if ((command.Supports & CommandSupport.Region) != 0)
  						{
  							sb.Append("<i><a href=\"#Region\">Region</a></i>, ");
  						}
  
  						if ((command.Supports & CommandSupport.Contained) != 0)
  						{
  							sb.Append("<i><a href=\"#Contained\">Contained</a></i>, ");
  						}
  
  						if ((command.Supports & CommandSupport.Multi) != 0)
  						{
  							sb.Append("<i><a href=\"#Multi\">Multi</a></i>, ");
  						}
  
  						if ((command.Supports & CommandSupport.Area) != 0)
  						{
  							sb.Append("<i><a href=\"#Area\">Area</a></i>, ");
  						}
  
  						if ((command.Supports & CommandSupport.Self) != 0)
  						{
  							sb.Append("<i><a href=\"#Self\">Self</a></i>, ");
  						}
  
  						sb.Remove(sb.Length - 2, 2);
  						sb.Append("<br />");
  						sb.Append(desc);
  
  						desc = sb.ToString();
  					}
  
  					list.Add(new DocCommandEntry(command.AccessLevel, cmd, aliases, usage, desc));
  				}
  
  				var commandImpls = BaseCommandImplementor.Implementors;
  
  				foreach (var command in commandImpls)
  				{
  					var usage = command.Usage;
  					var desc = command.Description;
  
  					if (usage == null || desc == null)
  					{
  						continue;
  					}
  
  					var cmds = command.Accessors;
  					var cmd = cmds[0];
  					var aliases = new string[cmds.Length - 1];
  
  					for (var j = 0; j < aliases.Length; ++j)
  					{
  						aliases[j] = cmds[j + 1];
  					}
  
  					desc = desc.Replace("<", "&lt;").Replace(">", "&gt;");
  
  					list.Add(new DocCommandEntry(command.AccessLevel, cmd, aliases, usage, desc));
  				}
  
  				list.Sort(new CommandEntrySorter());
  
  				var last = AccessLevel.Player;
  
  				foreach (var e in list)
  				{
  					if (e.AccessLevel != last)
  					{
  						if (last != AccessLevel.Player)
  						{
  							html.WriteLine("      </table></td></tr></table><br />");
  						}
  
  						last = e.AccessLevel;
  
  						html.WriteLine("      <a name=\"{0}\" />", last);
  
  						switch (last)
  						{
  							case AccessLevel.Administrator:
  								html.WriteLine(
  									"      <b>Administrator</b> | <a href=\"#GameMaster\">Game Master</a> | <a href=\"#Counselor\">Counselor</a> | <a href=\"#Player\">Player</a><br /><br />");
  								break;
  							case AccessLevel.GameMaster:
  								html.WriteLine(
  									"      <a href=\"#Top\">Administrator</a> | <b>Game Master</b> | <a href=\"#Counselor\">Counselor</a> | <a href=\"#Player\">Player</a><br /><br />");
  								break;
  							case AccessLevel.Seer:
  								html.WriteLine(
  									"      <a href=\"#Top\">Administrator</a> | <a href=\"#GameMaster\">Game Master</a> | <a href=\"#Counselor\">Counselor</a> | <a href=\"#Player\">Player</a><br /><br />");
  								break;
  							case AccessLevel.Counselor:
  								html.WriteLine(
  									"      <a href=\"#Top\">Administrator</a> | <a href=\"#GameMaster\">Game Master</a> | <b>Counselor</b> | <a href=\"#Player\">Player</a><br /><br />");
  								break;
  							case AccessLevel.Player:
  								html.WriteLine(
  									"      <a href=\"#Top\">Administrator</a> | <a href=\"#GameMaster\">Game Master</a> | <a href=\"#Counselor\">Counselor</a> | <b>Player</b><br /><br />");
  								break;
  						}
  
  						html.WriteLine("      <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">");
  						html.WriteLine("      <tr><td class=\"tbl-border\">");
  						html.WriteLine("      <table cellpadding=\"4\" cellspacing=\"1\">");
  						html.WriteLine(
  							"         <tr><td colspan=\"2\" class=\"header\">{0}</td></tr>",
  							last == AccessLevel.GameMaster ? "Game Master" : last.ToString());
  					}
  
  					DocumentCommand(html, e);
  				}
  
  				html.WriteLine("      </table></td></tr></table>");
  				html.WriteLine("   </body>");
  				html.WriteLine("</html>");
  			}
  		}
  
  		public static void Clean(List<CommandEntry> list)
  		{
  			for (var i = 0; i < list.Count; ++i)
  			{
  				var e = list[i];
  
  				for (var j = i + 1; j < list.Count; ++j)
  				{
  					var c = list[j];
  
  					if (e.Handler.Method == c.Handler.Method)
  					{
  						list.RemoveAt(j);
  						--j;
  					}
  				}
  			}
  		}
  
  		private static void DocumentCommand(StreamWriter html, DocCommandEntry e)
  		{
  			var usage = e.Usage;
  			var desc = e.Description;
  			var aliases = e.Aliases;
  
  			html.Write("         <tr><a name=\"{0}\" /><td class=\"lentry\">{0}</td>", e.Name);
  
  			if (aliases == null || aliases.Length == 0)
  			{
  				html.Write(
  					"<td class=\"rentry\"><b>Usage: {0}</b><br />{1}</td>",
  					usage.Replace("<", "&lt;").Replace(">", "&gt;"),
  					desc);
  			}
  			else
  			{
  				html.Write(
  					"<td class=\"rentry\"><b>Usage: {0}</b><br />Alias{1}: ",
  					usage.Replace("<", "&lt;").Replace(">", "&gt;"),
  					aliases.Length == 1 ? "" : "es");
  
  				for (var i = 0; i < aliases.Length; ++i)
  				{
  					if (i != 0)
  					{
  						html.Write(", ");
  					}
  
  					html.Write(aliases[i]);
  				}
  
  				html.Write("<br />{0}</td>", desc);
  			}
  
  			html.WriteLine("</tr>");
  		}
  		#endregion
  
  		private static void LoadTypes(Assembly a, Assembly[] asms)
  		{
  			var types = a.GetTypes();
  
  			foreach (var type in types)
  			{
  				var nspace = type.Namespace;
  
  				if (nspace == null || type.IsSpecialName)
  				{
  					continue;
  				}
  
  				var info = new TypeInfo(type);
  				m_Types[type] = info;
  
  				List<TypeInfo> nspaces;
  				m_Namespaces.TryGetValue(nspace, out nspaces);
  
  				if (nspaces == null)
  				{
  					m_Namespaces[nspace] = nspaces = new List<TypeInfo>();
  				}
  
  				nspaces.Add(info);
  
  				var baseType = info.m_BaseType;
  
  				if (baseType != null && InAssemblies(baseType, asms))
  				{
  					TypeInfo baseInfo;
  					m_Types.TryGetValue(baseType, out baseInfo);
  
  					if (baseInfo == null)
  					{
  						m_Types[baseType] = baseInfo = new TypeInfo(baseType);
  					}
  
  					if (baseInfo.m_Derived == null)
  					{
  						baseInfo.m_Derived = new List<TypeInfo>();
  					}
  
  					baseInfo.m_Derived.Add(info);
  				}
  
  				var decType = info.m_Declaring;
  
  				if (decType != null)
  				{
  					TypeInfo decInfo;
  					m_Types.TryGetValue(decType, out decInfo);
  
  					if (decInfo == null)
  					{
  						m_Types[decType] = decInfo = new TypeInfo(decType);
  					}
  
  					if (decInfo.m_Nested == null)
  					{
  						decInfo.m_Nested = new List<TypeInfo>();
  					}
  
  					decInfo.m_Nested.Add(info);
  				}
  
  				foreach (var iface in info.m_Interfaces)
  				{
  					if (!InAssemblies(iface, asms))
  					{
  						continue;
  					}
  
  					TypeInfo ifaceInfo = null;
  					m_Types.TryGetValue(iface, out ifaceInfo);
  
  					if (ifaceInfo == null)
  					{
  						m_Types[iface] = ifaceInfo = new TypeInfo(iface);
  					}
  
  					if (ifaceInfo.m_Derived == null)
  					{
  						ifaceInfo.m_Derived = new List<TypeInfo>();
  					}
  
  					ifaceInfo.m_Derived.Add(info);
  				}
  			}
  		}
  
  		private static bool InAssemblies(Type t, IEnumerable<Assembly> asms)
  		{
  			return asms.Any(a => a == t.Assembly);
  		}
  
  		#region Constructable Objects
  		private static readonly Type typeofItem = typeof(Item);
  		private static readonly Type typeofMobile = typeof(Mobile);
  		private static readonly Type typeofMap = typeof(Map);
  		private static readonly Type typeofCustomEnum = typeof(CustomEnumAttribute);
  
  		private static bool IsConstructable(Type t, out bool isItem)
  		{
  			isItem = typeofItem.IsAssignableFrom(t);
  
  			if (isItem)
  			{
  				return true;
  			}
  
  			return typeofMobile.IsAssignableFrom(t);
  		}
  
  		private static bool IsConstructable(ConstructorInfo ctor)
  		{
  			return ctor.IsDefined(typeof(ConstructableAttribute), false);
  		}
  
  		private static void DocumentConstructableObjects()
  		{
  			var types = new List<TypeInfo>(m_Types.Values);
  			types.Sort(new TypeComparer());
  
  			ArrayList items = new ArrayList(), mobiles = new ArrayList();
  
  			foreach (var t in types.Select(ti => ti.m_Type))
  			{
  				bool isItem;
  
  				if (t.IsAbstract || !IsConstructable(t, out isItem))
  				{
  					continue;
  				}
  
  				var ctors = t.GetConstructors();
  				var anyConstructable = false;
  
  				for (var j = 0; !anyConstructable && j < ctors.Length; ++j)
  				{
  					anyConstructable = IsConstructable(ctors[j]);
  				}
  
  				if (!anyConstructable)
  				{
  					continue;
  				}
  
  				(isItem ? items : mobiles).Add(t);
  				(isItem ? items : mobiles).Add(ctors);
  			}
  
  			using (var html = GetWriter("docs/", "objects.html"))
  			{
  				html.WriteLine("<!DOCTYPE html>");
  				html.WriteLine("<html>");
  				html.WriteLine("   <head>");
  				html.WriteLine("      <title>RunUO Documentation - Constructable Objects</title>");
  				html.WriteLine("      <style type=\"text/css\">");
  				html.WriteLine("      body { background-color: white; font-family: Tahoma; color: #000000; }");
  				html.WriteLine("      a, a:visited { color: #000000; }");
  				html.WriteLine("      a:active, a:hover { color: #808080; }");
  				html.WriteLine("      table { width: 100%; }");
  				html.WriteLine("      </style>");
  				html.WriteLine("      <link rel=\"stylesheet\" type=\"text/css\" href=\"styles.css\" />");
  				html.WriteLine("   </head>");
  				html.WriteLine("   <body>");
  				html.WriteLine("      <h4><a href=\"index.html\">Back to the index</a></h4>");
  				html.WriteLine("      <h2>Constructable <a href=\"#items\">Items</a> and <a href=\"#mobiles\">Mobiles</a></h2>");
  
  				html.WriteLine("      <a name=\"items\" />");
  				html.WriteLine("      <table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">");
  				html.WriteLine("      <tr><td class=\"tbl-border\">");
  				html.WriteLine("      <table cellpadding=\"4\" cellspacing=\"1\">");
  				html.WriteLine("         <tr><td class=\"header\">Item Name</td><td class=\"header\">Usage</td></tr>");
  
  				for (var i = 0; i < items.Count; i += 2)
  				{
  					DocumentConstructableObject(html, (Type)items[i], (ConstructorInfo[])items[i + 1]);
  				}
  
  				html.WriteLine("      </table></td></tr></table><br /><br />");
  
  				html.WriteLine("      <a name=\"mobiles\" />");
  				html.WriteLine("      <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\">");
  				html.WriteLine("      <tr><td class=\"tbl-border\">");
  				html.WriteLine("      <table width=\"100%\" cellpadding=\"4\" cellspacing=\"1\">");
  				html.WriteLine("         <tr><td class=\"header\">Mobile Name</td><td class=\"header\">Usage</td></tr>");
  
  				for (var i = 0; i < mobiles.Count; i += 2)
  				{
  					DocumentConstructableObject(html, (Type)mobiles[i], (ConstructorInfo[])mobiles[i + 1]);
  				}
  
  				html.WriteLine("      </table></td></tr></table>");
  
  				html.WriteLine("   </body>");
  				html.WriteLine("</html>");
  			}
  		}
  
  		private static void DocumentConstructableObject(StreamWriter html, Type t, IEnumerable<ConstructorInfo> ctors)
  		{
  			html.Write("         <tr><td class=\"lentry\">{0}</td><td class=\"rentry\">", t.Name);
  
  			var first = true;
  
  			foreach (var ctor in ctors.Where(IsConstructable))
  			{
  				if (!first)
  				{
  					html.Write("<br />");
  				}
  
  				first = false;
  
  				html.Write("{0}Add {1}", CommandSystem.Prefix, t.Name);
  
  				var parms = ctor.GetParameters();
  
  				foreach (var p in parms)
  				{
  					html.Write(" <a ");
  
  					TypeInfo typeInfo;
  					m_Types.TryGetValue(p.ParameterType, out typeInfo);
  
  					if (typeInfo != null)
  					{
  						html.Write("href=\"types/{0}\" ", typeInfo.FileName);
  					}
  
  					html.Write("title=\"{0}\">{1}</a>", GetTooltipFor(p), p.Name);
  				}
  			}
  
  			html.WriteLine("</td></tr>");
  		}
  		#endregion
  
  		#region Tooltips
  		private const string HtmlNewLine = "&#13;";
  
  		private static readonly object[,] m_Tooltips =
  		{
  			{typeof(Byte), "Numeric value in the range from 0 to 255, inclusive."},
  			{typeof(SByte), "Numeric value in the range from negative 128 to positive 127, inclusive."},
  			{typeof(UInt16), "Numeric value in the range from 0 to 65,535, inclusive."},
  			{typeof(Int16), "Numeric value in the range from negative 32,768 to positive 32,767, inclusive."},
  			{typeof(UInt32), "Numeric value in the range from 0 to 4,294,967,295, inclusive."},
  			{typeof(Int32), "Numeric value in the range from negative 2,147,483,648 to positive 2,147,483,647, inclusive."},
  			{typeof(UInt64), "Numeric value in the range from 0 through about 10^20."},
  			{typeof(Int64), "Numeric value in the approximate range from negative 10^19 through 10^19."},
  			{
  				typeof(String),
  				"Text value. To specify a value containing spaces, encapsulate the value in quote characters:{0}{0}&quot;Spaced text example&quot;"
  			},
  			{typeof(Boolean), "Boolean value which can be either True or False."},
  			{typeof(Map), "Map or facet name. Possible values include:{0}{0}- Felucca{0}- Trammel{0}- Ilshenar{0}- Malas"},
  			{
  				typeof(Poison),
  				"Poison name or level. Possible values include:{0}{0}- Lesser{0}- Regular{0}- Greater{0}- Deadly{0}- Lethal"
  			},
  			{
  				typeof(Point3D),
  				"Three-dimensional coordinate value. Format as follows:{0}{0}&quot;(<x value>, <y value>, <z value>)&quot;"
  			}
  		};
  
  		private static string GetTooltipFor(ParameterInfo param)
  		{
  			var paramType = param.ParameterType;
  
  			for (var i = 0; i < m_Tooltips.GetLength(0); ++i)
  			{
  				var checkType = (Type)m_Tooltips[i, 0];
  
  				if (paramType == checkType)
  				{
  					return String.Format((string)m_Tooltips[i, 1], HtmlNewLine);
  				}
  			}
  
  			if (paramType.IsEnum)
  			{
  				var sb = new StringBuilder();
  
  				sb.AppendFormat("Enumeration value or name. Possible named values include:{0}", HtmlNewLine);
  
  				var names = Enum.GetNames(paramType);
  
  				foreach (var n in names)
  				{
  					sb.AppendFormat("{0}- {1}", HtmlNewLine, n);
  				}
  
  				return sb.ToString();
  			}
  
  			if (paramType.IsDefined(typeofCustomEnum, false))
  			{
  				var attributes = paramType.GetCustomAttributes(typeofCustomEnum, false);
  
  				if (attributes.Length > 0)
  				{
  					var attr = attributes[0] as CustomEnumAttribute;
  
  					if (attr != null)
  					{
  						var sb = new StringBuilder();
  
  						sb.AppendFormat("Enumeration value or name. Possible named values include:{0}", HtmlNewLine);
  
  						var names = attr.Names;
  
  						foreach (var n in names)
  						{
  							sb.AppendFormat("{0}- {1}", HtmlNewLine, n);
  						}
  
  						return sb.ToString();
  					}
  				}
  			}
  			else if (paramType == typeofMap)
  			{
  				var sb = new StringBuilder();
  
  				sb.AppendFormat("Enumeration value or name. Possible named values include:{0}", HtmlNewLine);
  
  				var names = Map.GetMapNames();
  
  				foreach (var n in names)
  				{
  					sb.AppendFormat("{0}- {1}", HtmlNewLine, n);
  				}
  
  				return sb.ToString();
  			}
  
  			return "";
  		}
  		#endregion
  
  		#region Const Strings
  		private const string RefString = "<span style=\"color: blue;\">ref</span> ";
  		private const string GetString = " <span style=\"color: blue;\">get</span>;";
  		private const string SetString = " <span style=\"color: blue;\">set</span>;";
  
  		private const string InString = "<span style=\"color: blue;\">in</span> ";
  		private const string OutString = "<span style=\"color: blue;\">out</span> ";
  
  		private const string VirtString = "<span style=\"color: blue;\">virtual</span> ";
  		private const string CtorString = "(<span style=\"color: blue;\">ctor</span>) ";
  		private const string StaticString = "(<span style=\"color: blue;\">static</span>) ";
  		#endregion
  
  		private static void DocumentLoadedTypes()
  		{
  			using (var indexHtml = GetWriter("docs/", "overview.html"))
  			{
  				indexHtml.WriteLine("<!DOCTYPE html>");
  				indexHtml.WriteLine("<html>");
  				indexHtml.WriteLine("   <head>");
  				indexHtml.WriteLine("      <title>RunUO Documentation - Class Overview</title>");
  				indexHtml.WriteLine("      <style type=\"text/css\">");
  				indexHtml.WriteLine("      body { background-color: white; font-family: Tahoma; color: #000000; }");
  				indexHtml.WriteLine("      a, a:visited { color: #000000; }");
  				indexHtml.WriteLine("      a:active, a:hover { color: #808080; }");
  				indexHtml.WriteLine("      </style>");
  				indexHtml.WriteLine("   </head>");
  				indexHtml.WriteLine("   <body>");
  				indexHtml.WriteLine("      <h4><a href=\"index.html\">Back to the index</a></h4>");
  				indexHtml.WriteLine("      <h2>Namespaces</h2>");
  
  				var nspaces = new SortedList<string, List<TypeInfo>>(m_Namespaces);
  
  				foreach (var kvp in nspaces)
  				{
  					kvp.Value.Sort(new TypeComparer());
  
  					SaveNamespace(kvp.Key, kvp.Value, indexHtml);
  				}
  
  				indexHtml.WriteLine("   </body>");
  				indexHtml.WriteLine("</html>");
  			}
  		}
  
  		private static void SaveNamespace(string name, IEnumerable<TypeInfo> types, StreamWriter indexHtml)
  		{
  			var fileName = GetFileName("docs/namespaces/", name, ".html");
  
  			indexHtml.WriteLine("      <a href=\"namespaces/{0}\">{1}</a><br />", fileName, name);
  
  			using (var nsHtml = GetWriter("docs/namespaces/", fileName))
  			{
  				nsHtml.WriteLine("<!DOCTYPE html>");
  				nsHtml.WriteLine("<html>");
  				nsHtml.WriteLine("   <head>");
  				nsHtml.WriteLine("      <title>RunUO Documentation - Class Overview - {0}</title>", name);
  				nsHtml.WriteLine("      <style type=\"text/css\">");
  				nsHtml.WriteLine("      body { background-color: white; font-family: Tahoma; color: #000000; }");
  				nsHtml.WriteLine("      a, a:visited { color: #000000; }");
  				nsHtml.WriteLine("      a:active, a:hover { color: #808080; }");
  				nsHtml.WriteLine("      </style>");
  				nsHtml.WriteLine("   </head>");
  				nsHtml.WriteLine("   <body>");
  				nsHtml.WriteLine("      <h4><a href=\"../overview.html\">Back to the namespace index</a></h4>");
  				nsHtml.WriteLine("      <h2>{0}</h2>", name);
  
  				foreach (var t in types)
  				{
  					SaveType(t, nsHtml, fileName, name);
  				}
  
  				nsHtml.WriteLine("   </body>");
  				nsHtml.WriteLine("</html>");
  			}
  		}
  
  		private static void SaveType(TypeInfo info, StreamWriter nsHtml, string nsFileName, string nsName)
  		{
  			if (info.m_Declaring == null)
  			{
  				nsHtml.WriteLine("      <!-- DBG-ST -->" + info.LinkName("../types/") + "<br />");
  			}
  
  			using (var typeHtml = GetWriter(info.FileName))
  			{
  				typeHtml.WriteLine("<!DOCTYPE html>");
  				typeHtml.WriteLine("<html>");
  				typeHtml.WriteLine("   <head>");
  				typeHtml.WriteLine("      <title>RunUO Documentation - Class Overview - {0}</title>", info.TypeName);
  				typeHtml.WriteLine("      <style type=\"text/css\">");
  				typeHtml.WriteLine("      body { background-color: white; font-family: Tahoma; color: #000000; }");
  				typeHtml.WriteLine("      a, a:visited { color: #000000; }");
  				typeHtml.WriteLine("      a:active, a:hover { color: #808080; }");
  				typeHtml.WriteLine("      </style>");
  				typeHtml.WriteLine("   </head>");
  				typeHtml.WriteLine("   <body>");
  				typeHtml.WriteLine("      <h4><a href=\"../namespaces/{0}\">Back to {1}</a></h4>", nsFileName, nsName);
  
  				if (info.m_Type.IsEnum)
  				{
  					WriteEnum(info, typeHtml);
  				}
  				else
  				{
  					WriteType(info, typeHtml);
  				}
  
  				typeHtml.WriteLine("   </body>");
  				typeHtml.WriteLine("</html>");
  			}
  		}
  
  		#region Write[...]
  		private static void WriteEnum(TypeInfo info, StreamWriter typeHtml)
  		{
  			var type = info.m_Type;
  
  			typeHtml.WriteLine("      <h2>{0} (Enum)</h2>", info.TypeName);
  
  			var names = Enum.GetNames(type);
  
  			var flags = type.IsDefined(typeof(FlagsAttribute), false);
  
  			var format = flags ? "      {0:G} = 0x{1:X}{2}<br />" : "      {0:G} = {1:D}{2}<br />";
  
  			for (var i = 0; i < names.Length; ++i)
  			{
  				var value = Enum.Parse(type, names[i]);
  
  				typeHtml.WriteLine(format, names[i], value, i < (names.Length - 1) ? "," : "");
  			}
  		}
  
  		private static void WriteType(TypeInfo info, StreamWriter typeHtml)
  		{
  			var type = info.m_Type;
  
  			typeHtml.Write("      <h2>");
  
  			var decType = info.m_Declaring;
  
  			if (decType != null)
  			{
  				// We are a nested type
  
  				typeHtml.Write('(');
  
  				TypeInfo decInfo;
  				m_Types.TryGetValue(decType, out decInfo);
  
  				typeHtml.Write(decInfo == null ? decType.Name : decInfo.LinkName(null));
  
  				typeHtml.Write(") - ");
  			}
  
  			typeHtml.Write(info.TypeName);
  
  			var ifaces = info.m_Interfaces;
  			var baseType = info.m_BaseType;
  
  			var extendCount = 0;
  
  			if (baseType != null && baseType != typeof(object) && baseType != typeof(ValueType) && !baseType.IsPrimitive)
  			{
  				typeHtml.Write(" : ");
  
  				TypeInfo baseInfo;
  				m_Types.TryGetValue(baseType, out baseInfo);
  
  				if (baseInfo == null)
  				{
  					typeHtml.Write(baseType.Name);
  				}
  				else
  				{
  					typeHtml.Write("<!-- DBG-1 -->" + baseInfo.LinkName(null));
  				}
  
  				++extendCount;
  			}
  
  			if (ifaces.Length > 0)
  			{
  				if (extendCount == 0)
  				{
  					typeHtml.Write(" : ");
  				}
  
  				foreach (var iface in ifaces)
  				{
  					TypeInfo ifaceInfo;
  					m_Types.TryGetValue(iface, out ifaceInfo);
  
  					if (extendCount != 0)
  					{
  						typeHtml.Write(", ");
  					}
  
  					++extendCount;
  
  					if (ifaceInfo == null)
  					{
  						string typeName, fileName, linkName;
  						FormatGeneric(iface, out typeName, out fileName, out linkName);
  						linkName = linkName.Replace("@directory@", null);
  						typeHtml.Write("<!-- DBG-2.1 -->" + linkName);
  					}
  					else
  					{
  						typeHtml.Write("<!-- DBG-2.2 -->" + ifaceInfo.LinkName(null));
  					}
  				}
  			}
  
  			typeHtml.WriteLine("</h2>");
  
  			var derived = info.m_Derived;
  
  			if (derived != null)
  			{
  				typeHtml.Write("<h4>Derived Types: ");
  
  				derived.Sort(new TypeComparer());
  
  				for (var i = 0; i < derived.Count; ++i)
  				{
  					var derivedInfo = derived[i];
  
  					if (i != 0)
  					{
  						typeHtml.Write(", ");
  					}
  
  					//typeHtml.Write( "<a href=\"{0}\">{1}</a>", derivedInfo.m_FileName, derivedInfo.m_TypeName );
  					typeHtml.Write("<!-- DBG-3 -->" + derivedInfo.LinkName(null));
  				}
  
  				typeHtml.WriteLine("</h4>");
  			}
  
  			var nested = info.m_Nested;
  
  			if (nested != null)
  			{
  				typeHtml.Write("<h4>Nested Types: ");
  
  				nested.Sort(new TypeComparer());
  
  				for (var i = 0; i < nested.Count; ++i)
  				{
  					var nestedInfo = nested[i];
  
  					if (i != 0)
  					{
  						typeHtml.Write(", ");
  					}
  
  					//typeHtml.Write( "<a href=\"{0}\">{1}</a>", nestedInfo.m_FileName, nestedInfo.m_TypeName );
  					typeHtml.Write("<!-- DBG-4 -->" + nestedInfo.LinkName(null));
  				}
  
  				typeHtml.WriteLine("</h4>");
  			}
  
  			var membs =
  				type.GetMembers(
  					BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance |
  					BindingFlags.DeclaredOnly);
  
  			Array.Sort(membs, new MemberComparer());
  
  			foreach (var mi in membs)
  			{
  				if (mi is PropertyInfo)
  				{
  					WriteProperty((PropertyInfo)mi, typeHtml);
  				}
  				else if (mi is ConstructorInfo)
  				{
  					WriteCtor(info.TypeName, (ConstructorInfo)mi, typeHtml);
  				}
  				else if (mi is MethodInfo)
  				{
  					WriteMethod((MethodInfo)mi, typeHtml);
  				}
  			}
  		}
  
  		private static void WriteProperty(PropertyInfo pi, StreamWriter html)
  		{
  			html.Write("      ");
  
  			var getMethod = pi.GetGetMethod();
  			var setMethod = pi.GetSetMethod();
  
  			if ((getMethod != null && getMethod.IsStatic) || (setMethod != null && setMethod.IsStatic))
  			{
  				html.Write(StaticString);
  			}
  
  			html.Write(GetPair(pi.PropertyType, pi.Name, false));
  			html.Write('(');
  
  			if (pi.CanRead)
  			{
  				html.Write(GetString);
  			}
  
  			if (pi.CanWrite)
  			{
  				html.Write(SetString);
  			}
  
  			html.WriteLine(" )<br />");
  		}
  
  		private static void WriteCtor(string name, ConstructorInfo ctor, StreamWriter html)
  		{
  			if (ctor.IsStatic)
  			{
  				return;
  			}
  
  			html.Write("      ");
  			html.Write(CtorString);
  			html.Write(name);
  			html.Write('(');
  
  			var parms = ctor.GetParameters();
  
  			if (parms.Length > 0)
  			{
  				html.Write(' ');
  
  				for (var i = 0; i < parms.Length; ++i)
  				{
  					var pi = parms[i];
  
  					if (i != 0)
  					{
  						html.Write(", ");
  					}
  
  					if (pi.IsIn)
  					{
  						html.Write(InString);
  					}
  					else if (pi.IsOut)
  					{
  						html.Write(OutString);
  					}
  
  					html.Write(GetPair(pi.ParameterType, pi.Name, pi.IsOut));
  				}
  
  				html.Write(' ');
  			}
  
  			html.WriteLine(")<br />");
  		}
  
  		private static void WriteMethod(MethodInfo mi, StreamWriter html)
  		{
  			if (mi.IsSpecialName)
  			{
  				return;
  			}
  
  			html.Write("      ");
  
  			if (mi.IsStatic)
  			{
  				html.Write(StaticString);
  			}
  
  			if (mi.IsVirtual)
  			{
  				html.Write(VirtString);
  			}
  
  			html.Write(GetPair(mi.ReturnType, mi.Name, false));
  			html.Write('(');
  
  			var parms = mi.GetParameters();
  
  			if (parms.Length > 0)
  			{
  				html.Write(' ');
  
  				for (var i = 0; i < parms.Length; ++i)
  				{
  					var pi = parms[i];
  
  					if (i != 0)
  					{
  						html.Write(", ");
  					}
  
  					if (pi.IsIn)
  					{
  						html.Write(InString);
  					}
  					else if (pi.IsOut)
  					{
  						html.Write(OutString);
  					}
  
  					html.Write(GetPair(pi.ParameterType, pi.Name, pi.IsOut));
  				}
  
  				html.Write(' ');
  			}
  
  			html.WriteLine(")<br />");
  		}
  		#endregion
  
  		public static void FormatGeneric(Type type, out string typeName, out string fileName, out string linkName)
  		{
  			string name = null;
  			string fnam = null;
  			string link = null;
  
  			if (type.IsGenericType)
  			{
  				var index = type.Name.IndexOf('`');
  
  				if (index > 0)
  				{
  					var rootType = type.Name.Substring(0, index);
  
  					var nameBuilder = new StringBuilder(rootType);
  					var fnamBuilder = new StringBuilder("docs/types/" + SanitizeType(rootType));
  
  					var linkBuilder =
  						new StringBuilder(
  							DontLink(type)
  								? ("<span style=\"color: blue;\">" + rootType + "</span>")
  								: ("<a href=\"" + "@directory@" + rootType + "-T-.html\">" + rootType + "</a>"));
  
  					nameBuilder.Append("&lt;");
  					fnamBuilder.Append("-");
  					linkBuilder.Append("&lt;");
  
  					var typeArguments = type.GetGenericArguments();
  
  					for (var i = 0; i < typeArguments.Length; i++)
  					{
  						if (i != 0)
  						{
  							nameBuilder.Append(',');
  							fnamBuilder.Append(',');
  							linkBuilder.Append(',');
  						}
  
  						var sanitizedName = SanitizeType(typeArguments[i].Name);
  						var aliasedName = AliasForName(sanitizedName);
  
  						nameBuilder.Append(sanitizedName);
  						fnamBuilder.Append("T");
  
  						if (DontLink(typeArguments[i])) //if( DontLink( typeArguments[i].Name ) )
  						{
  							linkBuilder.Append("<span style=\"color: blue;\">" + aliasedName + "</span>");
  						}
  						else
  						{
  							linkBuilder.Append("<a href=\"" + "@directory@" + aliasedName + ".html\">" + aliasedName + "</a>");
  						}
  					}
  
  					nameBuilder.Append("&gt;");
  					fnamBuilder.Append("-");
  					linkBuilder.Append("&gt;");
  
  					name = nameBuilder.ToString();
  					fnam = fnamBuilder.ToString();
  					link = linkBuilder.ToString();
  				}
  			}
  
  			typeName = name ?? type.Name;
  
  
gt: 			if (fnam == null)