view Form1.cs @ 0:a70d6ce73a0e default tip

Initial commit under GPLv3
author IBBoard <dev@ibboard.co.uk>
date Sat, 06 Oct 2018 20:07:03 +0100
parents
children
line wrap: on
line source

// This file is a part of the Pattern Switcher app and is copyright 2006-2018 IBBoard.
//
// The file and the library/program it is in are licensed under the GNU GPL license, either version 3 of the License or (at your option) any later version. Please see COPYING for more information and the full license.
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;
using System.Xml;
using System.Text;
using System.Threading;

using IBBoard;
using IBBoard.IO;

///TODO: add error handling on malformed XML

namespace IBBoard.Relic.PatternSwitcher
{
	/// <summary>
	/// Summary description for Form1.
	/// </summary>
	public class Form1 : System.Windows.Forms.Form
	{
		private Preferences pref;
		private XmlDocument xmlSave = new XmlDocument();
		private System.Windows.Forms.Label lblNoAltPatterns;
		private System.Windows.Forms.Button bttnImport;
		private System.Windows.Forms.Button bttnImportMerge;
		private System.Windows.Forms.Button bttnRemove;
		private System.Windows.Forms.OpenFileDialog fdOpen;
		private System.Windows.Forms.SaveFileDialog fdSave;
		private System.Windows.Forms.Button bttnSave;
		private System.Windows.Forms.GroupBox groupBox1;
		private System.Windows.Forms.MainMenu mainMenu1;
		private System.Windows.Forms.MenuItem menuItem1;
		private System.Windows.Forms.MenuItem menuItem2;
		private System.Windows.Forms.MenuItem miAbout;
		private System.Windows.Forms.MenuItem miLoad;
		private System.Windows.Forms.MenuItem miRefresh;
		private System.Windows.Forms.MenuItem miExit;
		private System.Windows.Forms.Button bttnLaunchDoW;
		private readonly string noPatterns = "No alternate patterns found in {0}. Please check your configuration settings.";
		private readonly string noPatternsFolder = "Patterns folder {0} did not exist. Please check your configuration settings.";
		private System.Windows.Forms.TabControl tabControl;
		private System.Windows.Forms.MenuItem miConfig;
		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;


		private FrmLoadPattern loader = null;
		private FileInfo [] files;
		private FileInfo [] oldFiles;
		private bool delete = false;
		private string folder_from;
		private string folder_to;
		private System.Windows.Forms.MenuItem menuItem6;
		private System.Windows.Forms.MenuItem miHelp;
		private System.Windows.Forms.Button bttnLaunchWA;
		private System.Windows.Forms.MenuItem menuItem3;
		private System.Windows.Forms.GroupBox groupBox2;
		private System.Windows.Forms.Button bttnLaunchDC;
		private XmlNodeList savedNodes;
		private System.Windows.Forms.MenuItem menuItem8;
		private System.Windows.Forms.Button bttnLaunchSoulstorm;

		public delegate void ProgressUpdateDelegate(string message, bool completed);
		public delegate TabPage MappingMethodInvoker(FolderToArmyMapping mapping);
		public delegate void TabMethodInvoker(TabPage page);
		public delegate void ControlMethodInvoker(Control ctrl);

		public event ProgressUpdateDelegate ProgressUpdated;

		private Hashtable htTabs;

