From ca8de716adf7a191a749963fe5a974e3d8d4ca75 Mon Sep 17 00:00:00 2001
From: Ian C <ianc@noddybox.co.uk>
Date: Fri, 21 Jan 2005 01:09:09 +0000
Subject: Initial checkin

---
 About.cs                     |  212 ++++++++
 AssemblyInfo.cs              |   33 ++
 CopyMenu.cs                  |  165 ++++++
 GfxEd8.About.resources       |  Bin 0 -> 5474 bytes
 GfxEd8.GfxEditor.resources   |  Bin 0 -> 8792 bytes
 GfxEd8.MainForm.resources    |  Bin 0 -> 14677 bytes
 GfxEd8.PaletteForm.resources |  Bin 0 -> 5153 bytes
 GfxEd8.cmbx                  |   28 +
 GfxEd8.prjx                  |   39 ++
 GfxEditor.cs                 | 1156 ++++++++++++++++++++++++++++++++++++++++++
 MainForm.cs                  | 1046 ++++++++++++++++++++++++++++++++++++++
 PaletteForm.cs               |  205 ++++++++
 PluginControl.cs             |  217 ++++++++
 Util.cs                      |   91 ++++
 gfxed8config.xml             |    4 +
 15 files changed, 3196 insertions(+)
 create mode 100644 About.cs
 create mode 100644 AssemblyInfo.cs
 create mode 100644 CopyMenu.cs
 create mode 100644 GfxEd8.About.resources
 create mode 100644 GfxEd8.GfxEditor.resources
 create mode 100644 GfxEd8.MainForm.resources
 create mode 100644 GfxEd8.PaletteForm.resources
 create mode 100644 GfxEd8.cmbx
 create mode 100644 GfxEd8.prjx
 create mode 100644 GfxEditor.cs
 create mode 100644 MainForm.cs
 create mode 100644 PaletteForm.cs
 create mode 100644 PluginControl.cs
 create mode 100644 Util.cs
 create mode 100644 gfxed8config.xml