		#region Default stuff
		public Form1()
		{
			//
			// Required for Windows Form Designer support
			//
			InitializeComponent();
			htTabs = new Hashtable();
			pref = new Preferences("PatternSwitcher");
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(Form1));
			this.bttnImport = new System.Windows.Forms.Button();
			this.bttnImportMerge = new System.Windows.Forms.Button();
			this.tabControl = new System.Windows.Forms.TabControl();
			this.lblNoAltPatterns = new System.Windows.Forms.Label();
			this.bttnRemove = new System.Windows.Forms.Button();
			this.fdOpen = new System.Windows.Forms.OpenFileDialog();
			this.fdSave = new System.Windows.Forms.SaveFileDialog();
			this.bttnSave = new System.Windows.Forms.Button();
			this.groupBox1 = new System.Windows.Forms.GroupBox();
			this.mainMenu1 = new System.Windows.Forms.MainMenu();
			this.menuItem1 = new System.Windows.Forms.MenuItem();
			this.miLoad = new System.Windows.Forms.MenuItem();
			this.miRefresh = new System.Windows.Forms.MenuItem();
			this.menuItem8 = new System.Windows.Forms.MenuItem();
			this.miExit = new System.Windows.Forms.MenuItem();
			this.menuItem3 = new System.Windows.Forms.MenuItem();
			this.miConfig = new System.Windows.Forms.MenuItem();
			this.menuItem2 = new System.Windows.Forms.MenuItem();
			this.miHelp = new System.Windows.Forms.MenuItem();
			this.menuItem6 = new System.Windows.Forms.MenuItem();
			this.miAbout = new System.Windows.Forms.MenuItem();
			this.bttnLaunchDoW = new System.Windows.Forms.Button();
			this.bttnLaunchWA = new System.Windows.Forms.Button();
			this.groupBox2 = new System.Windows.Forms.GroupBox();
			this.bttnLaunchSoulstorm = new System.Windows.Forms.Button();
			this.bttnLaunchDC = new System.Windows.Forms.Button();
			this.groupBox1.SuspendLayout();
			this.groupBox2.SuspendLayout();
			this.SuspendLayout();
			// 
			// bttnImport
			// 
			this.bttnImport.Enabled = false;
			this.bttnImport.FlatStyle = System.Windows.Forms.FlatStyle.System;
			this.bttnImport.Location = new System.Drawing.Point(8, 16);
			this.bttnImport.Name = "bttnImport";
			this.bttnImport.Size = new System.Drawing.Size(128, 23);
			this.bttnImport.TabIndex = 0;
			this.bttnImport.Text = "Import";
			this.bttnImport.Click += new System.EventHandler(this.bttnImport_Click);
			// 
			// bttnImportMerge
			// 
			this.bttnImportMerge.Enabled = false;
			this.bttnImportMerge.FlatStyle = System.Windows.Forms.FlatStyle.System;
			this.bttnImportMerge.Location = new System.Drawing.Point(160, 16);
			this.bttnImportMerge.Name = "bttnImportMerge";
			this.bttnImportMerge.Size = new System.Drawing.Size(128, 24);
			this.bttnImportMerge.TabIndex = 1;
			this.bttnImportMerge.Text = "Import and Merge";
			this.bttnImportMerge.Click += new System.EventHandler(this.bttnImportMerge_Click);
			// 
			// tabControl
			// 
			this.tabControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
				| System.Windows.Forms.AnchorStyles.Left) 
				| System.Windows.Forms.AnchorStyles.Right)));
			this.tabControl.Location = new System.Drawing.Point(8, 8);
			this.tabControl.Name = "tabControl";
			this.tabControl.SelectedIndex = 0;
			this.tabControl.Size = new System.Drawing.Size(704, 200);
			this.tabControl.TabIndex = 2;
			this.tabControl.Tag = "";
			this.tabControl.SelectedIndexChanged += new System.EventHandler(this.tabControl_SelectedIndexChanged);
			// 
			// lblNoAltPatterns
			// 
			this.lblNoAltPatterns.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
			this.lblNoAltPatterns.Location = new System.Drawing.Point(8, 8);
			this.lblNoAltPatterns.Name = "lblNoAltPatterns";
			this.lblNoAltPatterns.Size = new System.Drawing.Size(704, 200);
			this.lblNoAltPatterns.TabIndex = 3;
			this.lblNoAltPatterns.Text = "No alternate patterns found in {path}. Please check your configuration settings.";
			// 
			// bttnRemove
			// 
			this.bttnRemove.FlatStyle = System.Windows.Forms.FlatStyle.System;
			this.bttnRemove.Location = new System.Drawing.Point(8, 48);
			this.bttnRemove.Name = "bttnRemove";
			this.bttnRemove.Size = new System.Drawing.Size(128, 23);
			this.bttnRemove.TabIndex = 6;
			this.bttnRemove.Text = "Remove Imported";
			this.bttnRemove.Click += new System.EventHandler(this.bttnRemove_Click);
			// 
			// fdOpen
			// 
			this.fdOpen.AddExtension = false;
			this.fdOpen.DefaultExt = "dwp";
			this.fdOpen.Filter = "Dawn of War Pattern Saves (dwp)|*.dwp";
			// 
			// fdSave
			// 
			this.fdSave.DefaultExt = "dwp";
			this.fdSave.Filter = "Dawn of War Pattern Saves (dwp)|*.dwp";
			// 
			// bttnSave
			// 
			this.bttnSave.FlatStyle = System.Windows.Forms.FlatStyle.System;
			this.bttnSave.Location = new System.Drawing.Point(160, 48);
			this.bttnSave.Name = "bttnSave";
			this.bttnSave.Size = new System.Drawing.Size(128, 23);
			this.bttnSave.TabIndex = 9;
			this.bttnSave.Text = "Save loaded patterns";
			this.bttnSave.Click += new System.EventHandler(this.bttnSave_Click);
			// 
			// groupBox1
			// 
			this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
			this.groupBox1.Controls.Add(this.bttnImport);
			this.groupBox1.Controls.Add(this.bttnImportMerge);
			this.groupBox1.Controls.Add(this.bttnRemove);
			this.groupBox1.Controls.Add(this.bttnSave);
			this.groupBox1.Location = new System.Drawing.Point(8, 216);
			this.groupBox1.Name = "groupBox1";
			this.groupBox1.Size = new System.Drawing.Size(296, 80);
			this.groupBox1.TabIndex = 10;
			this.groupBox1.TabStop = false;
			this.groupBox1.Text = "Race Actions";
			// 
			// mainMenu1
			// 
			this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
																					  this.menuItem1,
																					  this.menuItem3,
																					  this.menuItem2});
			// 
			// menuItem1
			// 
			this.menuItem1.Index = 0;
			this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
																					  this.miLoad,
																					  this.miRefresh,
																					  this.menuItem8,
																					  this.miExit});
			this.menuItem1.Text = "&File";
			// 
			// miLoad
			// 
			this.miLoad.Index = 0;
			this.miLoad.Text = "&Load patterns";
			this.miLoad.Click += new System.EventHandler(this.miLoad_Click);
			// 
			// miRefresh
			// 
			this.miRefresh.Index = 1;
			this.miRefresh.Text = "&Refresh list";
			this.miRefresh.Click += new System.EventHandler(this.miRefresh_Click);
			// 
			// menuItem8
			// 
			this.menuItem8.Index = 2;
			this.menuItem8.Text = "-";
			// 
			// miExit
			// 
			this.miExit.Index = 3;
			this.miExit.Text = "E&xit";
			this.miExit.Click += new System.EventHandler(this.miExit_Click);
			// 
			// menuItem3
			// 
			this.menuItem3.Index = 1;
			this.menuItem3.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
																					  this.miConfig});
			this.menuItem3.Text = "&Edit";
			// 
			// miConfig
			// 
			this.miConfig.Index = 0;
			this.miConfig.Text = "&Configuration";
			this.miConfig.Click += new System.EventHandler(this.miConfig_Click);
			// 
			// menuItem2
			// 
			this.menuItem2.Index = 2;
			this.menuItem2.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
																					  this.miHelp,
																					  this.menuItem6,
																					  this.miAbout});
			this.menuItem2.Text = "&Help";
			// 
			// miHelp
			// 
			this.miHelp.Index = 0;
			this.miHelp.Text = "Help";
			this.miHelp.Click += new System.EventHandler(this.miHelp_Click);
			// 
			// menuItem6
			// 
			this.menuItem6.Index = 1;
			this.menuItem6.Text = "-";
			// 
			// miAbout
			// 
			this.miAbout.Index = 2;
			this.miAbout.Text = "&About";
			this.miAbout.Click += new System.EventHandler(this.miAbout_Click);
			// 
			// bttnLaunchDoW
			// 
			this.bttnLaunchDoW.Image = ((System.Drawing.Image)(resources.GetObject("bttnLaunchDoW.Image")));
			this.bttnLaunchDoW.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
			this.bttnLaunchDoW.Location = new System.Drawing.Point(16, 24);
			this.bttnLaunchDoW.Name = "bttnLaunchDoW";
			this.bttnLaunchDoW.Size = new System.Drawing.Size(128, 40);
			this.bttnLaunchDoW.TabIndex = 11;
			this.bttnLaunchDoW.Text = "Dawn of War";
			this.bttnLaunchDoW.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
			this.bttnLaunchDoW.Click += new System.EventHandler(this.bttnLaunchDoW_Click);
			// 
			// bttnLaunchWA
			// 
			this.bttnLaunchWA.Image = ((System.Drawing.Image)(resources.GetObject("bttnLaunchWA.Image")));
			this.bttnLaunchWA.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
			this.bttnLaunchWA.Location = new System.Drawing.Point(160, 24);
			this.bttnLaunchWA.Name = "bttnLaunchWA";
			this.bttnLaunchWA.Size = new System.Drawing.Size(128, 40);
			this.bttnLaunchWA.TabIndex = 12;
			this.bttnLaunchWA.Text = "Winter Assault";
			this.bttnLaunchWA.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
			this.bttnLaunchWA.Click += new System.EventHandler(this.bttnLaunchWA_Click);
			// 
			// groupBox2
			// 
			this.groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
			this.groupBox2.Controls.Add(this.bttnLaunchSoulstorm);
			this.groupBox2.Controls.Add(this.bttnLaunchDC);
			this.groupBox2.Controls.Add(this.bttnLaunchWA);
			this.groupBox2.Controls.Add(this.bttnLaunchDoW);
			this.groupBox2.Location = new System.Drawing.Point(408, 216);
			this.groupBox2.Name = "groupBox2";
			this.groupBox2.Size = new System.Drawing.Size(304, 120);
			this.groupBox2.TabIndex = 13;
			this.groupBox2.TabStop = false;
			this.groupBox2.Text = "Launch";
			// 
			// bttnLaunchSoulstorm
			// 
			this.bttnLaunchSoulstorm.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
			this.bttnLaunchSoulstorm.Location = new System.Drawing.Point(160, 72);
			this.bttnLaunchSoulstorm.Name = "bttnLaunchSoulstorm";
			this.bttnLaunchSoulstorm.Size = new System.Drawing.Size(128, 40);
			this.bttnLaunchSoulstorm.TabIndex = 14;
			this.bttnLaunchSoulstorm.Text = "Soulstorm";
			this.bttnLaunchSoulstorm.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
			this.bttnLaunchSoulstorm.Click += new System.EventHandler(this.bttnLaunchSoulstorm_Click);
			// 
			// bttnLaunchDC
			// 
			this.bttnLaunchDC.Image = ((System.Drawing.Image)(resources.GetObject("bttnLaunchDC.Image")));
			this.bttnLaunchDC.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
			this.bttnLaunchDC.Location = new System.Drawing.Point(16, 72);
			this.bttnLaunchDC.Name = "bttnLaunchDC";
			this.bttnLaunchDC.Size = new System.Drawing.Size(128, 40);
			this.bttnLaunchDC.TabIndex = 13;
			this.bttnLaunchDC.Text = "Dark Crusade";
			this.bttnLaunchDC.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
			this.bttnLaunchDC.Click += new System.EventHandler(this.bttnLaunchDC_Click);
			// 
			// Form1
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
			this.ClientSize = new System.Drawing.Size(720, 341);
			this.Controls.Add(this.groupBox2);
			this.Controls.Add(this.groupBox1);
			this.Controls.Add(this.tabControl);
			this.Controls.Add(this.lblNoAltPatterns);
			this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
			this.Menu = this.mainMenu1;
			this.MinimumSize = new System.Drawing.Size(710, 372);
			this.Name = "Form1";
			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
			this.Tag = "Space_Marines";
			this.Text = "Dawn of War Pattern Switcher";
			this.Load += new System.EventHandler(this.Form1_Load);
			this.groupBox1.ResumeLayout(false);
			this.groupBox2.ResumeLayout(false);
			this.ResumeLayout(false);

		}
		#endregion

		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main(string[] args) 
		{
			Application.EnableVisualStyles();

			try
			{
				Application.Run(new Form1());
			}
			catch (InvalidFileException)
			{
				MessageBox.Show(null, "Could not load preferences file - file was not valid. Please try specifying another preferences file.", "Invalid preferences for Pattern Switcher", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
			catch (Exception ex)
			{
				MessageBox.Show(null, ex.Message, "Pattern Switcher - Major Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}
		#endregion

		private void Form1_Load(object sender, System.EventArgs e)
		{		
			setConfig();
			initialise();
		}

		private void initialise()
		{
			initialise(true);
		}

		private void initialise(bool recreateTabs)
		{
			loadCurrentPatternsXML();

			if (recreateTabs)
			{
				createTabs();
			}

			fillLists();
			showAllInstalled();
		}

		private void reinitialise()
		{
			initialise(false);
		}

		private void setConfig()
		{
			string dowPath = pref["DoWPath"].ToString();

			bttnLaunchDoW.Enabled = File.Exists(dowPath+getDoWAppName());
			bttnLaunchWA.Enabled = File.Exists(dowPath+getWAAppName());
			bttnLaunchDC.Enabled = File.Exists(dowPath+getDCAppName());
			bttnLaunchSoulstorm.Enabled = File.Exists(dowPath+getSoulstormAppName());

			fdOpen.InitialDirectory = getPatternPath();
			fdSave.InitialDirectory = getPatternPath();

			//Look for old version's pattern lists and move them to the new location
			if (File.Exists(getPatternPath()+"current_patterns.xml") && !File.Exists(getCurrentlyLoadedPath()))
			{
				File.Move(getPatternPath()+"current_patterns.xml", getCurrentlyLoadedPath());
			}
			else if (File.Exists(getPatternPath()+"current_patterns_dow.xml") && !File.Exists(getCurrentlyLoadedPath()))
			{
				File.Move(getPatternPath()+"current_patterns_dow.xml", getCurrentlyLoadedPath());
			}
			else if (File.Exists(getPatternPath()+"current_patterns_dc.xml") && !File.Exists(getCurrentlyLoadedPath()))
			{
				File.Move(getPatternPath()+"current_patterns_dc.xml", getCurrentlyLoadedPath());
			}
		}

		private void loadCurrentPatternsXML()
		{
			string xmlPath = getCurrentlyLoadedPath();

			if (File.Exists(xmlPath))
			{
				try
				{
					xmlSave.Load(xmlPath);
				}
				catch
				{
					DialogResult dr = MessageBox.Show(this, "The list of current patterns has become corrupt. Do you want to clear the list completely?","Corrupted File",MessageBoxButtons.YesNo,MessageBoxIcon.Error);

					if (dr==DialogResult.Yes)
					{
						xmlSave.LoadXml("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<!DOCTYPE races[\n  <!ELEMENT race (file)> \n  <!ELEMENT file ANY> \n  <!ATTLIST file id ID #REQUIRED>\n  <!ATTLIST race id ID #REQUIRED>\n  <!ATTLIST file folder CDATA #REQUIRED>\n  <!ATTLIST file date CDATA #REQUIRED>\n  <!ATTLIST file filesize CDATA #REQUIRED>\n]>\n<races>\n</races>");
						xmlSave.Save(xmlPath);
					}
					else
					{
						Close();
						return;
					}
				}
			}
			else
			{
				xmlSave.LoadXml("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<!DOCTYPE races[\n  <!ELEMENT race (file)> \n  <!ELEMENT file ANY> \n  <!ATTLIST file id ID #REQUIRED>\n  <!ATTLIST race id ID #REQUIRED>\n  <!ATTLIST file folder CDATA #REQUIRED>\n  <!ATTLIST file date CDATA #REQUIRED>\n  <!ATTLIST file filesize CDATA #REQUIRED>\n]>\n<races>\n</races>");
			}
		}

		private void fillLists()
		{
			Thread thr = new Thread(new ThreadStart(thrFillLists));
			thr.Start();
		}

		private void thrFillLists()
		{
			DirectoryInfo dir = null;
			bool raceWorked = false;
			bool worked = false;
			TabPage tab;

			if (Directory.Exists(getPatternPath()))
			{
				ArrayList mappings = new ArrayList(getCurrentFolderMappings());

				//Start by removing all of the old and unnecesary ones
				ArrayList vals = new ArrayList(htTabs.Values);

				foreach (TabPage page in vals)
				{
					FolderToArmyMapping mapping = (FolderToArmyMapping)page.Tag;
					bool found = false;
					
					foreach (FolderToArmyMapping map in mappings)
					{
						if (mapping.FolderName.Equals(map.FolderName))
						{
							found = true;
							break;
						}
					}

					if (!found)
					{
						removeTab(page);
					}
				}

				//Then update the valid ones
				foreach (FolderToArmyMapping mapping in mappings)
				{
					string key = mapping.FolderName;
					dir = new DirectoryInfo(getPatternPath()+key);
					tab = (TabPage)htTabs[key];

					if (!dir.Exists)
					{
						//If there is a mapping but no folder exists then don't show it
						removeTab(tab);
					}
					else
					{
						if (tab==null)
						{
							//If there is a mapping and a folder but no tab then the mapping has been newly added - add the tab
							tab = (TabPage)Invoke(new MappingMethodInvoker(createTab), new object[]{mapping});
						}

						//Update the list
						if (tab.Text!=mapping.ArmyName)
						{
							tab.Invoke(new PropertySetterInvoker(tab.GetType().GetProperty("Text").SetValue), new object[]{tab, mapping.ArmyName, null});
						}

						raceWorked = fillList(tab, getList(tab), dir);
						worked = worked | raceWorked;
					}
				}

				if (!worked)
				{
					this.Invoke(new MethodInvoker(noPatternsInFolders));
				}
				else
				{
					this.Invoke(new MethodInvoker(patternsInFolders));
				}
			}
			else
			{
				this.Invoke(new MethodInvoker(noPatternFolder));
			}
		}

		private void noPatternsInFolders()
		{
			lblNoAltPatterns.Visible = true;
			lblNoAltPatterns.Text = String.Format(noPatterns, getPatternPath());
			tabControl.Visible = false;
			groupBox1.Enabled = false;
			miLoad.Enabled = false;
		}

		private void noPatternFolder()
		{
			lblNoAltPatterns.Visible = true;
			lblNoAltPatterns.Text = String.Format(noPatternsFolder, getPatternPath());
			tabControl.Visible = false;
			groupBox1.Enabled = false;
			miLoad.Enabled = false;
		}

		private void patternsInFolders()
		{
			lblNoAltPatterns.Visible = false;
			tabControl.Visible = true;
			groupBox1.Enabled = true;
			miLoad.Enabled = true;
		}

		private bool fillList(TabPage tab, ListBox list, DirectoryInfo dir)
		{
			bool worked = false;

			if (dir.Exists)
			{
				DirectoryInfo [] subdir = dir.GetDirectories();
				PatternFolder fld = null;

				if (subdir.Length>0)
				{
					ArrayList arr = new ArrayList();

					for (int i = 0; i<subdir.Length;i++)
					{
						if ((subdir[i].GetFiles("*.wtp").Length+subdir[i].GetFiles("*.rsh").Length)>0)
						{
							fld = new PatternFolder(subdir[i].Name);
							arr.Add(fld);
							worked = true;
						}
					}

					arr.Sort();
					//list.SuspendLayout();
					list.Invoke(new PropertySetterInvoker(list.GetType().GetProperty("DataSource").SetValue), new object[]{list, arr, null});
					//list.DataSource = arr;
					//list.ResumeLayout();
				}
			}

			tab.ForeColor = (worked ? SystemColors.ControlText : SystemColors.GrayText);
			return worked;
		}

		private void showAllInstalled()
		{
			foreach (string key in htTabs.Keys)
			{
				if (tabControl.Contains((TabPage)htTabs[key]))
				{
					showInstalled(key, getDataGrid((TabPage)htTabs[key]));
				}
			}
		}
		
		private void showInstalled(string key, DataGrid dg)
		{
			showInstalled(xmlSave.GetElementById(key), dg);
		}

		private void showInstalled(XmlElement xmle, DataGrid dg)
		{
			DataTable dt = new DataTable("loaded");

			if (xmle!=null && xmle.ChildNodes.Count!=0)
			{
				
				dt.Columns.Add(new DataColumn("File"));
				dt.Columns["File"].DataType = System.Type.GetType("System.String");
				dt.Columns.Add(new DataColumn("Type"));
				dt.Columns["Type"].DataType = System.Type.GetType("System.String");
				dt.Columns.Add(new DataColumn("Loaded From"));
				dt.Columns["Loaded From"].DataType = System.Type.GetType("System.String");

				DataRow dr = null;

				for (int i = 0; i<xmle.ChildNodes.Count; i++)
				{
					string innerText = xmle.ChildNodes[i].InnerText;
					dr = dt.NewRow();

					dr["File"] = innerText.Replace("_default.wtp","").Replace(".rsh","");
					dr["Type"] = (innerText.ToLower().EndsWith(".rsh"))?"RSH":"WTP";
					dr["Loaded From"] = xmle.ChildNodes[i].Attributes["folder"].Value.Replace("_"," ");

					dt.Rows.Add(dr);
				}

				dt.DefaultView.Sort = "File";
				dg.DataSource = dt;
				dg.TableStyles.Clear();
				DataGridTableStyle style = new DataGridTableStyle();
				style.MappingName = "loaded";
				dg.TableStyles.Add(style);
				dg.TableStyles["loaded"].GridColumnStyles["File"].Width = 215;
				dg.TableStyles["loaded"].GridColumnStyles["Type"].Width = 40;
				dg.TableStyles["loaded"].GridColumnStyles["Loaded From"].Width = 215;
				dg.TableStyles["loaded"].ReadOnly = true;
				dg.TableStyles["loaded"].RowHeadersVisible = false;
				dg.TableStyles["loaded"].RowHeaderWidth = 0;
			}
			else
			{
				dg.DataSource = dt;
			}
		}

		private void bttnImport_Click(object sender, System.EventArgs e)
		{
			import(true);
		}

		private void bttnImportMerge_Click(object sender, System.EventArgs e)
		{
			import(false);			
		}

		private void import(bool doDelete)
		{
			delete = doDelete;

			folder_to = getGamePath()+ @"W40k\Data\art\ebps\races\"+getCurrentRaceMapping().FolderName.ToLower()+@"\texture_share\";
			folder_from = getPatternPath()+currentArmyPatternPath();

			if (!Directory.Exists(folder_to))
			{
				Directory.CreateDirectory(folder_to);
			}

			DirectoryInfo dir = new DirectoryInfo(folder_from);
			DirectoryInfo newDir = new DirectoryInfo(folder_to);

			FileInfo [] filesWTP = dir.GetFiles("*.wtp");
			FileInfo [] filesRSH = dir.GetFiles("*.rsh");
			files = new FileInfo[filesWTP.Length+filesRSH.Length];

			filesWTP.CopyTo(files,0);

			for (int i = 0; i<filesRSH.Length; i++)
			{
				files[i+filesWTP.Length] = filesRSH[i];
			}

			FileInfo [] oldFilesWTP = newDir.GetFiles("*.wtp");
			FileInfo [] oldFilesRSH = newDir.GetFiles("*.rsh");
			oldFiles = new FileInfo[oldFilesWTP.Length+oldFilesRSH.Length];

			oldFilesWTP.CopyTo(oldFiles,0);

			for (int i = 0; i<oldFilesRSH.Length; i++)
			{
				oldFiles[i+oldFilesWTP.Length] = oldFilesRSH[i];
			}

			loader = new FrmLoadPattern(this, files.Length+oldFiles.Length);

			Thread thr = new Thread(new ThreadStart(thrImport));
			thr.Start();
			
			loader.ShowDialog(this);
			
			showAllInstalled();
		}

		public string currentArmyPatternPath()
		{
			return getCurrentRaceMapping().FolderName+"\\"+currentFolderName()+"\\";
		}

		public string currentFolderName()
		{
			ListBox list = getList(tabControl.SelectedTab);

			return (list == null)?"":((PatternFolder)list.SelectedItem).RealName;
		}

		public FolderToArmyMapping getCurrentRaceMapping()
		{
			return getRaceMapping(tabControl.SelectedTab);
		}

		public FolderToArmyMapping getRaceMapping(TabPage tab)
		{
			if (tab !=null)
			{
				return (FolderToArmyMapping)tab.Tag;
			}

			return null;
		}

		public FolderToArmyMapping[] getCurrentFolderMappings()
		{
			return FolderToArmyMapping.CreateCollectionFromString((string)pref["ArmyMappings"]);
		}

		private void list_SelectedIndexChanged(object sender, EventArgs e)
		{
			if (sender is ListBox)
			{
				bttnImport.Enabled = (((ListBox)sender).SelectedIndex!=-1);
				bttnImportMerge.Enabled = (((ListBox)sender).SelectedIndex!=-1);
			}
		}

		private void tabControl_SelectedIndexChanged(object sender, System.EventArgs e)
		{
			setImportRemoveButtonEnabled();
		}

		private void setImportRemoveButtonEnabled()
		{
			FolderToArmyMapping mapping = getCurrentRaceMapping();

			if (mapping!=null)
			{
				bttnImport.Enabled = (getList(tabControl.SelectedTab).SelectedIndex!=-1);
				bttnImportMerge.Enabled = (getList(tabControl.SelectedTab).SelectedIndex!=-1);

				XmlElement xmle = xmlSave.GetElementById(mapping.FolderName);
					
				if (xmle!=null && xmle.ChildNodes.Count>0)
				{
					bttnRemove.Enabled = true;
					bttnSave.Enabled = true;
				}
				else
				{
					bttnRemove.Enabled = false;
					bttnSave.Enabled = false;
				}
			}
			else
			{
				bttnRemove.Enabled = false;
				bttnSave.Enabled = false;
				bttnImport.Enabled = false;
				bttnImportMerge.Enabled = false;
			}
		}

		private void bttnRemove_Click(object sender, System.EventArgs e)
		{
			string folder_from = getGamePath()+ @"W40k\Data\art\ebps\races\"+getCurrentRaceMapping().FolderName+@"\texture_share\";

			DirectoryInfo dir = new DirectoryInfo(folder_from);

			FileInfo [] filesWTP = dir.GetFiles("*.wtp");
			FileInfo [] filesRSH = dir.GetFiles("*.rsh");
			oldFiles = new FileInfo[filesWTP.Length+filesRSH.Length];
			
			filesWTP.CopyTo(oldFiles,0);

			for (int i = 0; i<filesRSH.Length; i++)
			{
				oldFiles[i+filesWTP.Length] = filesRSH[i];
			}

			if (oldFiles.Length>0)
			{
				DialogResult dr = MessageBox.Show(this,"Are you sure you want to delete all currently installed patterns for this race?", "Delete all?",MessageBoxButtons.YesNo,MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2);

				if (dr == DialogResult.Yes)
				{
					loader = new FrmLoadPattern(this, oldFiles.Length);

					Thread thr = new Thread(new ThreadStart(thrRemove));
					thr.Start();
			
					loader.ShowDialog(this);
			
					showAllInstalled();

					bttnRemove.Enabled = false;
					bttnSave.Enabled = false;
				}
			}
		}

		private void bttnRefresh_Click(object sender, System.EventArgs e)
		{
			fillLists();
		}

		public void AddNodeAttribute(XmlNode node, string attribName, object attribValue)
		{
			AddNodeAttribute(node,attribName,attribValue,xmlSave);
		}

		public void AddNodeAttribute(XmlNode node, string attribName, object attribValue, XmlDocument docFrom)
		{
			try
			{
				XmlAttribute att = docFrom.CreateAttribute(attribName);
				att.Value = attribValue.ToString();
				node.Attributes.Append(att);
			}
			catch{}
		}

		private void loadSaved(string pathToLoad)
		{
			XmlDocument saved = new XmlDocument();
			try
			{
				saved.Load(pathToLoad);
			}
			catch
			{
				MessageBox.Show(this,"ERROR: invalid save file or file corrupt","Loading Error",MessageBoxButtons.OK,MessageBoxIcon.Exclamation);
				return;
			}

			savedNodes = saved.LastChild.FirstChild.ChildNodes;
	
			string race = saved.LastChild.FirstChild.Attributes["race"].Value;
			fillLists();
			selectTab(race);
			string sl = Constants.DirectoryString;
			folder_to = getGamePath() + "W40k" + sl + "Data" + sl + "art" + sl + "ebps" + sl + "races" + sl + race + sl + "texture_share" + sl;
			folder_from = getPatternPath() + race + sl;

			if (!Directory.Exists(folder_to))
			{
				Directory.CreateDirectory(folder_to);
			}

			DirectoryInfo newDir = new DirectoryInfo(folder_to);

			oldFiles = newDir.GetFiles();

			loader = new FrmLoadPattern(this, oldFiles.Length+(savedNodes.Count*2));
			
			Thread thr = new Thread(new ThreadStart(thrLoadSaved));
			thr.Start();
			
			loader.ShowDialog(this);

			showAllInstalled();
		}
		
		private void saveLoaded(string savePath)
		{
			XmlDocument newSave = new XmlDocument();

			if (!savePath.EndsWith(".dwp"))
			{
				savePath+=".dwp";
			}

			XmlElement xmle = xmlSave.GetElementById(getCurrentRaceMapping().FolderName);
			if (xmle==null)
			{
				return;
			}

			XmlNode files = null;
			if (File.Exists(savePath))
			{
				try
				{
					newSave.Load(savePath);
					files = newSave.LastChild.FirstChild;
					files.InnerXml = "";
					files.Attributes["race"].Value = getCurrentRaceMapping().FolderName;
				}
				catch
				{
					newSave.LoadXml("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<!DOCTYPE save[\n  <!ELEMENT files (file)> \n  <!ELEMENT file ANY> \n  <!ATTLIST files race ID #REQUIRED>\n  <!ATTLIST file id ID #REQUIRED>\n  <!ATTLIST file folder CDATA #REQUIRED>\n  ]>\n<save>\n  <files race=\""+getCurrentRaceMapping().FolderName+"\"></files></save>");
					files = newSave.LastChild.FirstChild;
				}
			}
			else
			{
				newSave.LoadXml("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<!DOCTYPE save[\n  <!ELEMENT files (file)> \n  <!ELEMENT file ANY> \n  <!ATTLIST files race ID #REQUIRED>\n  <!ATTLIST file id ID #REQUIRED>\n  <!ATTLIST file folder CDATA #REQUIRED>\n  ]>\n<save>\n  <files race=\""+getCurrentRaceMapping().FolderName+"\"></files></save>");
				files = newSave.LastChild.FirstChild;
			}

			XmlNode node= null;

			for (int i = 0; i < xmle.ChildNodes.Count; i++)
			{
				node = newSave.CreateNode(XmlNodeType.Element,"file","");
				node.InnerText = xmle.ChildNodes[i].InnerText;
				AddNodeAttribute(node,"id",xmle.ChildNodes[i].Attributes["id"].Value, newSave);
				AddNodeAttribute(node,"folder",xmle.ChildNodes[i].Attributes["folder"].Value, newSave);
				files.AppendChild(node);
			}

			newSave.Save(savePath);
			
		}

		private void bttnLoad_Click(object sender, System.EventArgs e)
		{
			DialogResult dr = fdOpen.ShowDialog();
			if (dr==DialogResult.OK)
			{
				loadSaved(fdOpen.FileName);
			}
		}

		private void bttnSave_Click(object sender, System.EventArgs e)
		{
			XmlElement xmle = xmlSave.GetElementById(getCurrentRaceMapping().FolderName);
			if (xmle.ChildNodes.Count>0)
			{
				DialogResult dr = fdSave.ShowDialog();
				if (dr==DialogResult.OK)
				{
					saveLoaded(fdSave.FileName);
				}
			}
		}

		private void miLoad_Click(object sender, System.EventArgs e)
		{
			DialogResult dr = fdOpen.ShowDialog();
			if (dr==DialogResult.OK)
			{
				loadSaved(fdOpen.FileName);
			}
		}

		private void miRefresh_Click(object sender, System.EventArgs e)
		{
			fillLists();		
		}

		private void miExit_Click(object sender, System.EventArgs e)
		{
			Close();
		}

		private void miAbout_Click(object sender, System.EventArgs e)
		{
			string version = Application.ProductVersion.Substring(0, Application.ProductVersion.LastIndexOf('.'));

			if (version.EndsWith(".0"))
			{
				version = version.Substring(0, version.Length-2);
			}

			MessageBox.Show(this,
				"Dawn of War Pattern Switcher:\r\nCreated by IBBoard for Skins @ Hive World Terra\r\n(http://skins.hiveworldterra.co.uk/)\r\nVersion "+version,
				"About Pattern Switcher",MessageBoxButtons.OK);
		}

		private void miConfig_Click(object sender, System.EventArgs e)
		{
			FrmConfig cfg = new FrmConfig(pref);
			DialogResult dr = cfg.ShowDialog(this);

			if (dr == DialogResult.OK)
			{
				setConfig();
				reinitialise();
			}
		}

		#region threadable methods
		private void thrImport()
		{
			Thread.Sleep(250);
			Hashtable htExisting = new Hashtable();

			bool mismatch = false;

			XmlNode node = xmlSave.GetElementById(getCurrentRaceMapping().FolderName);

			if (oldFiles.Length>0)
			{
				if (delete)
				{
					if (node!=null)
					{
						node.InnerXml = "";
					}

					ProgressUpdated("Deleting previous patterns for race...", false);

					for (int i = 0; i<oldFiles.Length; i++)
					{
						oldFiles[i].Delete();
						ProgressUpdated(null,false);
					}
				}
				else
				{
					XmlNode filenode = null;
					ProgressUpdated("Reading existing files...",false);
					
					for (int i = 0; i<oldFiles.Length; i++)
					{
						filenode = xmlSave.GetElementById(getCurrentRaceMapping().FolderName+"_"+oldFiles[i].Name.ToLower());
						if (filenode==null)
						{
							mismatch = true;
						}
						else
						{
							string temp = filenode.Attributes["filesize"].Value;
							if (oldFiles[i].Length != int.Parse(filenode.Attributes["filesize"].Value))
								mismatch = true;
						}

						htExisting.Add(oldFiles[i].Name.ToLower(),oldFiles[i]);
					
						ProgressUpdated(null,false);
					}
				}
			}

			if (mismatch)
			{
				DialogResult dr = MessageBox.Show(this, "There was a mismatch between the existing file and the file that was expected. Do you want to continue?", "File Mismatch", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2);

				if (dr!=DialogResult.Yes)
				{
					ProgressUpdated("Import stopped by user.",true);
					return;
				}
				else
				{
					//fake it so we don't get prompted - they said they didn't care
					delete = true;
				}
			}

			XmlAttribute att = null;
			if (node==null)
			{
				node = xmlSave.CreateNode(XmlNodeType.Element,"race","");
				node.Attributes.RemoveAll();
				att = xmlSave.CreateAttribute("id");
				att.Value = getCurrentRaceMapping().FolderName;
				node.Attributes.Append(att);
				xmlSave.LastChild.AppendChild(node);
			}

			XmlNode fileNode = null;

			ProgressUpdated("Importing pattern files...", false);

			bool existed = false;
			int filesize = 0;
			string foldername = "";
			string newFolder = currentFolderName();
			for (int i = 0; i<files.Length; i++)
			{
				fileNode = xmlSave.GetElementById(getCurrentRaceMapping().FolderName+"_"+files[i].Name.ToLower());

				if (fileNode==null)
				{
					existed = false;
					filesize = 0;
					foldername = "UNKNOWN";
					fileNode = xmlSave.CreateNode(XmlNodeType.Element,"file","");
				}
				else 
				{
					filesize = int.Parse(fileNode.Attributes["filesize"].Value);
					foldername = fileNode.Attributes["folder"].Value;
					existed = true;
				}

				bool copy = false;

				if (delete)
				{
					copy = true;
				}
				else if (htExisting.ContainsKey(files[i].Name.ToLower()) && (files[i].Length != filesize || newFolder != foldername))
				{
					//if we're not deleteing and the file already exists and either the file size or the folder is different then prompt the user
					DialogResult dr = MessageBox.Show(this,
						"Overwrite '"+((FileInfo)htExisting[files[i].Name.ToLower()]).Name.ToLower()+"'?\r\n\r\n"+
						"Current:\t"+foldername+" - "+Math.Round((filesize/1024.0),1)+"KB\r\n"+
						"New:\t"+newFolder+" - "+Math.Round((files[i].Length/1024.0),1)+"KB\r\n\r\n"+
						"Note: doing this may cause ugly results due to incompatible parts of patterns. A full \"Import\" is recommended to remove the files first.",
						"File already Exists!",
						MessageBoxButtons.YesNo,
						MessageBoxIcon.Exclamation,
						MessageBoxDefaultButton.Button2);
					copy = (dr == DialogResult.Yes);
				}
				else
				{
					copy = true;
				}

				if (copy)
				{
					try
					{
						files[i].CopyTo(folder_to+files[i].Name.ToLower(),true);
							
						if (!existed)
							node.AppendChild(fileNode);

						fileNode.Attributes.RemoveAll();
						AddNodeAttribute(fileNode,"id",getCurrentRaceMapping().FolderName+"_"+files[i].Name.ToLower());
						AddNodeAttribute(fileNode,"folder",currentFolderName());
						AddNodeAttribute(fileNode,"filesize",files[i].Length);
						AddNodeAttribute(fileNode,"date",DateTime.Now);
						fileNode.InnerText = files[i].Name.ToLower();
					}							
					catch (System.Exception ex)
					{
						string exm = ex.Message;
					}
				}

				ProgressUpdated(null,false);
			}
			xmlSave.Save(getCurrentlyLoadedPath());

			ProgressUpdated("Completed.",true);

			bttnRemove.Enabled = true;
			bttnSave.Enabled = true;
		}

		private void thrRemove()
		{
			Thread.Sleep(250);

			ProgressUpdated("Deleting old patterns...",false);
			XmlElement xmle = xmlSave.GetElementById(getCurrentRaceMapping().FolderName);
			if (xmle!=null)
			{
				xmle.InnerXml = "";
				xmlSave.Save(getCurrentlyLoadedPath());
			}

			for (int i = 0; i<oldFiles.Length; i++)
			{
				oldFiles[i].Delete();
				ProgressUpdated(null,false);
			}

			ProgressUpdated("Previous patterns deleted.",true);
		}

		private void thrLoadSaved()
		{
			Hashtable htError = new Hashtable();
			
			for (int i = 0; i<savedNodes.Count; i++)
			{
				if (!File.Exists(folder_from+savedNodes[i].Attributes["folder"].Value+"\\"+savedNodes[i].InnerText))
				{
					htError[savedNodes[i].Attributes["folder"].Value+"\\"+savedNodes[i].InnerText] = savedNodes[i].InnerText+" in "+savedNodes[i].Attributes["folder"].Value;
				}
				ProgressUpdated(null,false);
			}

			if (htError.Count>0)
			{
				StringBuilder message = new StringBuilder();
				foreach (string fileMissing in htError.Values)
				{
					message.Append("\r\n"+fileMissing);
				}

				ProgressUpdated("ERROR",false);
				MessageBox.Show(this,"Loading failed, the following files were missing:\r\n"+message,
					"Incomplete files",MessageBoxButtons.OK,MessageBoxIcon.Error);

				loader.Close();				

				return;
			}

			XmlElement raceElem = xmlSave.GetElementById(getCurrentRaceMapping().FolderName);
			if (raceElem == null)
			{
				raceElem = xmlSave.CreateElement("","race","");
				AddNodeAttribute(raceElem,"id",getCurrentRaceMapping().FolderName);
				xmlSave.LastChild.AppendChild(raceElem);
			}
			else
			{
				raceElem.InnerXml = "";
			}

			if (oldFiles.Length>0)
			{
				ProgressUpdated("Deleting previous patterns for race...",false);

				for (int i = 0; i<oldFiles.Length; i++)
				{
					if (oldFiles[i].Extension.ToLower()==".wtp" ||
						oldFiles[i].Extension.ToLower()==".rsh")
					{
						oldFiles[i].Delete();
					}
					ProgressUpdated(null,false);
				}
			}

			ProgressUpdated("Copying patterns...",false);

			bool error = false;
			FileInfo fi = null;
			XmlElement xmle = null;

			for (int i = 0; i<savedNodes.Count; i++)
			{
				if (savedNodes[i]!=null)
				{
					try
					{
						fi = new FileInfo(folder_from+savedNodes[i].Attributes["folder"].Value+"\\"+savedNodes[i].InnerText);
						fi.CopyTo(folder_to+savedNodes[i].InnerText,true);
						xmle = xmlSave.CreateElement("","file","");
						AddNodeAttribute(xmle,"id",savedNodes[i].Attributes["id"].Value);
						AddNodeAttribute(xmle,"folder",savedNodes[i].Attributes["folder"].Value);
						AddNodeAttribute(xmle,"filesize",fi.Length);
						AddNodeAttribute(xmle,"date",DateTime.Now);
						xmle.InnerText = fi.Name.ToLower();
						raceElem.AppendChild(xmle);
					}							
					catch
					{
						error = true;
					}
				}
				ProgressUpdated(null,false);
			}

			xmlSave.Save(getCurrentlyLoadedPath());
			string msgComplete = "";

			if (error)
			{
				msgComplete = "One or more files failed to import.";
			}
			else
			{
				msgComplete = "Pattern file import complete.";
			}

			ProgressUpdated(msgComplete,true);
		}
		#endregion

		private void selectTab(string race)
		{
			tabControl.SelectedTab = (TabPage)htTabs[race];

			if (tabControl.SelectedTab == null)
			{
				tabControl.SelectedIndex = 0;
			}
		}

		private void miHelp_Click(object sender, System.EventArgs e)
		{
			FrmHelp hlp = new FrmHelp();
			hlp.ShowDialog(this);
		}

		private void bttnLaunchDoW_Click(object sender, System.EventArgs e)
		{
			startGame(getDoWAppName());
		}

		private void bttnLaunchWA_Click(object sender, System.EventArgs e)
		{
			startGame(getWAAppName());
		}

		private void bttnLaunchDC_Click(object sender, System.EventArgs e)
		{
			startGame(getDCAppName());
		}

		private void bttnLaunchSoulstorm_Click(object sender, System.EventArgs e)
		{
			startGame(getSoulstormAppName());
		}

		private void startGame(string exeName)
		{
			string dowPath = getGamePath();

			if (File.Exists(dowPath+exeName))
			{
				System.Diagnostics.Process proc = new System.Diagnostics.Process();
				proc.EnableRaisingEvents = false;
				proc.StartInfo.WorkingDirectory = dowPath;
				proc.StartInfo.FileName = exeName;
				proc.Start(); 		
			}
			else
			{
				MessageBox.Show(this, "Unable to start "+exeName+" - could not find program", "Launch failed",
					MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
			}
		}

		private string getDoWAppName()
		{
			return getAppName(0);
		}

		private string getWAAppName()
		{
			return getAppName(1);
		}

		private string getDCAppName()
		{
			return getAppName(2);
		}

		private string getSoulstormAppName()
		{
			return getAppName(3);
		}

		private string getAppName(int index)
		{
			string[] exeList = ((string)pref["ExeList"]).Split(',');

			if (index < exeList.Length)
			{
				return exeList[index];
			}

			throw new ArgumentException("Last application name is at index "+exeList.Length);
		}

		private string getPatternPath()
		{
			return pref["DoWTexturePath"].ToString();
		}

		private string getGamePath()
		{
			return pref["DoWPath"].ToString();
		}

		private void createTabs()
		{
			htTabs.Clear();
			TabPage tab;

			tabControl.TabPages.Clear();

			foreach (FolderToArmyMapping mapping in getCurrentFolderMappings())
			{
				tab = createTab(mapping);
			}
		}

		/// <summary>
		/// Method must be called via Invoke when called from a thread
		/// </summary>
		/// <param name="mapping"></param>
		/// <returns></returns>
		private TabPage createTab(FolderToArmyMapping mapping)
		{
			TabPage tab = new TabPage();			
			ListBox lst = new ListBox();
			DataGrid dg = new DataGrid();

			tab.SuspendLayout();
			((System.ComponentModel.ISupportInitialize)(dg)).BeginInit();

			tab.Controls.Add(lst);
			tab.Controls.Add(dg);

			tab.Location = new System.Drawing.Point(4, 22);
			tab.Name = "tab" + mapping.FolderName;
			tab.Size = new System.Drawing.Size(696, 198);
			tab.Tag = mapping;
			tab.Text = mapping.ArmyName;

			lst.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left)));
			lst.Location = new System.Drawing.Point(8, 8);
			lst.Name = "lstMarines";
			lst.Size = new System.Drawing.Size(168, 186);
			lst.TabIndex = 0;
			lst.SelectedIndexChanged += new System.EventHandler(this.list_SelectedIndexChanged);

			dg.AllowNavigation = false;
			dg.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right)));
			dg.BackgroundColor = System.Drawing.SystemColors.Control;
			dg.CaptionVisible = false;
			dg.DataMember = "";
			dg.FlatMode = true;
			dg.HeaderForeColor = System.Drawing.SystemColors.ControlText;
			dg.Location = new System.Drawing.Point(184, 8);
			dg.Name = "dgLoadedMarines";
			dg.PreferredColumnWidth = 150;
			dg.ReadOnly = true;
			dg.RowHeadersVisible = false;
			dg.RowHeaderWidth = 0;
			dg.Size = new System.Drawing.Size(504, 186);
			dg.TabIndex = 1;
			
			tab.ResumeLayout(false);
			((System.ComponentModel.ISupportInitialize)(dg)).EndInit();

			tabControl.SuspendLayout();
			addTabToHashtable(tab);
			tabControl.Controls.Add(tab);
			tabControl.ResumeLayout();

			return tab;
		}

		private void addTabToHashtable(TabPage tab)
		{
			if (tab!=null)
			{
				htTabs.Add(getRaceMapping(tab).FolderName, tab);
			}
		}

		private void removeTab(TabPage tab)
		{
			if (tab!=null)
			{
				tabControl.Invoke(new TabMethodInvoker(tabControl.TabPages.Remove), new object[]{tab});
				htTabs.Remove(getRaceMapping(tab).FolderName);
			}
		}

		private ListBox getList(TabPage tab)
		{
			foreach (Control ctrl in tab.Controls)
			{
				if (ctrl is ListBox)
				{
					return (ListBox)ctrl;
				}
			}

			return null;
		}

		private DataGrid getDataGrid(TabPage tab)
		{
			foreach (Control ctrl in tab.Controls)
			{
				if (ctrl is DataGrid)
				{
					return (DataGrid)ctrl;
				}
			}

			return null;
		}

		private string getCurrentlyLoadedPath()
		{
			string gamePath = getGamePath().TrimEnd(Constants.DirectoryChar);
			int slash = gamePath.LastIndexOf(Constants.DirectoryChar) + 1;
			return getPatternPath()+"current_patterns_"+gamePath.Substring(slash).Replace(" ", "_")+".xml";
		}
	}
}