diff --git a/About.cs b/About.cs
new file mode 100644
index 0000000..8f9f98f
--- /dev/null
+++ b/About.cs
@@ -0,0 +1,212 @@
+// GfxEd8
+// Copyright (C) 2004  Ian Cowburn
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+// $Id$
+//
+using System;
+using System.Windows.Forms;
+using System.Text;
+using System.Reflection;
+using GfxEdInterface;
+
+namespace GfxEd8
+{
+	/// <summary>
+	/// Description of About
+	/// </summary>
+	public class About : System.Windows.Forms.Form
+	{
+		private System.Windows.Forms.Label label2;
+		private System.Windows.Forms.Label label1;
+		private System.Windows.Forms.RichTextBox m_info;
+		private System.Windows.Forms.Button m_ok;
+		private System.Windows.Forms.ListBox m_plugin;
+		private System.Windows.Forms.Label m_gpl;
+		private System.Windows.Forms.Label m_title;
+
+		public About()
+		{
+			//
+			// The InitializeComponent() call is required for Windows Forms designer support.
+			//
+			InitializeComponent();
+
+			StringBuilder s=new StringBuilder();
+
+			m_title.Text="GfxEd8 (c) Copyright 2004  Ian Cowburn";
+			
+			s.Append("This program is distributed in the hope that it will be useful, ");
+			s.Append("but WITHOUT ANY WARRANTY; without even the implied warranty of ");
+			s.Append("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the ");
+			s.Append("GNU General Public License for more details.");
+			
+			m_gpl.Text=s.ToString();
+			
+			foreach (string n in PluginControl.Names)
+			{
+				m_plugin.Items.Add(n);
+			}
+			
+			m_plugin.SelectedIndex=0;
+		}
+		
+		#region Windows Forms Designer generated code
+		/// <summary>
+		/// This method is required for Windows Forms designer support.
+		/// Do not change the method contents inside the source code editor. The Forms designer might
+		/// not be able to load this method if it was changed manually.
+		/// </summary>
+		private void InitializeComponent() {
+			this.m_title = new System.Windows.Forms.Label();
+			this.m_gpl = new System.Windows.Forms.Label();
+			this.m_plugin = new System.Windows.Forms.ListBox();
+			this.m_ok = new System.Windows.Forms.Button();
+			this.m_info = new System.Windows.Forms.RichTextBox();
+			this.label1 = new System.Windows.Forms.Label();
+			this.label2 = new System.Windows.Forms.Label();
+			this.SuspendLayout();
+			// 
+			// m_title
+			// 
+			this.m_title.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
+			this.m_title.Location = new System.Drawing.Point(8, 8);
+			this.m_title.Name = "m_title";
+			this.m_title.Size = new System.Drawing.Size(504, 32);
+			this.m_title.TabIndex = 7;
+			this.m_title.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+			// 
+			// m_gpl
+			// 
+			this.m_gpl.Location = new System.Drawing.Point(72, 48);
+			this.m_gpl.Name = "m_gpl";
+			this.m_gpl.Size = new System.Drawing.Size(392, 56);
+			this.m_gpl.TabIndex = 0;
+			// 
+			// m_plugin
+			// 
+			this.m_plugin.Location = new System.Drawing.Point(8, 128);
+			this.m_plugin.Name = "m_plugin";
+			this.m_plugin.Size = new System.Drawing.Size(504, 82);
+			this.m_plugin.Sorted = true;
+			this.m_plugin.TabIndex = 4;
+			this.m_plugin.SelectedIndexChanged += new System.EventHandler(this.OnSelectPlugin);
+			// 
+			// m_ok
+			// 
+			this.m_ok.Location = new System.Drawing.Point(424, 352);
+			this.m_ok.Name = "m_ok";
+			this.m_ok.Size = new System.Drawing.Size(88, 24);
+			this.m_ok.TabIndex = 2;
+			this.m_ok.Text = "OK";
+			this.m_ok.Click += new System.EventHandler(this.OnOK);
+			// 
+			// m_info
+			// 
+			this.m_info.BackColor = System.Drawing.SystemColors.Info;
+			this.m_info.Location = new System.Drawing.Point(8, 232);
+			this.m_info.Name = "m_info";
+			this.m_info.ReadOnly = true;
+			this.m_info.Size = new System.Drawing.Size(504, 112);
+			this.m_info.TabIndex = 5;
+			this.m_info.Text = "";
+			// 
+			// label1
+			// 
+			this.label1.Location = new System.Drawing.Point(8, 112);
+			this.label1.Name = "label1";
+			this.label1.Size = new System.Drawing.Size(96, 16);
+			this.label1.TabIndex = 3;
+			this.label1.Text = "Plugins:";
+			// 
+			// label2
+			// 
+			this.label2.Location = new System.Drawing.Point(8, 216);
+			this.label2.Name = "label2";
+			this.label2.Size = new System.Drawing.Size(96, 16);
+			this.label2.TabIndex = 6;
+			this.label2.Text = "Plugin Info:";
+			// 
+			// About
+			// 
+			this.AutoScaleBaseSize = new System.Drawing.Size(5, 14);
+			this.ClientSize = new System.Drawing.Size(522, 383);
+			this.Controls.Add(this.m_title);
+			this.Controls.Add(this.label2);
+			this.Controls.Add(this.m_plugin);
+			this.Controls.Add(this.label1);
+			this.Controls.Add(this.m_ok);
+			this.Controls.Add(this.m_gpl);
+			this.Controls.Add(this.m_info);
+			this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+			this.MaximizeBox = false;
+			this.MinimizeBox = false;
+			this.Name = "About";
+			this.ShowInTaskbar = false;
+			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+			this.Text = "About GfxEd8";
+			this.ResumeLayout(false);
+		}
+		#endregion
+
+
+		void OnOK(object sender, System.EventArgs e)
+		{
+			Close();
+		}
+
+		string GetSize(SpriteSize[] sz)
+		{
+			string ret="(";
+			bool first=true;
+			
+			foreach (SpriteSize s in sz)
+			{
+				if (!first)
+				{
+					ret+=", (";
+				}
+				
+				ret+=s.ToString()+")";
+				
+				first=false;
+			}
+			
+			return ret;
+		}
+
+		void OnSelectPlugin(object sender, System.EventArgs e)
+		{
+			string name=m_plugin.Items[m_plugin.SelectedIndex].ToString();
+			IPlugin i=PluginControl.Get(name);
+			Assembly a=PluginControl.GetAssembly(name);
+			
+			StringBuilder s=new StringBuilder();
+
+			s.Append("Author\t\t: "+i.Author+"\n");
+			s.Append("Description\t: "+i.Description+"\n");
+			s.Append("URL\t\t: "+i.URL+"\n");
+			s.Append("Version\t\t: "+PluginControl.Version(name)+"\n");
+			s.Append("Colours per sprite\t: "+i.MaxColours+"\n");
+			s.Append("Supported sizes\t: "+GetSize(i.AllowedSizes)+"\n");
+			s.Append("Location\t\t: "+a.Location+"\n");
+			s.Append("CLR Version\t: "+a.ImageRuntimeVersion+"\n");
+			
+			m_info.Text=s.ToString();
+		}
+		
+	}
+}
diff --git a/AssemblyInfo.cs b/AssemblyInfo.cs
new file mode 100644
index 0000000..965e66d
--- /dev/null
+++ b/AssemblyInfo.cs
@@ -0,0 +1,33 @@
+// $Id$
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+// Information about this assembly is defined by the following
+// attributes.
+//
+// change them to the information which is associated with the assembly
+// you compile.
+
+[assembly: AssemblyTitle("")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// The assembly version has following format :
+//
+// Major.Minor.Build.Revision
+//
+// You can specify all values by your own or you can build default build and revision
+// numbers with the '*' character (the default):
+
+[assembly: AssemblyVersion("1.0.*")]
+
+// The following attributes specify the key for the sign of your assembly. See the
+// .NET Framework documentation for more information about signing.
+// This is not required, if you don't want signing let these attributes like they're.
+[assembly: AssemblyDelaySign(false)]
+[assembly: AssemblyKeyFile("")]
diff --git a/CopyMenu.cs b/CopyMenu.cs
new file mode 100644
index 0000000..32af903
--- /dev/null
+++ b/CopyMenu.cs
@@ -0,0 +1,165 @@
+// GfxEd8
+// Copyright (C) 2004  Ian Cowburn
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+// $Id$
+//
+using System;
+using System.Collections;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace GfxEd8
+{
+	/// <summary>
+	/// Implements the Copy/Paste menu
+	/// </summary>
+	public class CopyMenu
+	{
+		public enum EMode
+		{
+			eNothing,
+			eMark,
+			ePaste,
+			ePasteTransparent
+		};
+		
+		public enum EGravity
+		{
+			eTopLeft,
+			eTopRight,
+			eBottomLeft,
+			eBottomRight,
+			eCentre
+		};
+
+		public CopyMenu()
+		{
+			m_mode=EMode.eNothing;
+			m_grav=EGravity.eTopLeft;
+			
+			CreateMenus();
+		}
+		
+		public bool AllowPaste
+		{
+			set
+			{
+				m_paste.Enabled=value;
+				m_pasteTrans.Enabled=value;
+			}
+		}
+		
+		public EMode Mode
+		{
+			get {return m_mode;}
+			set {m_mode=value;}
+		}
+
+		public EGravity Gravity
+		{
+			get {return m_grav;}
+		}
+		
+		public void Show(Control c, int x, int y)
+		{
+			m_menu.Show(c, new Point(x,y));
+		}
+
+
+		// ------------------------------------------------
+		// PRIVATE
+		//
+		private EMode		m_mode;
+		private EGravity	m_grav;
+		
+		private Hashtable	m_modeMap;
+		private Hashtable	m_gravMap;
+
+		private ContextMenu	m_menu;
+		private MenuItem	m_mark;
+		private MenuItem	m_paste;
+		private MenuItem	m_pasteTrans;
+		private MenuItem	m_gravSub;
+		private MenuItem	m_topLeft;
+		private MenuItem	m_topRight;
+		private MenuItem	m_bottomLeft;
+		private MenuItem	m_bottomRight;
+		private MenuItem	m_centre;
+
+		private void CreateMenus()
+		{
+			m_menu=new ContextMenu();
+			
+			m_mark=new MenuItem("Mark and Copy", new EventHandler(OnMenu));
+			m_paste=new MenuItem("Paste", new EventHandler(OnMenu));
+			m_pasteTrans=new MenuItem("Paste Transparent", new EventHandler(OnMenu));
+			
+			m_gravSub=new MenuItem("Paste Gravity");
+			
+			m_topLeft=new MenuItem("Top Left",new EventHandler(OnGravity));
+			m_topRight=new MenuItem("Top Right",new EventHandler(OnGravity));
+			m_bottomLeft=new MenuItem("Bottom Left",new EventHandler(OnGravity));
+			m_bottomRight=new MenuItem("Bottom Right",new EventHandler(OnGravity));
+			m_centre=new MenuItem("Centre",new EventHandler(OnGravity));
+			
+			m_gravSub.MenuItems.Add(m_topLeft);
+			m_gravSub.MenuItems.Add(m_topRight);
+			m_gravSub.MenuItems.Add(m_bottomLeft);
+			m_gravSub.MenuItems.Add(m_bottomRight);
+			m_gravSub.MenuItems.Add(m_centre);
+			
+			m_topLeft.Checked=true;
+			
+			m_menu.MenuItems.Add(m_mark);
+			m_menu.MenuItems.Add(m_gravSub);
+			m_menu.MenuItems.Add(m_paste);
+			m_menu.MenuItems.Add(m_pasteTrans);
+			
+			m_modeMap=new Hashtable();
+			m_gravMap=new Hashtable();
+			
+			m_modeMap[m_mark]=EMode.eMark;
+			m_modeMap[m_paste]=EMode.ePaste;
+			m_modeMap[m_pasteTrans]=EMode.ePasteTransparent;
+			
+			m_gravMap[m_topLeft]=EGravity.eTopLeft;
+			m_gravMap[m_topRight]=EGravity.eTopRight;
+			m_gravMap[m_bottomLeft]=EGravity.eBottomLeft;
+			m_gravMap[m_bottomRight]=EGravity.eBottomRight;
+			m_gravMap[m_centre]=EGravity.eCentre;
+		}
+		
+		private void OnMenu(object sender, System.EventArgs e)
+		{
+			m_mode=(EMode)m_modeMap[sender];
+		}
+		
+		private void OnGravity(object sender, System.EventArgs e)
+		{
+			MenuItem i=(MenuItem)sender;
+			
+			m_grav=(EGravity)m_gravMap[sender];
+			
+			foreach (MenuItem m in m_gravSub.MenuItems)
+			{
+				m.Checked=false;
+			}
+			
+			i.Checked=true;
+		}
+	}
+}
diff --git a/GfxEd8.About.resources b/GfxEd8.About.resources
new file mode 100644
index 0000000..e880ad9
Binary files /dev/null and b/GfxEd8.About.resources differ
diff --git a/GfxEd8.GfxEditor.resources b/GfxEd8.GfxEditor.resources
new file mode 100644
index 0000000..0ba4a25
Binary files /dev/null and b/GfxEd8.GfxEditor.resources differ
diff --git a/GfxEd8.MainForm.resources b/GfxEd8.MainForm.resources
new file mode 100644
index 0000000..bbd0f11
Binary files /dev/null and b/GfxEd8.MainForm.resources differ
diff --git a/GfxEd8.PaletteForm.resources b/GfxEd8.PaletteForm.resources
new file mode 100644
index 0000000..44721c3
Binary files /dev/null and b/GfxEd8.PaletteForm.resources differ
diff --git a/GfxEd8.cmbx b/GfxEd8.cmbx
new file mode 100644
index 0000000..0325574
--- /dev/null
+++ b/GfxEd8.cmbx
@@ -0,0 +1,28 @@
+<Combine fileversion="1.0" name="GfxEd8" description="">
+  <StartMode startupentry="GfxEd8" single="True">
+    <Execute entry="GfxEd8" type="None" />
+    <Execute entry="GfxEdInterface" type="None" />
+    <Execute entry="Noddybox.GUI" type="None" />
+    <Execute entry="GfxEdBasePlugin" type="None" />
+  </StartMode>
+  <Entries>
+    <Entry filename=".\.\GfxEd8.prjx" />
+    <Entry filename="..\GfxEdInterface\GfxEdInterface.prjx" />
+    <Entry filename="..\Noddybox.GUI\Noddybox.GUI.prjx" />
+    <Entry filename="..\GfxEdBasePlugin\GfxEdBasePlugin.prjx" />
+  </Entries>
+  <Configurations active="Debug">
+    <Configuration name="Release">
+      <Entry name="GfxEd8" configurationname="Debug" build="False" />
+      <Entry name="GfxEdInterface" configurationname="Debug" build="False" />
+      <Entry name="Noddybox.GUI" configurationname="Debug" build="False" />
+      <Entry name="GfxEdBasePlugin" configurationname="Debug" build="False" />
+    </Configuration>
+    <Configuration name="Debug">
+      <Entry name="GfxEd8" configurationname="Debug" build="False" />
+      <Entry name="GfxEdInterface" configurationname="Debug" build="False" />
+      <Entry name="Noddybox.GUI" configurationname="Debug" build="False" />
+      <Entry name="GfxEdBasePlugin" configurationname="Debug" build="False" />
+    </Configuration>
+  </Configurations>
+</Combine>
\ No newline at end of file
diff --git a/GfxEd8.prjx b/GfxEd8.prjx
new file mode 100644
index 0000000..6602de7
--- /dev/null
+++ b/GfxEd8.prjx
@@ -0,0 +1,39 @@
+<Project name="GfxEd8" standardNamespace="GfxEd8" description="" newfilesearch="None" enableviewstate="True" version="1.1" projecttype="C#">
+  <Contents>
+    <File name=".\MainForm.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
+    <File name=".\AssemblyInfo.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
+    <File name=".\gfxed8config.xml" subtype="Code" buildaction="Nothing" dependson="" data="" />
+    <File name=".\GfxEd8.MainForm.resources" subtype="Code" buildaction="EmbedAsResource" dependson="" data="" />
+    <File name=".\PluginControl.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
+    <File name=".\Util.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
+    <File name=".\About.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
+    <File name=".\GfxEd8.About.resources" subtype="Code" buildaction="EmbedAsResource" dependson="" data="" />
+    <File name=".\GfxEditor.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
+    <File name=".\GfxEd8.GfxEditor.resources" subtype="Code" buildaction="EmbedAsResource" dependson="" data="" />
+    <File name=".\PaletteForm.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
+    <File name=".\GfxEd8.PaletteForm.resources" subtype="Code" buildaction="EmbedAsResource" dependson="" data="" />
+    <File name=".\CopyMenu.cs" subtype="Code" buildaction="Compile" dependson="" data="" />
+  </Contents>
+  <References>
+    <Reference type="Project" refto="GfxEdInterface" localcopy="True" />
+    <Reference type="Project" refto="Noddybox.GUI" localcopy="True" />
+  </References>
+  <DeploymentInformation target="" script="" strategy="File" />
+  <Configuration runwithwarnings="True" name="Debug">
+    <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="" warninglevel="4" nowarn="" includedebuginformation="True" optimize="False" unsafecodeallowed="False" generateoverflowchecks="True" mainclass="" target="WinExe" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" />
+    <Execution commandlineparameters="" consolepause="False" />
+    <Output directory="..\bin\Debug" assembly="GfxEd8" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
+  </Configuration>
+  <Configurations active="Debug">
+    <Configuration runwithwarnings="True" name="Debug">
+      <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="" warninglevel="4" nowarn="" includedebuginformation="True" optimize="False" unsafecodeallowed="False" generateoverflowchecks="True" mainclass="" target="WinExe" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" />
+      <Execution commandlineparameters="" consolepause="False" />
+      <Output directory="..\bin\Debug" assembly="GfxEd8" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
+    </Configuration>
+    <Configuration runwithwarnings="True" name="Release">
+      <CodeGeneration runtime="MsNet" compiler="Csc" compilerversion="" warninglevel="4" nowarn="" includedebuginformation="False" optimize="True" unsafecodeallowed="False" generateoverflowchecks="False" mainclass="" target="WinExe" definesymbols="" generatexmldocumentation="False" win32Icon="" noconfig="False" nostdlib="False" />
+      <Execution commandlineparameters="" consolepause="False" />
+      <Output directory="..\bin\Release" assembly="GfxEd8" executeScript="" executeBeforeBuild="" executeAfterBuild="" executeBeforeBuildArguments="" executeAfterBuildArguments="" />
+    </Configuration>
+  </Configurations>
+</Project>
\ No newline at end of file
diff --git a/GfxEditor.cs b/GfxEditor.cs
new file mode 100644
index 0000000..e606939
--- /dev/null
+++ b/GfxEditor.cs
@@ -0,0 +1,1156 @@
+// GfxEd8
+// Copyright (C) 2004  Ian Cowburn
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+// $Id$
+//
+using System;
+using System.ComponentModel;
+using System.Windows.Forms;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using GfxEdInterface;
+
+using System.Diagnostics;
+
+namespace GfxEd8
+{
+	/// <summary>
+	/// Implements a control for drawing
+	/// </summary>
+	[ToolboxItem(true)]
+	public class GfxEditor : System.Windows.Forms.UserControl
+	{
+		private System.Windows.Forms.Label m_fgPreview;
+		private System.Windows.Forms.PictureBox m_edit;
+		private System.Windows.Forms.ComboBox m_modeList;
+		private System.Windows.Forms.Label m_bgPreview;
+		private System.Windows.Forms.NumericUpDown m_leftCol;
+		private System.Windows.Forms.NumericUpDown m_rightCol;
+		private System.Windows.Forms.PictureBox m_preview;
+		private System.Windows.Forms.Button m_palButton;
+		private System.Windows.Forms.TextBox m_name;
+		private System.Windows.Forms.Button m_undoButton;
+		private System.Windows.Forms.Label label3;
+		private System.Windows.Forms.Label label2;
+		private System.Windows.Forms.Label label1;
+		private System.Windows.Forms.Label label5;
+		private System.Windows.Forms.Label label4;
+		private System.Windows.Forms.ComboBox m_size;
+		
+		private const uint	SIZE=256;
+		private const uint	TRANS=UInt32.MaxValue;
+
+		// These MUST match the entries in the drop down list
+		//
+		private enum Mode
+		{
+			ePlot,
+			eLine,
+			eRectangle,
+			eFilledRectangle,
+			eCircle,
+			eFilledCircle,
+			eEllipse,
+			eFilledEllipse,
+			eFloodFill,
+			eCopyPaste
+		};
+
+		//private bool		m_inEditor;
+		
+		private IPlugin		m_plugin;
+		private Sprite		m_sprite;
+		private Sprite		m_undo;
+		private SpriteSize	m_sprsize;
+		private Colour[]	m_pal;
+		private uint		m_width;
+		private uint		m_height;
+		
+		private Rectangle	m_editRect;
+		private Bitmap		m_editBmp;
+		private Rectangle	m_prevRect;
+		private Bitmap		m_prevBmp;
+		
+		private uint		m_gridx;
+		private uint		m_gridy;
+		private uint		m_mx;
+		private uint		m_my;
+		private Color		m_gridCol;
+		
+		private bool		m_drawing;
+		private Mode		m_mode;
+		private uint[,]		m_overlay;
+		private uint		m_ox;
+		private uint		m_oy;
+		private uint		m_pen;
+		
+		private CopyMenu	m_cpMenu;
+		private uint		m_cpX;
+		private uint		m_cpY;
+		private uint		m_cpWidth;
+		private uint		m_cpHeight;
+		private uint[,]		m_cpBuff;
+		
+		public GfxEditor()
+		{
+			//
+			// InitializeComponent() call is required for Windows Forms designer support.
+			//
+			InitializeComponent();
+			
+			//m_inEditor=false;
+			
+			m_sprite=null;
+			m_gridx=8;
+			m_gridy=8;
+			m_mx=SIZE;
+			m_my=SIZE;
+			m_gridCol=Color.LightGreen;
+			
+			m_editRect=m_edit.ClientRectangle;
+			m_editBmp=new Bitmap(m_editRect.Width,m_editRect.Height);
+			m_edit.Image=m_editBmp;
+
+			m_prevRect=m_edit.ClientRectangle;
+			m_prevBmp=new Bitmap(m_prevRect.Width,m_prevRect.Height);
+			m_preview.Image=m_prevBmp;
+			
+			m_drawing=false;
+			m_mode=Mode.ePlot;
+			
+			if (m_editRect.Width!=SIZE && m_editRect.Height!=SIZE)
+			{
+				throw new Exception("GfxEditor built with bad SIZE!");
+			}
+
+			m_modeList.SelectedIndex=0;
+			
+			m_cpMenu=new CopyMenu();
+		}
+		
+		#region Windows Forms Designer generated code
+		/// <summary>
+		/// This method is required for Windows Forms designer support.
+		/// Do not change the method contents inside the source code editor. The Forms designer might
+		/// not be able to load this method if it was changed manually.
+		/// </summary>
+		private void InitializeComponent() {
+			this.m_size = new System.Windows.Forms.ComboBox();
+			this.label4 = new System.Windows.Forms.Label();
+			this.label5 = new System.Windows.Forms.Label();
+			this.label1 = new System.Windows.Forms.Label();
+			this.label2 = new System.Windows.Forms.Label();
+			this.label3 = new System.Windows.Forms.Label();
+			this.m_undoButton = new System.Windows.Forms.Button();
+			this.m_name = new System.Windows.Forms.TextBox();
+			this.m_palButton = new System.Windows.Forms.Button();
+			this.m_preview = new System.Windows.Forms.PictureBox();
+			this.m_rightCol = new System.Windows.Forms.NumericUpDown();
+			this.m_leftCol = new System.Windows.Forms.NumericUpDown();
+			this.m_bgPreview = new System.Windows.Forms.Label();
+			this.m_modeList = new System.Windows.Forms.ComboBox();
+			this.m_edit = new System.Windows.Forms.PictureBox();
+			this.m_fgPreview = new System.Windows.Forms.Label();
+			((System.ComponentModel.ISupportInitialize)(this.m_rightCol)).BeginInit();
+			((System.ComponentModel.ISupportInitialize)(this.m_leftCol)).BeginInit();
+			this.SuspendLayout();
+			// 
+			// m_size
+			// 
+			this.m_size.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.m_size.Location = new System.Drawing.Point(280, 64);
+			this.m_size.MaxDropDownItems = 16;
+			this.m_size.Name = "m_size";
+			this.m_size.Size = new System.Drawing.Size(96, 21);
+			this.m_size.TabIndex = 9;
+			this.m_size.SelectedIndexChanged += new System.EventHandler(this.OnSize);
+			// 
+			// label4
+			// 
+			this.label4.Location = new System.Drawing.Point(280, 128);
+			this.label4.Name = "label4";
+			this.label4.Size = new System.Drawing.Size(32, 16);
+			this.label4.TabIndex = 10;
+			this.label4.Text = "Left";
+			this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+			// 
+			// label5
+			// 
+			this.label5.Location = new System.Drawing.Point(328, 128);
+			this.label5.Name = "label5";
+			this.label5.Size = new System.Drawing.Size(48, 16);
+			this.label5.TabIndex = 12;
+			this.label5.Text = "Right";
+			this.label5.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+			// 
+			// label1
+			// 
+			this.label1.Location = new System.Drawing.Point(280, 88);
+			this.label1.Name = "label1";
+			this.label1.Size = new System.Drawing.Size(64, 16);
+			this.label1.TabIndex = 3;
+			this.label1.Text = "Draw Mode";
+			this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+			// 
+			// label2
+			// 
+			this.label2.Location = new System.Drawing.Point(280, 8);
+			this.label2.Name = "label2";
+			this.label2.Size = new System.Drawing.Size(64, 16);
+			this.label2.TabIndex = 6;
+			this.label2.Text = "Name";
+			this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+			// 
+			// label3
+			// 
+			this.label3.Location = new System.Drawing.Point(280, 48);
+			this.label3.Name = "label3";
+			this.label3.Size = new System.Drawing.Size(64, 16);
+			this.label3.TabIndex = 8;
+			this.label3.Text = "Size";
+			this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+			// 
+			// m_undoButton
+			// 
+			this.m_undoButton.Location = new System.Drawing.Point(8, 272);
+			this.m_undoButton.Name = "m_undoButton";
+			this.m_undoButton.Size = new System.Drawing.Size(72, 24);
+			this.m_undoButton.TabIndex = 16;
+			this.m_undoButton.Text = "Undo";
+			this.m_undoButton.Click += new System.EventHandler(this.OnUndo);
+			// 
+			// m_name
+			// 
+			this.m_name.Location = new System.Drawing.Point(280, 24);
+			this.m_name.Name = "m_name";
+			this.m_name.Size = new System.Drawing.Size(96, 20);
+			this.m_name.TabIndex = 7;
+			this.m_name.Text = "";
+			this.m_name.TextChanged += new System.EventHandler(this.OnNameChanged);
+			// 
+			// m_palButton
+			// 
+			this.m_palButton.Location = new System.Drawing.Point(280, 192);
+			this.m_palButton.Name = "m_palButton";
+			this.m_palButton.Size = new System.Drawing.Size(96, 24);
+			this.m_palButton.TabIndex = 14;
+			this.m_palButton.Text = "Palette...";
+			this.m_palButton.Click += new System.EventHandler(this.OnPalette);
+			// 
+			// m_preview
+			// 
+			this.m_preview.BackColor = System.Drawing.SystemColors.Control;
+			this.m_preview.Location = new System.Drawing.Point(280, 224);
+			this.m_preview.Name = "m_preview";
+			this.m_preview.Size = new System.Drawing.Size(34, 34);
+			this.m_preview.TabIndex = 1;
+			this.m_preview.TabStop = false;
+			// 
+			// m_rightCol
+			// 
+			this.m_rightCol.Location = new System.Drawing.Point(328, 144);
+			this.m_rightCol.Name = "m_rightCol";
+			this.m_rightCol.ReadOnly = true;
+			this.m_rightCol.Size = new System.Drawing.Size(40, 20);
+			this.m_rightCol.TabIndex = 13;
+			this.m_rightCol.ValueChanged += new System.EventHandler(this.OnColourChanged);
+			// 
+			// m_leftCol
+			// 
+			this.m_leftCol.Location = new System.Drawing.Point(280, 144);
+			this.m_leftCol.Name = "m_leftCol";
+			this.m_leftCol.ReadOnly = true;
+			this.m_leftCol.Size = new System.Drawing.Size(40, 20);
+			this.m_leftCol.TabIndex = 11;
+			this.m_leftCol.ValueChanged += new System.EventHandler(this.OnColourChanged);
+			// 
+			// m_bgPreview
+			// 
+			this.m_bgPreview.Location = new System.Drawing.Point(328, 168);
+			this.m_bgPreview.Name = "m_bgPreview";
+			this.m_bgPreview.Size = new System.Drawing.Size(40, 16);
+			this.m_bgPreview.TabIndex = 18;
+			// 
+			// m_modeList
+			// 
+			this.m_modeList.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.m_modeList.Items.AddRange(new object[] {
+						"Plot",
+						"Line",
+						"Rectangle",
+						"Filled Rect",
+						"Circle",
+						"Filled Circle",
+						"Ellipse",
+						"Filled Ellipse",
+						"Flood Fill",
+						"Copy/Paste"});
+			this.m_modeList.Location = new System.Drawing.Point(280, 104);
+			this.m_modeList.MaxDropDownItems = 16;
+			this.m_modeList.Name = "m_modeList";
+			this.m_modeList.Size = new System.Drawing.Size(96, 21);
+			this.m_modeList.TabIndex = 15;
+			this.m_modeList.SelectedIndexChanged += new System.EventHandler(this.OnModeSelect);
+			// 
+			// m_edit
+			// 
+			this.m_edit.BackColor = System.Drawing.SystemColors.Control;
+			this.m_edit.Location = new System.Drawing.Point(8, 8);
+			this.m_edit.Name = "m_edit";
+			this.m_edit.Size = new System.Drawing.Size(256, 256);
+			this.m_edit.TabIndex = 0;
+			this.m_edit.TabStop = false;
+			this.m_edit.MouseEnter += new System.EventHandler(this.OnEnterEditor);
+			this.m_edit.MouseUp += new System.Windows.Forms.MouseEventHandler(this.OnMouseMove);
+			this.m_edit.MouseMove += new System.Windows.Forms.MouseEventHandler(this.OnMouseMove);
+			this.m_edit.MouseLeave += new System.EventHandler(this.OnLeaveEditor);
+			this.m_edit.MouseDown += new System.Windows.Forms.MouseEventHandler(this.OnMouseMove);
+			// 
+			// m_fgPreview
+			// 
+			this.m_fgPreview.Location = new System.Drawing.Point(280, 168);
+			this.m_fgPreview.Name = "m_fgPreview";
+			this.m_fgPreview.Size = new System.Drawing.Size(40, 16);
+			this.m_fgPreview.TabIndex = 17;
+			// 
+			// GfxEditor
+			// 
+			this.Controls.Add(this.m_bgPreview);
+			this.Controls.Add(this.m_fgPreview);
+			this.Controls.Add(this.m_undoButton);
+			this.Controls.Add(this.m_modeList);
+			this.Controls.Add(this.m_palButton);
+			this.Controls.Add(this.m_rightCol);
+			this.Controls.Add(this.label5);
+			this.Controls.Add(this.m_leftCol);
+			this.Controls.Add(this.label4);
+			this.Controls.Add(this.m_size);
+			this.Controls.Add(this.label3);
+			this.Controls.Add(this.m_name);
+			this.Controls.Add(this.label2);
+			this.Controls.Add(this.label1);
+			this.Controls.Add(this.m_preview);
+			this.Controls.Add(this.m_edit);
+			this.Name = "GfxEditor";
+			this.Size = new System.Drawing.Size(384, 304);
+			((System.ComponentModel.ISupportInitialize)(this.m_rightCol)).EndInit();
+			((System.ComponentModel.ISupportInitialize)(this.m_leftCol)).EndInit();
+			this.ResumeLayout(false);
+		}
+		#endregion
+
+		// -------------------------------------------------------
+		// PUBLIC INTERFACES
+		// -------------------------------------------------------
+		#region Public Interfaces
+		
+		public Color GridColour
+		{
+			set {m_gridCol=value;}
+		}
+
+		public IPlugin Plugin
+		{
+			set
+			{
+				m_plugin=value;
+				
+				m_pal=m_plugin.Palette;
+				
+				m_leftCol.Minimum=0;
+				m_leftCol.Maximum=m_plugin.MaxColours-1;
+				m_leftCol.Value=1;
+				
+				m_rightCol.Minimum=0;
+				m_rightCol.Maximum=m_plugin.MaxColours-1;
+				m_rightCol.Value=0;
+				
+				if (m_plugin.MaxColours<3)
+				{
+					m_leftCol.Enabled=false;
+					m_rightCol.Enabled=false;
+				}
+				else
+				{
+					m_leftCol.Enabled=true;
+					m_rightCol.Enabled=true;
+				}
+				
+				m_size.Items.Clear();
+				
+				foreach (SpriteSize sz in m_plugin.AllowedSizes)
+				{
+					m_size.Items.Add(sz);
+				}
+				
+				m_sprite=null;
+				m_undo=null;
+				
+				InitialiseOverlay();
+				
+				m_size.SelectedIndex=0;
+				
+				bool flag=false;
+				
+				foreach (bool b in m_plugin.SprPalEditable)
+				{
+					flag|=b;
+				}
+				
+				m_palButton.Enabled=flag;
+				
+				m_cpBuff=null;
+				m_cpMenu.Mode=CopyMenu.EMode.eNothing;
+			}
+		}
+		
+		public Sprite Sprite
+		{
+			get {return m_sprite;}
+			set 
+			{
+				m_sprite=value;
+				
+				m_name.Text=m_sprite.Name;
+				
+				for(int f=0;f<m_size.Items.Count;f++)
+				{
+					SpriteSize sz=(SpriteSize)m_size.Items[f];
+					
+					if (sz.Width==m_sprite.Width && sz.Height==m_sprite.Height)
+					{
+						m_size.SelectedIndex=f;
+						break;
+					}
+				}
+				
+				InitialiseOverlay();
+				DrawSprite();
+				UpdateColourPreviews();
+			}
+		}
+		
+		#endregion
+		
+		// -------------------------------------------------------
+		// SPRITE/GENERAL DRAWING
+		// -------------------------------------------------------
+		#region Sprite/General Drawing
+		
+		private void Flush(ref Graphics g)
+		{
+			g.Flush(FlushIntention.Flush);
+			g.Dispose();
+			g=null;
+		}
+		
+		private void UpdateColourPreviews()
+		{
+			m_fgPreview.BackColor=
+				m_plugin.Palette[m_sprite.Pal(Convert.ToUInt32(m_leftCol.Value))].DrawCol;
+
+			m_bgPreview.BackColor=
+				m_plugin.Palette[m_sprite.Pal(Convert.ToUInt32(m_rightCol.Value))].DrawCol;
+		}
+		
+		private void DrawGrid()
+		{
+			Graphics g=Graphics.FromImage(m_editBmp);
+			Pen p=new Pen(m_gridCol);
+			
+			g.Clear(m_pal[0].DrawCol);
+			Flush(ref g);
+			
+			g=Graphics.FromImage(m_prevBmp);
+
+			g.Clear(m_pal[0].DrawCol);
+			Flush(ref g);
+			
+			g=Graphics.FromImage(m_editBmp);
+			
+			for(int x=0;x<m_width+1;x++)
+			{
+				g.DrawLine(p,x*m_gridx,0,x*m_gridx,m_my);
+			}
+
+			for(int y=0;y<m_height+1;y++)
+			{
+				g.DrawLine(p,0,y*m_gridy,m_mx,y*m_gridy);
+			}
+			
+			Flush(ref g);
+
+			m_edit.Invalidate();
+			m_preview.Invalidate();
+		}
+		
+		private void DrawSprite()
+		{
+			if (m_sprite==null)
+			{
+				return;
+			}
+			
+			Graphics editg=Graphics.FromImage(m_editBmp);
+			Graphics prevg=Graphics.FromImage(m_prevBmp);
+
+			for(uint x=0;x<m_sprite.Width;x++)
+				for(uint y=0;y<m_sprite.Height;y++)
+					if (m_overlay[x,y]==TRANS)
+						SpritePlot(editg,prevg,x,y,m_sprite[x,y]);
+					else
+						SpritePlot(editg,prevg,x,y,m_overlay[x,y]);
+
+			Flush(ref editg);
+			Flush(ref prevg);
+
+			m_edit.Invalidate();
+			m_preview.Invalidate();
+		}
+
+		// A SpritePlot() for every occasion...
+		//
+		private void SpritePlot(Graphics edit, Graphics prev,
+		                        uint x, uint y, uint col)
+		{
+			Color c=m_plugin.Palette[m_sprite.Pal(col)].DrawCol;
+			
+			SpritePlot(edit,prev,x,y,c);
+		}
+
+		private void SpritePlot(Graphics edit, Graphics prev,
+		                        uint x, uint y)
+		{
+			Color c=m_plugin.Palette[m_sprite.Pal(m_sprite[x,y])].DrawCol;
+			
+			SpritePlot(edit,prev,x,y,c);
+		}
+		
+		private void SpritePlot(Graphics edit, Graphics prev,
+		                        uint x, uint y, Color c)
+		{
+			SolidBrush p=new SolidBrush(c);
+			
+			edit.FillRectangle(p,x*m_gridx+1,y*m_gridy+1,m_gridx-1,m_gridy-1);
+			prev.FillRectangle(p,1+x*m_sprsize.XAspect,1+y*m_sprsize.YAspect,
+							   m_sprsize.XAspect,m_sprsize.YAspect);
+		}
+		#endregion
+				
+		// -------------------------------------------------------
+		// OVERLAY HANDLING
+		// -------------------------------------------------------
+		#region Overlay Handling
+		
+		private void InitialiseOverlay()
+		{
+			if (m_sprite==null)
+			{
+				m_overlay=null;
+				return;
+			}
+			
+			m_overlay=new uint[m_sprite.Width,m_sprite.Height];
+			
+			for(uint y=0;y<m_sprite.Height;y++)
+				for(uint x=0;x<m_sprite.Width;x++)
+					m_overlay[x,y]=TRANS;	
+		}
+
+		private void ClearOverlay()
+		{
+			if (m_overlay==null)
+			{
+				return;
+			}
+			
+			for(uint y=0;y<m_sprite.Height;y++)
+				for(uint x=0;x<m_sprite.Width;x++)
+					m_overlay[x,y]=TRANS;	
+		}
+		
+		private void ApplyOverlay()
+		{
+			for(uint y=0;y<m_sprite.Height;y++)
+				for(uint x=0;x<m_sprite.Width;x++)
+				{
+					if (m_overlay[x,y]!=TRANS)
+						m_sprite[x,y]=m_overlay[x,y];
+					
+					m_overlay[x,y]=TRANS;
+				}
+		}
+		
+		private void OverlayFromSprite()
+		{
+			for(uint y=0;y<m_sprite.Height;y++)
+				for(uint x=0;x<m_sprite.Width;x++)
+					m_overlay[x,y]=m_sprite[x,y];
+		}
+
+		#endregion
+		
+		// -------------------------------------------------------
+		// COPY/PASTE ROUTINES
+		// -------------------------------------------------------
+		#region Copy/Paste
+
+		private void DrawMarkBox(uint x1, uint y1,uint x2, uint y2)
+		{
+			RankCoord(ref x1, ref x2);
+			RankCoord(ref y1, ref y2);
+			
+			for(uint x=x1;x<=x2;x++)
+			{
+				m_overlay[x,y1]=(m_sprite[x,y1]+1)%m_plugin.MaxColours;
+				m_overlay[x,y2]=(m_sprite[x,y2]+1)%m_plugin.MaxColours;
+			}
+
+			for(uint y=y1;y<=y2;y++)
+			{
+				m_overlay[x1,y]=(m_sprite[x1,y]+1)%m_plugin.MaxColours;
+				m_overlay[x2,y]=(m_sprite[x2,y]+1)%m_plugin.MaxColours;
+			}
+		}
+		
+		private void Copy(uint x1, uint y1, uint x2, uint y2)
+		{
+			RankCoord(ref x1, ref x2);
+			RankCoord(ref y1, ref y2);
+			
+			m_cpX=x1;
+			m_cpY=y1;
+			m_cpWidth=x2-x1+1;
+			m_cpHeight=y2-y1+1;
+			
+			m_cpBuff=new uint[m_cpWidth,m_cpHeight];
+			
+			for(uint x=0;x<m_cpWidth;x++)
+				for(uint y=0;y<m_cpHeight;y++)
+					m_cpBuff[x,y]=m_sprite[x1+x,y1+y];
+			
+			m_drawing=false;
+			m_cpMenu.Mode=CopyMenu.EMode.eNothing;
+		}
+		
+		private void Paste(int x, int y, bool trans)
+		{
+			switch (m_cpMenu.Gravity)
+			{
+				case CopyMenu.EGravity.eTopLeft:
+					break;
+					
+				case CopyMenu.EGravity.eTopRight:
+					x-=(int)m_cpWidth;
+					break;
+
+				case CopyMenu.EGravity.eBottomLeft:
+					y-=(int)m_cpHeight;
+					break;
+
+				case CopyMenu.EGravity.eBottomRight:
+					x-=(int)m_cpWidth;
+					y-=(int)m_cpHeight;
+					break;
+
+				case CopyMenu.EGravity.eCentre:
+					x-=(int)m_cpWidth/2;
+					y-=(int)m_cpHeight/2;
+					break;
+			}
+			
+			for(int w=0;w<m_cpWidth;w++)
+				for(int h=0;h<m_cpHeight;h++)
+					if (!trans || m_cpBuff[w,h]!=0)
+						SafePlot(x+w,y+h,m_cpBuff[w,h]);
+		}
+		
+		#endregion
+		
+		// -------------------------------------------------------
+		// DRAWING ROUTINES
+		// -------------------------------------------------------
+		#region Drawing Routines
+		
+		private void RankCoord(ref uint c1, ref uint c2)
+		{
+			if (c1>c2)
+			{
+				uint t=c2;
+				c2=c1;
+				c1=t;
+			}
+		}
+		
+		private void SafePlot(int x, int y, uint col)
+		{
+			if (x>=0 && x<m_sprite.Width && y>=0 && y<m_sprite.Height)
+				m_overlay[x,y]=col;
+		}
+		
+		private void DrawLine(uint ux1, uint uy1,
+		                      uint ux2, uint uy2,
+		                      uint col)
+		{
+			int x1=(int)ux1;;
+			int y1=(int)uy1;
+			int x2=(int)ux2;
+			int y2=(int)uy2;
+		
+		    int dx=x2-x1;
+		    int dy=y2-y1;
+		
+		    int ix=Math.Sign(dx);
+		    int iy=Math.Sign(dy);
+		
+		    dx=Math.Abs(dx);
+		    dy=Math.Abs(dy);
+		
+		    int incrE;
+			int incrNE;
+			int d;
+			bool ymode;
+		
+		    if (dy>dx)
+			{
+				ymode=true;
+				d=dx*2-dy;
+				incrE=dx*2;
+				incrNE=(dx-dy)*2;
+			}
+		    else
+			{
+				ymode=false;
+				d=dy*2-dx;
+				incrE=dy*2;
+				incrNE=(dy-dx)*2;
+			}
+		
+		    int x=x1;
+		    int y=y1;
+		
+			SafePlot(x,y,col);
+		
+		    if (ymode)
+		    {
+				while(y!=y2)
+				{
+					if (d<=0)
+					{
+						d+=incrE;
+						y+=iy;
+					}
+					else
+					{
+						d+=incrNE;
+						y+=iy;
+						x+=ix;
+					}
+		
+					SafePlot(x,y,col);
+				}
+		    }
+		    else
+		    {
+				while(x!=x2)
+				{
+					if (d<=0)
+					{
+						d+=incrE;
+						x+=ix;
+					}
+					else
+					{
+						d+=incrNE;
+						y+=iy;
+						x+=ix;
+					}
+		
+					SafePlot(x,y,col);
+				}
+		    }
+		}
+		
+		private void DrawRect(uint x1, uint y1,
+		                      uint x2, uint y2,
+		                      uint col, bool fill)
+		{
+			RankCoord(ref x1, ref x2);
+			RankCoord(ref y1, ref y2);
+			
+			if (fill)
+			{
+				for(uint x=x1;x<=x2;x++)
+					for(uint y=y1;y<=y2;y++)
+						m_overlay[x,y]=col;
+			}
+			else
+			{
+				for(uint x=x1;x<=x2;x++)
+				{
+					m_overlay[x,y1]=col;
+					m_overlay[x,y2]=col;
+				}
+
+				for(uint y=y1;y<=y2;y++)
+				{
+					m_overlay[x1,y]=col;
+					m_overlay[x2,y]=col;
+				}
+			}
+		}
+		
+		private void DrawEllipse(uint originx, uint originy,
+		                         uint radpointx, uint radpointy,
+		                         uint col, bool circle, bool fill)
+		{
+			int ox=(int)originx;
+			int oy=(int)originy;
+			double rx=Math.Abs(ox-(int)radpointx)+1;
+			double ry=Math.Abs(oy-(int)radpointy)+1;
+			
+			if (circle)
+			{
+				rx=Math.Max(rx,ry);
+				ry=rx;
+			}
+			
+			for(double a=0;a<=Math.PI;a+=Math.PI/180)
+			{
+				int x=(int)(Math.Sin(a)*rx);
+				int y=(int)(Math.Cos(a)*ry);
+				
+				if (fill)
+				{
+					for(int f=-x;f<=x;f++)
+						SafePlot(ox+f,oy+y,col);
+				}
+				else
+				{
+					SafePlot(ox+x,oy+y,col);
+					SafePlot(ox-x,oy+y,col);
+				}
+			}
+		}
+		
+		private void FloodFill(uint x, uint y, uint col, uint bg)
+		{
+			if (m_overlay[x,y]==col || m_overlay[x,y]!=bg)
+			{
+				return;
+			}
+			
+			m_overlay[x,y]=col;
+			
+			if (x>0)
+				FloodFill(x-1,y,col,bg);
+			
+			if (y>0)
+				FloodFill(x,y-1,col,bg);
+			
+			if (x<m_sprite.Width-1)
+				FloodFill(x+1,y,col,bg);
+			
+			if (y<m_sprite.Height-1)
+				FloodFill(x,y+1,col,bg);
+		}
+
+		#endregion
+		
+		// -------------------------------------------------------
+		// CALLBACKS
+		// -------------------------------------------------------
+		#region Callbacks
+		
+		void OnDrawMode(object sender, System.EventArgs e)
+		{
+			m_mode=(Mode)m_modeList.SelectedIndex;
+		}
+		
+		void OnSize(object sender, System.EventArgs e)
+		{
+			m_sprsize=(SpriteSize)m_size.Items[m_size.SelectedIndex];
+			
+			if (m_sprite!=null)
+			{
+				m_undo=new Sprite(m_sprite);
+				m_sprite.Resize(m_sprsize.Width,m_sprsize.Height);
+				InitialiseOverlay();
+			}
+			
+			m_width=m_sprsize.Width;
+			m_height=m_sprsize.Height;
+
+			uint grid=Math.Min((SIZE/m_sprsize.XAspect)/m_width,
+			                   (SIZE/m_sprsize.YAspect)/m_height);
+			
+			m_gridx=grid*m_sprsize.XAspect;
+			m_gridy=grid*m_sprsize.YAspect;
+			
+			m_mx=m_width*m_gridx;
+			m_my=m_height*m_gridy;
+			
+			DrawGrid();
+			DrawSprite();
+		}
+
+		void OnNameChanged(object sender, System.EventArgs e)
+		{
+			if (m_sprite!=null)
+			{
+				m_sprite.Name=m_name.Text;
+			}
+		}
+		
+		void OnEnterEditor(object sender, System.EventArgs e)
+		{
+			//m_inEditor=true;
+		}
+		
+		void OnLeaveEditor(object sender, System.EventArgs e)
+		{
+			//m_inEditor=false;
+
+			if (m_drawing && m_mode==Mode.ePlot)
+			{
+				m_undo=new Sprite(m_sprite);
+				ApplyOverlay();
+			}
+
+			m_drawing=false;
+
+			ClearOverlay();
+			DrawSprite();
+		}
+
+		void OnPalette(object sender, System.EventArgs e)
+		{
+			PaletteForm f=new PaletteForm(m_plugin,new Sprite(m_sprite));
+			
+			if (f.ShowDialog()==DialogResult.OK)
+			{
+				m_sprite=f.Sprite;
+				DrawSprite();
+				UpdateColourPreviews();
+			}
+		}
+		
+		void OnModeSelect(object sender, System.EventArgs e)
+		{
+			m_mode=(Mode)m_modeList.SelectedIndex;
+		}
+		
+		void OnUndo(object sender, System.EventArgs e)
+		{
+			if (m_undo!=null)
+			{
+				Sprite s=m_sprite;
+				Sprite=m_undo;
+				m_undo=s;
+			}
+		}
+		
+		void OnMouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
+		{
+			if (m_sprite==null || e.X<0 || e.X>=m_mx || e.Y<0 || e.Y>=m_my)
+			{
+				return;
+			}
+			
+			uint x;
+			uint y;
+			
+			x=(uint)e.X/m_gridx;
+			y=(uint)e.Y/m_gridy;
+			
+			if (x>=m_sprite.Width || y>=m_sprite.Height)
+			{
+				return;
+			}
+
+			if (e.Button==MouseButtons.None)
+			{
+				switch(m_mode)
+				{
+					case Mode.eFloodFill:
+						if (m_drawing)
+						{
+							OverlayFromSprite();
+							FloodFill(m_ox,m_oy,m_pen,m_sprite[m_ox,m_oy]);
+							m_drawing=false;
+							m_undo=new Sprite(m_sprite);
+							ApplyOverlay();
+							DrawSprite();
+						}
+						break;
+						
+					case Mode.eCopyPaste:
+						switch(m_cpMenu.Mode)
+						{
+							case CopyMenu.EMode.eMark:
+								if (m_drawing)
+								{
+									ClearOverlay();
+									Copy(m_ox,m_oy,x,y);
+									DrawSprite();
+									m_drawing=false;
+								}
+								break;
+								
+							case CopyMenu.EMode.ePaste:
+								ClearOverlay();
+								Paste((int)x,(int)y,false);
+								DrawSprite();
+								break;
+								
+							case CopyMenu.EMode.ePasteTransparent:
+								ClearOverlay();
+								Paste((int)x,(int)y,true);
+								DrawSprite();
+								break;
+								
+							default:
+								break;
+						}
+						break;
+						
+					default:
+						if (m_drawing)
+						{
+							m_drawing=false;
+							m_undo=new Sprite(m_sprite);
+							ApplyOverlay();
+							DrawSprite();
+						}
+						break;
+				}
+				
+				return;
+			}
+			
+			if (!m_drawing)
+			{
+				m_ox=x;
+				m_oy=y;
+				m_drawing=true;
+
+				if (e.Button==MouseButtons.Left)
+				{
+					m_pen=(byte)m_leftCol.Value;
+				}
+				else
+				{
+					m_pen=(byte)m_rightCol.Value;
+				}
+			}
+
+			switch(m_mode)
+			{
+				case Mode.ePlot:
+					m_overlay[x,y]=m_pen;
+					DrawSprite();
+					break;
+					
+				case Mode.eLine:
+					ClearOverlay();
+					DrawLine(m_ox,m_oy,x,y,m_pen);
+					DrawSprite();
+					break;
+					
+				case Mode.eRectangle:
+					ClearOverlay();
+					DrawRect(m_ox,m_oy,x,y,m_pen,false);
+					DrawSprite();
+					break;
+					
+				case Mode.eFilledRectangle:
+					ClearOverlay();
+					DrawRect(m_ox,m_oy,x,y,m_pen,true);
+					DrawSprite();
+					break;
+
+				case Mode.eCircle:
+					ClearOverlay();
+					DrawEllipse(m_ox,m_oy,x,y,m_pen,true,false);
+					DrawSprite();
+					break;
+					
+				case Mode.eFilledCircle:
+					ClearOverlay();
+					DrawEllipse(m_ox,m_oy,x,y,m_pen,true,true);
+					DrawSprite();
+					break;
+					
+				case Mode.eEllipse:
+					ClearOverlay();
+					DrawEllipse(m_ox,m_oy,x,y,m_pen,false,false);
+					DrawSprite();
+					break;
+					
+				case Mode.eFilledEllipse:
+					ClearOverlay();
+					DrawEllipse(m_ox,m_oy,x,y,m_pen,false,true);
+					DrawSprite();
+					break;
+					
+				case Mode.eFloodFill:
+					break;
+
+				case Mode.eCopyPaste:
+					if (e.Button==MouseButtons.Right)
+					{
+						m_cpMenu.AllowPaste=(m_cpBuff!=null);
+						m_cpMenu.Show(m_edit,e.X,e.Y);
+					}
+					else
+					{
+						switch(m_cpMenu.Mode)
+						{
+							case CopyMenu.EMode.eMark:
+								ClearOverlay();
+								DrawMarkBox(m_ox,m_oy,x,y);
+								DrawSprite();
+								break;
+								
+							case CopyMenu.EMode.ePaste:
+							case CopyMenu.EMode.ePasteTransparent:
+								m_undo=new Sprite(m_sprite);
+								ApplyOverlay();
+								DrawSprite();
+								m_drawing=false;
+								m_cpMenu.Mode=CopyMenu.EMode.eNothing;
+								break;
+								
+							default:
+								break;
+						}
+					}
+					break;
+			}
+		}
+		
+		void OnColourChanged(object sender, System.EventArgs e)
+		{
+			if (m_plugin!=null && m_sprite!=null)
+			{
+				UpdateColourPreviews();
+			}
+		}
+		
+		#endregion
+	}
+}
diff --git a/MainForm.cs b/MainForm.cs
new file mode 100644
index 0000000..b2212e2
--- /dev/null
+++ b/MainForm.cs
@@ -0,0 +1,1046 @@
+// GfxEd8
+// Copyright (C) 2004  Ian Cowburn
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+// $Id$
+//
+using System;
+using System.Windows.Forms;
+using System.IO;
+using System.Drawing;
+using GfxEdInterface;
+using Noddybox.GUI;
+
+namespace GfxEd8
+{
+	/// <summary>
+	/// Description of MainForm.	
+	/// </summary>
+	public class MainForm : System.Windows.Forms.Form
+	{
+		private System.Windows.Forms.CheckBox m_applyAll;
+		private System.Windows.Forms.Button m_scrollLeftButton;
+		private System.Windows.Forms.Button m_rotateRightButton;
+		private System.Windows.Forms.NumericUpDown m_spriteNo;
+		private System.Windows.Forms.Button m_deleteButton;
+		private System.Windows.Forms.Button m_insertButton;
+		private System.Windows.Forms.Button m_scrollRightButton;
+		private System.Windows.Forms.Button m_clearButton;
+		private System.Windows.Forms.GroupBox m_adjustGroup;
+		private System.Windows.Forms.Button m_scrollDownButton;
+		private System.Windows.Forms.Button m_rotateLeftButton;
+		private System.Windows.Forms.Button m_mirrorHorizButton;
+		private System.Windows.Forms.GroupBox m_editorGroup;
+		private System.Windows.Forms.GroupBox m_controlGroup;
+		private System.Windows.Forms.Button m_scrollUpButton;
+		private System.Windows.Forms.Button m_revertButton;
+		private System.Windows.Forms.Label label3;
+		private System.Windows.Forms.Label label2;
+		private System.Windows.Forms.Label label1;
+		private System.Windows.Forms.NumericUpDown m_duplicateCount;
+		private System.Windows.Forms.Button m_duplicateButton;
+		private System.Windows.Forms.Button m_addButton;
+		private System.Windows.Forms.Label label5;
+		private System.Windows.Forms.Label label4;
+		private System.Windows.Forms.TextBox m_spriteCount;
+		private System.Windows.Forms.Button m_mirrorVerticalButton;
+
+		private GfxEditor		m_editor;
+		private string			m_path;
+		private string			m_exportPath;
+		private MenuTree		m_menu;
+		private MenuItem		m_editorTypeMenu;
+		private IPlugin			m_plugin;
+		private SpriteList		m_sprite;
+		private int				m_index;
+		private bool			m_blockValue;
+		private bool			m_commonPal;
+		
+		private const string	MAGIC="@GFXED8_ISS1";
+
+		public MainForm()
+		{
+			//
+			// The InitializeComponent() call is required for Windows Forms designer support.
+			//
+			InitializeComponent();
+			
+			// For some reason, this crashes the designer since I put them
+			// inside groups...
+			//
+			m_editor=new GfxEditor();
+			m_editor.Location=new Point(4,16);
+			
+			m_editorGroup.Controls.Add(m_editor);
+
+			PluginControl.Initialise("gfxed8config.xml");
+			
+			if (PluginControl.Names.Count==0)
+			{
+				throw new Exception("No plugins defined");
+			}
+			
+			m_sprite=new SpriteList();
+			
+			Menu=new MainMenu();
+			
+			m_menu=new MenuTree(Menu);
+			m_editorTypeMenu=new MenuItem("Editor Type");
+			
+			foreach (string s in PluginControl.Names)
+			{
+				m_editorTypeMenu.MenuItems.Add
+					(new MenuItem(s,new EventHandler(OnEditorChanged)));
+			}
+			
+			m_menu.Add("/file", "File");
+			m_menu.Add("/file/new", "New", new EventHandler(OnNew));
+			m_menu.Add("/file/open", "Open...", new EventHandler(OnOpen));
+			m_menu.Add("/file/save", "Save", new EventHandler(OnSave));
+			m_menu.Add("/file/saveas", "Save as...", new EventHandler(OnSaveAs));
+			m_menu.Add("/file/quit", "Quit", new EventHandler(OnQuit));
+			
+			m_menu.Add("/editor", m_editorTypeMenu);
+			
+			m_menu.Add("/plugin","Plugin");
+			m_menu.Add("/plugin/export","Export...",new EventHandler(OnExport));
+			m_menu.Add("/plugin/config","Config...",new EventHandler(OnConfig));
+			m_menu.Add("/plugin/process","Process");
+			m_menu.Add("/plugin/process/one","Current Sprite...",new EventHandler(OnProcessOne));
+			m_menu.Add("/plugin/process/all","All Sprites...",new EventHandler(OnProcessAll));
+			
+			m_menu.Add("/help","Help");
+			m_menu.Add("/help/about","About...", new EventHandler(OnAbout));
+			
+			m_menu.Get("/file/save").Enabled=false;
+		
+			m_path="";
+			m_exportPath="";
+			
+			m_editorTypeMenu.MenuItems[0].Checked=true;
+			
+			InitialiseEditor(m_editorTypeMenu.MenuItems[0]);
+		}
+		
+		[STAThread]
+		public static void Main(string[] args)
+		{
+			//try
+			{
+				Application.Run(new MainForm());
+			}
+			//catch (Exception e)
+			{
+				//Util.Error(e.Message);
+			}
+		}
+		
+		#region Windows Forms Designer generated code
+		/// <summary>
+		/// This method is required for Windows Forms designer support.
+		/// Do not change the method contents inside the source code editor. The Forms designer might
+		/// not be able to load this method if it was changed manually.
+		/// </summary>
+		private void InitializeComponent() {
+			this.m_mirrorVerticalButton = new System.Windows.Forms.Button();
+			this.m_spriteCount = new System.Windows.Forms.TextBox();
+			this.label4 = new System.Windows.Forms.Label();
+			this.label5 = new System.Windows.Forms.Label();
+			this.m_addButton = new System.Windows.Forms.Button();
+			this.m_duplicateButton = new System.Windows.Forms.Button();
+			this.m_duplicateCount = new System.Windows.Forms.NumericUpDown();
+			this.label1 = new System.Windows.Forms.Label();
+			this.label2 = new System.Windows.Forms.Label();
+			this.label3 = new System.Windows.Forms.Label();
+			this.m_revertButton = new System.Windows.Forms.Button();
+			this.m_scrollUpButton = new System.Windows.Forms.Button();
+			this.m_controlGroup = new System.Windows.Forms.GroupBox();
+			this.m_editorGroup = new System.Windows.Forms.GroupBox();
+			this.m_mirrorHorizButton = new System.Windows.Forms.Button();
+			this.m_rotateLeftButton = new System.Windows.Forms.Button();
+			this.m_scrollDownButton = new System.Windows.Forms.Button();
+			this.m_adjustGroup = new System.Windows.Forms.GroupBox();
+			this.m_clearButton = new System.Windows.Forms.Button();
+			this.m_scrollRightButton = new System.Windows.Forms.Button();
+			this.m_insertButton = new System.Windows.Forms.Button();
+			this.m_deleteButton = new System.Windows.Forms.Button();
+			this.m_spriteNo = new System.Windows.Forms.NumericUpDown();
+			this.m_rotateRightButton = new System.Windows.Forms.Button();
+			this.m_scrollLeftButton = new System.Windows.Forms.Button();
+			this.m_applyAll = new System.Windows.Forms.CheckBox();
+			((System.ComponentModel.ISupportInitialize)(this.m_duplicateCount)).BeginInit();
+			this.m_controlGroup.SuspendLayout();
+			this.m_adjustGroup.SuspendLayout();
+			((System.ComponentModel.ISupportInitialize)(this.m_spriteNo)).BeginInit();
+			this.SuspendLayout();
+			// 
+			// m_mirrorVerticalButton
+			// 
+			this.m_mirrorVerticalButton.Location = new System.Drawing.Point(80, 96);
+			this.m_mirrorVerticalButton.Name = "m_mirrorVerticalButton";
+			this.m_mirrorVerticalButton.Size = new System.Drawing.Size(64, 24);
+			this.m_mirrorVerticalButton.TabIndex = 4;
+			this.m_mirrorVerticalButton.Text = "Vertical";
+			this.m_mirrorVerticalButton.Click += new System.EventHandler(this.OnVerticalMirror);
+			// 
+			// m_spriteCount
+			// 
+			this.m_spriteCount.Location = new System.Drawing.Point(88, 24);
+			this.m_spriteCount.Name = "m_spriteCount";
+			this.m_spriteCount.ReadOnly = true;
+			this.m_spriteCount.Size = new System.Drawing.Size(48, 20);
+			this.m_spriteCount.TabIndex = 10;
+			this.m_spriteCount.Text = "textBox1";
+			// 
+			// label4
+			// 
+			this.label4.Location = new System.Drawing.Point(8, 208);
+			this.label4.Name = "label4";
+			this.label4.Size = new System.Drawing.Size(136, 16);
+			this.label4.TabIndex = 11;
+			this.label4.Text = "Rotate";
+			this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+			// 
+			// label5
+			// 
+			this.label5.Location = new System.Drawing.Point(272, 56);
+			this.label5.Name = "label5";
+			this.label5.Size = new System.Drawing.Size(40, 24);
+			this.label5.TabIndex = 17;
+			this.label5.Text = "Times";
+			this.label5.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+			// 
+			// m_addButton
+			// 
+			this.m_addButton.Location = new System.Drawing.Point(144, 24);
+			this.m_addButton.Name = "m_addButton";
+			this.m_addButton.Size = new System.Drawing.Size(64, 24);
+			this.m_addButton.TabIndex = 12;
+			this.m_addButton.Text = "Add";
+			this.m_addButton.Click += new System.EventHandler(this.OnAddSprite);
+			// 
+			// m_duplicateButton
+			// 
+			this.m_duplicateButton.Location = new System.Drawing.Point(144, 56);
+			this.m_duplicateButton.Name = "m_duplicateButton";
+			this.m_duplicateButton.Size = new System.Drawing.Size(64, 24);
+			this.m_duplicateButton.TabIndex = 15;
+			this.m_duplicateButton.Text = "Duplicate";
+			this.m_duplicateButton.Click += new System.EventHandler(this.OnDuplicateSprite);
+			// 
+			// m_duplicateCount
+			// 
+			this.m_duplicateCount.Location = new System.Drawing.Point(216, 56);
+			this.m_duplicateCount.Maximum = new System.Decimal(new int[] {
+						999,
+						0,
+						0,
+						0});
+			this.m_duplicateCount.Minimum = new System.Decimal(new int[] {
+						1,
+						0,
+						0,
+						0});
+			this.m_duplicateCount.Name = "m_duplicateCount";
+			this.m_duplicateCount.Size = new System.Drawing.Size(48, 20);
+			this.m_duplicateCount.TabIndex = 16;
+			this.m_duplicateCount.UpDownAlign = System.Windows.Forms.LeftRightAlignment.Left;
+			this.m_duplicateCount.Value = new System.Decimal(new int[] {
+						7,
+						0,
+						0,
+						0});
+			// 
+			// label1
+			// 
+			this.label1.Location = new System.Drawing.Point(8, 80);
+			this.label1.Name = "label1";
+			this.label1.Size = new System.Drawing.Size(40, 16);
+			this.label1.TabIndex = 3;
+			this.label1.Text = "Mirror";
+			this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+			// 
+			// label2
+			// 
+			this.label2.Location = new System.Drawing.Point(64, 24);
+			this.label2.Name = "label2";
+			this.label2.Size = new System.Drawing.Size(16, 24);
+			this.label2.TabIndex = 9;
+			this.label2.Text = "of";
+			this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+			// 
+			// label3
+			// 
+			this.label3.Location = new System.Drawing.Point(8, 128);
+			this.label3.Name = "label3";
+			this.label3.Size = new System.Drawing.Size(136, 16);
+			this.label3.TabIndex = 6;
+			this.label3.Text = "Scroll";
+			this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+			// 
+			// m_revertButton
+			// 
+			this.m_revertButton.Location = new System.Drawing.Point(360, 24);
+			this.m_revertButton.Name = "m_revertButton";
+			this.m_revertButton.Size = new System.Drawing.Size(64, 24);
+			this.m_revertButton.TabIndex = 14;
+			this.m_revertButton.Text = "Revert";
+			this.m_revertButton.Click += new System.EventHandler(this.OnRevertSprite);
+			// 
+			// m_scrollUpButton
+			// 
+			this.m_scrollUpButton.Location = new System.Drawing.Point(8, 176);
+			this.m_scrollUpButton.Name = "m_scrollUpButton";
+			this.m_scrollUpButton.Size = new System.Drawing.Size(64, 24);
+			this.m_scrollUpButton.TabIndex = 8;
+			this.m_scrollUpButton.Text = "Up";
+			this.m_scrollUpButton.Click += new System.EventHandler(this.OnUpScroll);
+			// 
+			// m_controlGroup
+			// 
+			this.m_controlGroup.Controls.Add(this.label5);
+			this.m_controlGroup.Controls.Add(this.m_duplicateCount);
+			this.m_controlGroup.Controls.Add(this.m_duplicateButton);
+			this.m_controlGroup.Controls.Add(this.m_revertButton);
+			this.m_controlGroup.Controls.Add(this.m_deleteButton);
+			this.m_controlGroup.Controls.Add(this.m_addButton);
+			this.m_controlGroup.Controls.Add(this.m_insertButton);
+			this.m_controlGroup.Controls.Add(this.m_spriteNo);
+			this.m_controlGroup.Controls.Add(this.label2);
+			this.m_controlGroup.Controls.Add(this.m_spriteCount);
+			this.m_controlGroup.Location = new System.Drawing.Point(8, 344);
+			this.m_controlGroup.Name = "m_controlGroup";
+			this.m_controlGroup.Size = new System.Drawing.Size(560, 88);
+			this.m_controlGroup.TabIndex = 12;
+			this.m_controlGroup.TabStop = false;
+			this.m_controlGroup.Text = "Sprite";
+			// 
+			// m_editorGroup
+			// 
+			this.m_editorGroup.Location = new System.Drawing.Point(8, 0);
+			this.m_editorGroup.Name = "m_editorGroup";
+			this.m_editorGroup.Size = new System.Drawing.Size(392, 336);
+			this.m_editorGroup.TabIndex = 13;
+			this.m_editorGroup.TabStop = false;
+			this.m_editorGroup.Text = "Editor";
+			// 
+			// m_mirrorHorizButton
+			// 
+			this.m_mirrorHorizButton.Location = new System.Drawing.Point(8, 96);
+			this.m_mirrorHorizButton.Name = "m_mirrorHorizButton";
+			this.m_mirrorHorizButton.Size = new System.Drawing.Size(64, 24);
+			this.m_mirrorHorizButton.TabIndex = 2;
+			this.m_mirrorHorizButton.Text = "Horizontal";
+			this.m_mirrorHorizButton.Click += new System.EventHandler(this.OnHorizontalMirror);
+			// 
+			// m_rotateLeftButton
+			// 
+			this.m_rotateLeftButton.Location = new System.Drawing.Point(8, 224);
+			this.m_rotateLeftButton.Name = "m_rotateLeftButton";
+			this.m_rotateLeftButton.Size = new System.Drawing.Size(64, 24);
+			this.m_rotateLeftButton.TabIndex = 10;
+			this.m_rotateLeftButton.Text = "Left";
+			this.m_rotateLeftButton.Click += new System.EventHandler(this.OnRotateLeft);
+			// 
+			// m_scrollDownButton
+			// 
+			this.m_scrollDownButton.Location = new System.Drawing.Point(80, 176);
+			this.m_scrollDownButton.Name = "m_scrollDownButton";
+			this.m_scrollDownButton.Size = new System.Drawing.Size(64, 24);
+			this.m_scrollDownButton.TabIndex = 9;
+			this.m_scrollDownButton.Text = "Down";
+			this.m_scrollDownButton.Click += new System.EventHandler(this.OnDownScroll);
+			// 
+			// m_adjustGroup
+			// 
+			this.m_adjustGroup.Controls.Add(this.m_rotateRightButton);
+			this.m_adjustGroup.Controls.Add(this.label4);
+			this.m_adjustGroup.Controls.Add(this.m_rotateLeftButton);
+			this.m_adjustGroup.Controls.Add(this.m_scrollDownButton);
+			this.m_adjustGroup.Controls.Add(this.m_scrollUpButton);
+			this.m_adjustGroup.Controls.Add(this.m_scrollRightButton);
+			this.m_adjustGroup.Controls.Add(this.label3);
+			this.m_adjustGroup.Controls.Add(this.m_scrollLeftButton);
+			this.m_adjustGroup.Controls.Add(this.m_mirrorVerticalButton);
+			this.m_adjustGroup.Controls.Add(this.label1);
+			this.m_adjustGroup.Controls.Add(this.m_mirrorHorizButton);
+			this.m_adjustGroup.Controls.Add(this.m_applyAll);
+			this.m_adjustGroup.Controls.Add(this.m_clearButton);
+			this.m_adjustGroup.Location = new System.Drawing.Point(408, 0);
+			this.m_adjustGroup.Name = "m_adjustGroup";
+			this.m_adjustGroup.Size = new System.Drawing.Size(160, 336);
+			this.m_adjustGroup.TabIndex = 14;
+			this.m_adjustGroup.TabStop = false;
+			this.m_adjustGroup.Text = "Adjust";
+			// 
+			// m_clearButton
+			// 
+			this.m_clearButton.Location = new System.Drawing.Point(8, 48);
+			this.m_clearButton.Name = "m_clearButton";
+			this.m_clearButton.Size = new System.Drawing.Size(64, 24);
+			this.m_clearButton.TabIndex = 0;
+			this.m_clearButton.Text = "Clear";
+			this.m_clearButton.Click += new System.EventHandler(this.OnClear);
+			// 
+			// m_scrollRightButton
+			// 
+			this.m_scrollRightButton.Location = new System.Drawing.Point(80, 144);
+			this.m_scrollRightButton.Name = "m_scrollRightButton";
+			this.m_scrollRightButton.Size = new System.Drawing.Size(64, 24);
+			this.m_scrollRightButton.TabIndex = 7;
+			this.m_scrollRightButton.Text = "Right";
+			this.m_scrollRightButton.Click += new System.EventHandler(this.OnRightScroll);
+			// 
+			// m_insertButton
+			// 
+			this.m_insertButton.Location = new System.Drawing.Point(216, 24);
+			this.m_insertButton.Name = "m_insertButton";
+			this.m_insertButton.Size = new System.Drawing.Size(64, 24);
+			this.m_insertButton.TabIndex = 11;
+			this.m_insertButton.Text = "Insert";
+			this.m_insertButton.Click += new System.EventHandler(this.OnInsertSprite);
+			// 
+			// m_deleteButton
+			// 
+			this.m_deleteButton.Location = new System.Drawing.Point(288, 24);
+			this.m_deleteButton.Name = "m_deleteButton";
+			this.m_deleteButton.Size = new System.Drawing.Size(64, 24);
+			this.m_deleteButton.TabIndex = 13;
+			this.m_deleteButton.Text = "Delete";
+			this.m_deleteButton.Click += new System.EventHandler(this.OnDeleteSprite);
+			// 
+			// m_spriteNo
+			// 
+			this.m_spriteNo.Location = new System.Drawing.Point(8, 24);
+			this.m_spriteNo.Name = "m_spriteNo";
+			this.m_spriteNo.Size = new System.Drawing.Size(56, 20);
+			this.m_spriteNo.TabIndex = 6;
+			this.m_spriteNo.ValueChanged += new System.EventHandler(this.OnSprite);
+			// 
+			// m_rotateRightButton
+			// 
+			this.m_rotateRightButton.Location = new System.Drawing.Point(80, 224);
+			this.m_rotateRightButton.Name = "m_rotateRightButton";
+			this.m_rotateRightButton.Size = new System.Drawing.Size(64, 24);
+			this.m_rotateRightButton.TabIndex = 12;
+			this.m_rotateRightButton.Text = "Right";
+			this.m_rotateRightButton.Click += new System.EventHandler(this.OnRotateRight);
+			// 
+			// m_scrollLeftButton
+			// 
+			this.m_scrollLeftButton.Location = new System.Drawing.Point(8, 144);
+			this.m_scrollLeftButton.Name = "m_scrollLeftButton";
+			this.m_scrollLeftButton.Size = new System.Drawing.Size(64, 24);
+			this.m_scrollLeftButton.TabIndex = 5;
+			this.m_scrollLeftButton.Text = "Left";
+			this.m_scrollLeftButton.Click += new System.EventHandler(this.OnLeftScroll);
+			// 
+			// m_applyAll
+			// 
+			this.m_applyAll.Location = new System.Drawing.Point(8, 16);
+			this.m_applyAll.Name = "m_applyAll";
+			this.m_applyAll.Size = new System.Drawing.Size(120, 24);
+			this.m_applyAll.TabIndex = 1;
+			this.m_applyAll.Text = "Apply to all Sprites";
+			// 
+			// MainForm
+			// 
+			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
+			this.ClientSize = new System.Drawing.Size(578, 447);
+			this.Controls.Add(this.m_adjustGroup);
+			this.Controls.Add(this.m_editorGroup);
+			this.Controls.Add(this.m_controlGroup);
+			this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
+			this.MaximizeBox = false;
+			this.Name = "MainForm";
+			this.Text = "GfxEd8";
+			this.Closing += new System.ComponentModel.CancelEventHandler(this.OnFormClosing);
+			((System.ComponentModel.ISupportInitialize)(this.m_duplicateCount)).EndInit();
+			this.m_controlGroup.ResumeLayout(false);
+			this.m_adjustGroup.ResumeLayout(false);
+			((System.ComponentModel.ISupportInitialize)(this.m_spriteNo)).EndInit();
+			this.ResumeLayout(false);
+		}
+		#endregion
+
+		private void UpdateSpriteCount()
+		{
+			m_blockValue=true;
+
+			m_spriteCount.Text=m_sprite.Count.ToString();
+			m_deleteButton.Enabled=(m_sprite.Count>1);
+			
+			if (m_index>=m_sprite.Count)
+			{
+				m_index=m_sprite.Count-1;
+				m_spriteNo.Value=m_sprite.Count;
+			}
+			
+			m_spriteNo.Maximum=m_sprite.Count;
+
+			m_blockValue=false;
+		}
+		
+		private void InitialiseEditor(MenuItem i)
+		{
+			m_blockValue=true;
+			
+			m_plugin=PluginControl.Get(i.Text);
+			
+			m_commonPal=false;
+			
+			foreach (bool b in m_plugin.SprPalCommon)
+			{
+				m_commonPal|=b;
+			}
+			
+			m_menu.Get("/plugin/config").Enabled=(m_plugin.Config!=null);
+			m_menu.Get("/plugin/process").Enabled=(m_plugin.Process!=null);
+			
+			m_sprite.Clear();
+			m_sprite.Add(new Sprite("",m_plugin.AllowedSizes[0],m_plugin.MaxColours));
+			m_sprite.Changed=false;
+
+			Text="GfxEd8 - " + m_plugin.Description;
+			
+			m_editor.Plugin=m_plugin;
+			m_editor.Sprite=m_sprite[0];
+			
+			m_spriteNo.Value=1;
+			m_spriteNo.Minimum=1;
+			m_spriteNo.Maximum=1;
+			m_index=0;
+
+			m_blockValue=false;
+
+			UpdateSpriteCount();
+		}
+		
+		private void GetEdit()
+		{
+			m_sprite[m_index]=m_editor.Sprite;
+			CheckCommonPal();
+		}
+		
+		private void CheckCommonPal()
+		{
+			if (m_commonPal)
+			{
+				Sprite spr=m_sprite[m_index];
+				
+				foreach (Sprite s in m_sprite)
+				{
+					uint i=0;
+					
+					foreach (bool b in m_plugin.SprPalCommon)
+					{
+						if (b)
+						{
+							s.Pal(i,spr.Pal(i));
+						}
+						
+						++i;
+					}
+				}
+			}
+		}
+		
+		private bool LoadSprites()
+		{
+	    	TextReader str=null;
+	    	
+	    	try
+	    	{
+	    		IPlugin plugin;
+	    		
+	    		str=File.OpenText(m_path);
+
+		    	string s;
+		    	
+		    	s=str.ReadLine();
+		    	
+		    	if (s!=MAGIC)
+		    	{
+		    		Util.Error(m_path,"Not a GfxEd8 file or incompatible version");
+		    		str.Close();
+		    		m_path="";
+		    		return false;
+		    	}
+		    	
+		    	s=str.ReadLine();
+	    		
+	    		plugin=PluginControl.Get(s);
+	    		
+	    		if (plugin==null)
+	    		{
+		    		Util.Error(m_path,"Used an unknown plugin:\n"+s);
+		    		str.Close();
+		    		m_path="";
+		    		return false;
+	    		}
+	    		
+	    		s=str.ReadLine();
+	    		
+	    		if (s!=PluginControl.Version(plugin.ShortName))
+	    		{
+	    			if (!Util.YesNo
+	    			    	("Different version of plugin used to save file.\n"+
+	    			    	 "Loaded plugin version: "+
+	    			    	 PluginControl.Version(plugin.ShortName)+"\n"+
+	    			    	 "File saved with version: "+s+".\n\nTry and load anyway?"))
+					{
+			    		str.Close();
+			    		m_path="";
+			    		return false;
+					}
+	    		}
+	    		
+	    		m_exportPath=str.ReadLine();
+	    		
+	    		if (plugin.Config!=null)
+	    		{
+	    			plugin.Config.Input(str);
+	    		}
+	    		
+	    		foreach (MenuItem i in m_editorTypeMenu.MenuItems)
+	    		{
+	    			if (i.Text==plugin.ShortName)
+	    			{
+	    				i.Checked=true;
+	    				InitialiseEditor(i);
+	    				break;
+	    			}
+	    			else
+	    			{
+	    				i.Checked=false;
+	    			}
+	    		}
+	    		
+	    		m_sprite=SpriteList.Input(str);
+	    		
+	    		UpdateSpriteCount();
+	    		m_editor.Sprite=new Sprite(m_sprite[0]);
+	    		
+	    		str.Close();
+	    		
+	    		return true;
+	    	}
+	    	catch (IOException ex)
+	    	{
+	    		if (str!=null)
+	    		{
+	    			str.Close();
+	    		}
+	    		
+	    		Util.Error(m_path,"Error reading file:\n"+ex.Message);
+	    		m_path="";
+	    		return false;
+	    	}
+		}
+		
+		private void SaveSprites()
+		{
+			GetEdit();
+
+	    	TextWriter str=null;
+	    	
+	    	try
+	    	{
+	    		str=File.CreateText(m_path);
+	    		
+	    		str.WriteLine(MAGIC);
+	    		str.WriteLine(m_plugin.ShortName);
+	    		str.WriteLine(PluginControl.Version(m_plugin.ShortName));
+	    		str.WriteLine(m_exportPath);
+	    		
+	    		if (m_plugin.Config!=null)
+	    		{
+	    			m_plugin.Config.Output(str);
+	    		}
+	    		
+	    		m_sprite.Output(str);
+	    		
+	    		str.Close();
+	    		
+	    		m_sprite.Changed=false;
+	    	}
+	    	catch (IOException ex)
+	    	{
+	    		if (str!=null)
+	    		{
+	    			str.Close();
+	    		}
+	    		
+	    		Util.Error(m_path,"Error writing file:\n"+ex.Message);
+	    		m_path="";
+	    		return;
+	    	}
+		}
+		
+		private void Scroll(int dx, int dy)
+		{
+			GetEdit();
+			
+			if (m_applyAll.Checked)
+			{
+				foreach (Sprite s in m_sprite)
+				{
+					s.Scroll(dx,dy);
+				}
+			}
+			else
+			{
+				m_sprite[m_index].Scroll(dx,dy);
+			}
+			
+			m_sprite.Changed=true;
+			m_editor.Sprite=new Sprite(m_sprite[m_index]);
+		}
+
+		void OnEditorChanged(object sender, System.EventArgs e)
+		{
+			MenuItem i=(MenuItem)sender;
+			
+			if (i.Checked)
+			{
+				return;
+			}
+			
+			if (m_sprite.Changed && !Util.YesNo("Sure you want to change the editor?\n"+
+												"All edits will be lost."))
+			{
+				return;
+			}
+			
+			foreach (MenuItem m in m_editorTypeMenu.MenuItems)
+			{
+				m.Checked=(m==i);
+			}
+			
+			InitialiseEditor(i);
+		}
+		
+		void OnNew(object sender, System.EventArgs e)
+		{
+			if (!m_sprite.Changed || Util.YesNo("Lose current edits?"))
+			{
+				m_sprite.Clear();
+				m_sprite.Add(new Sprite("",m_plugin.AllowedSizes[0],m_plugin.MaxColours));
+				m_sprite.Changed=false;
+				m_menu.Get("/file/save").Enabled=false;
+				m_path="";
+			}
+		}
+		
+		void OnOpen(object sender, System.EventArgs e)
+		{
+			if (!m_sprite.Changed || Util.YesNo("Lose current edits?"))
+			{
+			    OpenFileDialog fsel=new OpenFileDialog();
+
+				fsel.FileName=m_path;
+			    fsel.Filter="GfxEd8 files (*.gf8)|*.gf8|All files (*.*)|*.*";
+			    fsel.FilterIndex=1;
+			
+			    if(fsel.ShowDialog()==DialogResult.OK)
+			    {
+			    	m_path=fsel.FileName;
+			    	
+			    	if (LoadSprites())
+			    	{
+						m_menu.Get("/file/save").Enabled=true;
+			    	}
+			    }
+			}
+		}
+		
+		void OnSave(object sender, System.EventArgs e)
+		{
+			SaveSprites();
+		}
+		
+		void OnSaveAs(object sender, System.EventArgs e)
+		{
+		    SaveFileDialog fsel=new SaveFileDialog();
+
+			fsel.FileName=m_path;
+		    fsel.Filter="GfxEd8 files (*.gf8)|*.gf8|All files (*.*)|*.*";
+		    fsel.FilterIndex=1;
+		
+		    if(fsel.ShowDialog()==DialogResult.OK)
+		    {
+		    	m_path=fsel.FileName;
+		    	
+		    	SaveSprites();
+				m_menu.Get("/file/save").Enabled=true;
+		    }
+		}
+		
+		void OnQuit(object sender, System.EventArgs e)
+		{
+			if (!m_sprite.Changed || Util.YesNo("Lose current edits?"))
+			{
+				m_sprite.Changed=false;
+				Close();
+			}
+		}
+
+		void OnAbout(object sender, System.EventArgs e)
+		{
+			Form about=new About();
+			
+			about.ShowDialog(this);
+		}
+
+		void OnExport(object sender, System.EventArgs e)
+		{
+		    SaveFileDialog fsel=new SaveFileDialog();
+			
+			string ext=m_plugin.ExportExtension;
+
+			fsel.FileName=m_exportPath;
+		    fsel.Filter="Exported files (*."+ext+")|*."+ext+"|All files (*.*)|*.*";
+		    fsel.FilterIndex=1;
+		
+		    if(fsel.ShowDialog()==DialogResult.OK)
+		    {
+		    	GetEdit();
+		    	
+		    	m_exportPath=fsel.FileName;
+		    	
+		    	try
+		    	{
+		    		m_plugin.Export(m_exportPath,m_sprite);
+		    	}
+		    	catch (IOException ex)
+		    	{
+		    		Util.Error(m_path,"Error writing file:\n"+ex.Message);
+		    		return;
+		    	}
+		    	catch (Exception ex)
+		    	{
+		    		Util.Error("Problem exporting sprites:\n"+ex.Message);
+		    	}
+		    }
+
+		}
+
+		void OnConfig(object sender, System.EventArgs e)
+		{
+			m_plugin.Config.Settings(this);
+		}
+
+		void OnProcessOne(object sender, System.EventArgs e)
+		{
+			m_plugin.Process.Single(m_editor.Sprite);
+		}
+
+		void OnProcessAll(object sender, System.EventArgs e)
+		{
+			// TODO: Get sprite
+			m_plugin.Process.All(m_sprite);
+		}
+
+		void OnFormClosing(object sender, System.ComponentModel.CancelEventArgs e)
+		{
+			if (m_sprite.Changed && !Util.YesNo("Lose current edits?"))
+			{
+				e.Cancel=true;
+			}
+		}
+
+		void OnSprite(object sender, System.EventArgs e)
+		{
+			// Cheap, I know...
+			//
+			if (m_blockValue)
+			{
+				return;
+			}
+			
+			GetEdit();
+			m_index=Convert.ToInt32(m_spriteNo.Value)-1;
+			m_editor.Sprite=new Sprite(m_sprite[m_index]);
+		}
+		
+		void OnAddSprite(object sender, System.EventArgs e)
+		{
+			m_sprite.Add(new Sprite("",m_plugin.AllowedSizes[0],m_plugin.MaxColours));
+			UpdateSpriteCount();
+			CheckCommonPal();
+		}
+		
+		void OnInsertSprite(object sender, System.EventArgs e)
+		{
+			GetEdit();
+			m_sprite.Insert(m_index,
+			                new Sprite("",m_plugin.AllowedSizes[0],m_plugin.MaxColours));
+			UpdateSpriteCount();
+			CheckCommonPal();
+			m_editor.Sprite=new Sprite(m_sprite[m_index]);
+		}
+		
+		void OnDeleteSprite(object sender, System.EventArgs e)
+		{
+			m_sprite.RemoveAt(m_index);
+			UpdateSpriteCount();
+			m_editor.Sprite=new Sprite(m_sprite[m_index]);
+		}
+
+		void OnRevertSprite(object sender, System.EventArgs e)
+		{
+			m_editor.Sprite=new Sprite(m_sprite[m_index]);
+		}
+		
+		void OnDuplicateSprite(object sender, System.EventArgs e)
+		{
+			GetEdit();
+			
+			for(int i=m_index;i<m_index+m_duplicateCount.Value;i++)
+			{
+				if (i==m_sprite.Count-1)
+				{
+					m_sprite.Add(new Sprite(m_sprite[i]));
+				}
+				else
+				{
+					m_sprite.Insert(i+1,new Sprite(m_sprite[i]));
+				}
+				
+				m_sprite[i+1].Name=Util.NextName(m_sprite[i].Name);
+			}
+			
+			UpdateSpriteCount();
+			CheckCommonPal();
+		}
+		
+		void OnClear(object sender, System.EventArgs e)
+		{
+			GetEdit();
+			
+			if (m_applyAll.Checked)
+			{
+				foreach (Sprite s in m_sprite)
+				{
+					s.Clear();
+				}
+			}
+			else
+			{
+				m_sprite[m_index].Clear();
+			}
+			
+			m_editor.Sprite=new Sprite(m_sprite[m_index]);
+		}
+		
+		void OnHorizontalMirror(object sender, System.EventArgs e)
+		{
+			GetEdit();
+			
+			if (m_applyAll.Checked)
+			{
+				foreach (Sprite s in m_sprite)
+				{
+					s.MirrorHorizontal();
+				}
+			}
+			else
+			{
+				m_sprite[m_index].MirrorHorizontal();
+			}
+			
+			m_editor.Sprite=new Sprite(m_sprite[m_index]);
+		}
+		
+		void OnVerticalMirror(object sender, System.EventArgs e)
+		{
+			GetEdit();
+			
+			if (m_applyAll.Checked)
+			{
+				foreach (Sprite s in m_sprite)
+				{
+					s.MirrorVertical();
+				}
+			}
+			else
+			{
+				m_sprite[m_index].MirrorVertical();
+			}
+			
+			m_editor.Sprite=new Sprite(m_sprite[m_index]);
+		}
+		
+		void OnLeftScroll(object sender, System.EventArgs e)
+		{
+			Scroll(-1,0);
+		}
+		
+		void OnRightScroll(object sender, System.EventArgs e)
+		{
+			Scroll(1,0);
+		}
+		
+		void OnUpScroll(object sender, System.EventArgs e)
+		{
+			Scroll(0,-1);
+		}
+		
+		void OnDownScroll(object sender, System.EventArgs e)
+		{
+			Scroll(0,1);
+		}
+
+		void OnRotateLeft(object sender, System.EventArgs e)
+		{
+			GetEdit();
+			
+			if (m_applyAll.Checked)
+			{
+				foreach (Sprite s in m_sprite)
+				{
+					s.RotateLeft();
+				}
+			}
+			else
+			{
+				m_sprite[m_index].RotateLeft();
+			}
+			
+			m_editor.Sprite=new Sprite(m_sprite[m_index]);
+		}
+		
+		void OnRotateRight(object sender, System.EventArgs e)
+		{
+			GetEdit();
+			
+			if (m_applyAll.Checked)
+			{
+				foreach (Sprite s in m_sprite)
+				{
+					s.RotateRight();
+				}
+			}
+			else
+			{
+				m_sprite[m_index].RotateRight();
+			}
+			
+			m_editor.Sprite=new Sprite(m_sprite[m_index]);
+		}
+		
+	}
+}
diff --git a/PaletteForm.cs b/PaletteForm.cs
new file mode 100644
index 0000000..b24824b
--- /dev/null
+++ b/PaletteForm.cs
@@ -0,0 +1,205 @@
+// GfxEd8
+// Copyright (C) 2004  Ian Cowburn
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+// $Id$
+//
+using System;
+using System.Windows.Forms;
+using GfxEdInterface;
+
+namespace GfxEd8
+{
+	/// <summary>
+	/// Description of PaletteForm.	
+	/// </summary>
+	public class PaletteForm : System.Windows.Forms.Form
+	{
+		private System.Windows.Forms.NumericUpDown m_index;
+		private System.Windows.Forms.Label m_warning;
+		private System.Windows.Forms.Button m_cancel;
+		private System.Windows.Forms.Label m_col;
+		private System.Windows.Forms.ComboBox m_pal;
+		private System.Windows.Forms.Button m_ok;
+		
+		private IPlugin		m_plugin;
+		private Sprite		m_sprite;
+
+		public PaletteForm(IPlugin plugin, Sprite s)
+		{
+			//
+			// The InitializeComponent() call is required for Windows Forms designer support.
+			//
+			InitializeComponent();
+			
+			m_plugin=plugin;
+			m_sprite=s;
+			
+			CancelButton=m_cancel;
+			
+			foreach (Colour c in m_plugin.Palette)
+			{
+				m_pal.Items.Add(c);
+			}
+			
+			m_index.Minimum=0;
+			m_index.Maximum=m_plugin.MaxColours-1;
+			m_index.Value=1;
+		}
+		
+		public Sprite Sprite
+		{
+			get {return m_sprite;}
+		}
+		
+		#region Windows Forms Designer generated code
+		/// <summary>
+		/// This method is required for Windows Forms designer support.
+		/// Do not change the method contents inside the source code editor. The Forms designer might
+		/// not be able to load this method if it was changed manually.
+		/// </summary>
+		private void InitializeComponent() {
+			this.m_ok = new System.Windows.Forms.Button();
+			this.m_pal = new System.Windows.Forms.ComboBox();
+			this.m_col = new System.Windows.Forms.Label();
+			this.m_cancel = new System.Windows.Forms.Button();
+			this.m_warning = new System.Windows.Forms.Label();
+			this.m_index = new System.Windows.Forms.NumericUpDown();
+			((System.ComponentModel.ISupportInitialize)(this.m_index)).BeginInit();
+			this.SuspendLayout();
+			// 
+			// m_ok
+			// 
+			this.m_ok.Location = new System.Drawing.Point(160, 64);
+			this.m_ok.Name = "m_ok";
+			this.m_ok.Size = new System.Drawing.Size(88, 24);
+			this.m_ok.TabIndex = 0;
+			this.m_ok.Text = "OK";
+			this.m_ok.Click += new System.EventHandler(this.OnOK);
+			// 
+			// m_pal
+			// 
+			this.m_pal.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+			this.m_pal.Location = new System.Drawing.Point(80, 24);
+			this.m_pal.Name = "m_pal";
+			this.m_pal.Size = new System.Drawing.Size(192, 21);
+			this.m_pal.TabIndex = 3;
+			this.m_pal.SelectedIndexChanged += new System.EventHandler(this.OnSelectColour);
+			// 
+			// m_col
+			// 
+			this.m_col.Location = new System.Drawing.Point(280, 16);
+			this.m_col.Name = "m_col";
+			this.m_col.Size = new System.Drawing.Size(64, 40);
+			this.m_col.TabIndex = 4;
+			// 
+			// m_cancel
+			// 
+			this.m_cancel.Location = new System.Drawing.Point(256, 64);
+			this.m_cancel.Name = "m_cancel";
+			this.m_cancel.Size = new System.Drawing.Size(88, 24);
+			this.m_cancel.TabIndex = 1;
+			this.m_cancel.Text = "Cancel";
+			this.m_cancel.Click += new System.EventHandler(this.OnCancel);
+			// 
+			// m_warning
+			// 
+			this.m_warning.Location = new System.Drawing.Point(8, 48);
+			this.m_warning.Name = "m_warning";
+			this.m_warning.Size = new System.Drawing.Size(144, 40);
+			this.m_warning.TabIndex = 5;
+			this.m_warning.Text = "label1";
+			// 
+			// m_index
+			// 
+			this.m_index.Location = new System.Drawing.Point(8, 24);
+			this.m_index.Name = "m_index";
+			this.m_index.Size = new System.Drawing.Size(56, 20);
+			this.m_index.TabIndex = 2;
+			this.m_index.ValueChanged += new System.EventHandler(this.OnSelectIndex);
+			// 
+			// PaletteForm
+			// 
+			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
+			this.ClientSize = new System.Drawing.Size(354, 95);
+			this.ControlBox = false;
+			this.Controls.Add(this.m_warning);
+			this.Controls.Add(this.m_col);
+			this.Controls.Add(this.m_pal);
+			this.Controls.Add(this.m_index);
+			this.Controls.Add(this.m_cancel);
+			this.Controls.Add(this.m_ok);
+			this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+			this.MaximizeBox = false;
+			this.MinimizeBox = false;
+			this.Name = "PaletteForm";
+			this.ShowInTaskbar = false;
+			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+			this.Text = "Edit Sprite Palette";
+			((System.ComponentModel.ISupportInitialize)(this.m_index)).EndInit();
+			this.ResumeLayout(false);
+		}
+		#endregion
+
+		void OnSelectIndex(object sender, System.EventArgs e)
+		{
+			uint i=(uint)m_index.Value;
+			
+			m_pal.SelectedIndex=(int)m_sprite.Pal(i);
+			
+			m_pal.Enabled=m_plugin.SprPalEditable[i];
+			
+			if (m_pal.Enabled && m_plugin.SprPalCommon[i])
+			{
+				m_warning.Text="WARNING:\nThis colour is common amongst all sprites";
+			}
+			else
+			{
+				m_warning.Text="";
+			}
+		}
+		
+		void OnSelectColour(object sender, System.EventArgs e)
+		{
+			uint i=(uint)m_pal.SelectedIndex;
+			
+			if (m_index.Value!=0 && i==0)
+			{
+				Util.Message("The background colour cannot be used\n"+
+				             "for anything but the background");
+				m_pal.SelectedIndex=1;
+			}
+			
+			Colour c=(Colour)m_pal.Items[m_pal.SelectedIndex];
+			
+			m_sprite.Pal((uint)m_index.Value,(uint)m_pal.SelectedIndex);
+			m_col.BackColor=c.DrawCol;
+		}
+		
+		void OnOK(object sender, System.EventArgs e)
+		{
+			DialogResult=DialogResult.OK;
+			Close();
+		}
+		
+		void OnCancel(object sender, System.EventArgs e)
+		{
+			DialogResult=DialogResult.Cancel;
+			Close();
+		}
+		
+	}
+}
diff --git a/PluginControl.cs b/PluginControl.cs
new file mode 100644
index 0000000..82cbd39
--- /dev/null
+++ b/PluginControl.cs
@@ -0,0 +1,217 @@
+// GfxEd8
+// Copyright (C) 2004  Ian Cowburn
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+// $Id$
+//
+using System;
+using System.IO;
+using System.Xml;
+using System.Xml.Serialization;
+using System.Reflection;
+using System.Collections;
+using System.Collections.Specialized;
+using GfxEdInterface;
+using System.Diagnostics;
+
+namespace GfxEd8
+{
+	/// <summary>
+	/// Controls the plugins for the editor
+	/// </summary>
+	public class PluginControl
+	{
+		// -------------------------------------------
+		// PUBLIC
+		// -------------------------------------------
+
+		/// <summary>
+		/// Creates the plugins
+		/// </summary>
+		/// <param name="config">The XML config file</param>
+		public static void Initialise(string config)
+		{
+			XmlDocument xml=new XmlDocument();
+			
+			xml.PreserveWhitespace=false;
+			
+			//Debugger.Break();
+			
+			xml.Load(config);
+			
+			XmlNode node=xml.FirstChild;
+			
+			if (node.Name!="Config")
+			{
+				throw new Exception("Illegal element " + node.Name + " in config");
+			}
+			
+			m_names=new StringCollection();
+			m_interface=new Hashtable();
+			
+			foreach (XmlNode n in node.ChildNodes)
+			{
+				if (n.Name!="Import")
+				{
+					throw new Exception("Illegal element " + node.Name + " in config");
+				}
+
+				PluginData plg=new PluginData();
+				
+				XmlNode dll=n.Attributes.GetNamedItem("dll");
+				XmlNode name=n.Attributes.GetNamedItem("name");
+				
+				if (dll==null || name==null)
+				{
+					throw new Exception("Missing attributes in Import config");
+				}
+				
+				string path=Path.GetFullPath(dll.Value);
+				
+				try
+				{
+					plg.asm=Assembly.LoadFile(path);
+				}
+				catch (System.Security.SecurityException)
+				{
+					throw new Exception("Do not have permission to load:\n"+path);
+				}
+				catch (Exception)
+				{
+					throw new Exception("Could not load plugin:\n"+path);
+				}
+				
+				try
+				{
+					plg.iface=(IPlugin)plg.asm.CreateInstance(name.Value);
+				}
+				catch (InvalidCastException)
+				{
+					throw new Exception("The plugin " + dll.Value+" is incorrect");
+				}
+				
+				Validate(plg.iface,path);
+				
+				m_names.Add(plg.iface.ShortName);
+				m_interface[plg.iface.ShortName]=plg;
+			}
+		}
+		
+		
+		public static StringCollection Names
+		{
+			get {return m_names;}
+		}
+		
+		public static IPlugin Get(string name)
+		{
+			PluginData d=(PluginData)m_interface[name];
+
+			if (d!=null)
+			{
+				return d.iface;
+			}
+			else
+			{
+				return null;
+			}
+		}
+		
+		public static Assembly GetAssembly(string name)
+		{
+			PluginData d=(PluginData)m_interface[name];
+			
+			if (d!=null)
+			{
+				return d.asm;
+			}
+			else
+			{
+				return null;
+			}
+		}
+		
+		public static string Version(string name)
+		{
+			Assembly a=GetAssembly(name);
+			
+			if (a==null)
+			{
+				return "0.0";
+			}
+			
+			AssemblyName n=a.GetName();
+			
+			return n.Version.Major.ToString()+"."+n.Version.Minor.ToString();
+		}
+		
+		// -------------------------------------------
+		// PRIVATE
+		// -------------------------------------------
+		private static StringCollection		m_names=null;
+		private static Hashtable			m_interface=null;
+		
+		private class PluginData
+		{
+			public PluginData()
+			{
+				asm=null;
+				iface=null;
+			}
+			
+			public Assembly	asm;
+			public IPlugin	iface;
+		}
+		
+		private static void Validate(IPlugin p, string path)
+		{
+			foreach (SpriteSize s in p.AllowedSizes)
+			{
+				if (s==null)
+				{
+					throw new Exception("Sprite Sizes incorrecly defined in\n"+path);
+				}
+			}
+
+			foreach (Colour c in p.Palette)
+			{
+				if (c==null)
+				{
+					throw new Exception("Sprite Sizes incorrecly defined in\n"+path);
+				}
+			}
+			
+			if (p.Palette.Length<p.MaxColours)
+			{
+				throw new Exception("Not enough colours defined in\n"+path);
+			}
+
+			if (p.SprPalEditable.Length!=p.MaxColours)
+			{
+				throw new Exception("Wrong editable colour indicators in\n"+path);
+			}
+
+			if (p.SprPalCommon.Length!=p.MaxColours)
+			{
+				throw new Exception("Wrong common colour indicators in\n"+path);
+			}
+		}
+		
+		private PluginControl()
+		{
+		}
+	}
+}
diff --git a/Util.cs b/Util.cs
new file mode 100644
index 0000000..23f3bc7
--- /dev/null
+++ b/Util.cs
@@ -0,0 +1,91 @@
+// GfxEd8
+// Copyright (C) 2004  Ian Cowburn
+// 
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+// 
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+// $Id$
+//
+using System;
+using System.Windows.Forms;
+using System.Text;
+
+namespace GfxEd8
+{
+	/// <summary>
+	/// Usual utils and some state machine
+	/// </summary>
+	public class Util
+	{
+		public static bool YesNo(string s)
+		{
+			return MessageBox.Show(s,"Question",
+			                       MessageBoxButtons.YesNo,
+			                       MessageBoxIcon.Question)==DialogResult.Yes;
+		}
+
+		public static void Message(string s)
+		{
+			MessageBox.Show(s,"Message",MessageBoxButtons.OK,MessageBoxIcon.Information);
+		}
+
+		public static void Error(string s)
+		{
+			MessageBox.Show(s,"ERROR",MessageBoxButtons.OK,MessageBoxIcon.Error);
+		}
+
+		public static void Error(string file, string s)
+		{
+			MessageBox.Show(file+"\n\n"+s,"File Error",MessageBoxButtons.OK,MessageBoxIcon.Error);
+		}
+		
+		public static string NextName(string s)
+		{
+			if (s.Length==0)
+			{
+				return "_1";
+			}
+			
+			string n="";
+			
+			for(int i=s.Length-1;i>0;--i)
+			{
+				if (Char.IsDigit(s[i]))
+				{
+					n=s[i]+n;
+				}
+				else
+				{
+					break;
+				}
+			}
+			
+			if (n.Length>0)
+			{
+				s=s.Remove(s.Length-n.Length,n.Length);
+				s+=(Convert.ToInt32(n)+1).ToString();
+			}
+			else
+			{
+				s+="_1";
+			}
+			
+			return s;
+		}
+		
+		private Util()
+		{
+		}
+	}
+}
diff --git a/gfxed8config.xml b/gfxed8config.xml
new file mode 100644
index 0000000..4ddb54c
--- /dev/null
+++ b/gfxed8config.xml
@@ -0,0 +1,4 @@
+<!-- $Id$ -->
+<Config>
+	<Import dll="GfxEdSpectrum.dll" name="GfxEdSpectrum.Spectrum" />
+</Config>
-- 
cgit v1.2.3