diff --git a/SpriteCompiler.GUI/App.config b/SpriteCompiler.GUI/App.config
new file mode 100644
index 0000000..88fa402
--- /dev/null
+++ b/SpriteCompiler.GUI/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SpriteCompiler.GUI/MainForm.Designer.cs b/SpriteCompiler.GUI/MainForm.Designer.cs
new file mode 100644
index 0000000..f065520
--- /dev/null
+++ b/SpriteCompiler.GUI/MainForm.Designer.cs
@@ -0,0 +1,471 @@
+namespace SpriteCompiler.GUI
+{
+ partial class MainForm
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
+ this.mainMenu = new System.Windows.Forms.MenuStrip();
+ this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.newToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.toolStripSeparator = new System.Windows.Forms.ToolStripSeparator();
+ this.saveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.saveAsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
+ this.printToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.printPreviewToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
+ this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.editToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.undoToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.redoToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
+ this.cutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.copyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.pasteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator();
+ this.selectAllToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.toolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.customizeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.optionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.contentsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.indexToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.searchToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.toolStripSeparator5 = new System.Windows.Forms.ToolStripSeparator();
+ this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.openFileDialog = new System.Windows.Forms.OpenFileDialog();
+ this.pictureBox1 = new System.Windows.Forms.PictureBox();
+ this.button1 = new System.Windows.Forms.Button();
+ this.button2 = new System.Windows.Forms.Button();
+ this.label1 = new System.Windows.Forms.Label();
+ this.pictureBox2 = new System.Windows.Forms.PictureBox();
+ this.label2 = new System.Windows.Forms.Label();
+ this.textBox1 = new System.Windows.Forms.TextBox();
+ this.pictureBox3 = new System.Windows.Forms.PictureBox();
+ this.label3 = new System.Windows.Forms.Label();
+ this.mainMenu.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.pictureBox3)).BeginInit();
+ this.SuspendLayout();
+ //
+ // mainMenu
+ //
+ this.mainMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.fileToolStripMenuItem,
+ this.editToolStripMenuItem,
+ this.toolsToolStripMenuItem,
+ this.helpToolStripMenuItem});
+ this.mainMenu.Location = new System.Drawing.Point(0, 0);
+ this.mainMenu.Name = "mainMenu";
+ this.mainMenu.Size = new System.Drawing.Size(1051, 24);
+ this.mainMenu.TabIndex = 0;
+ this.mainMenu.Text = "menuStrip1";
+ //
+ // fileToolStripMenuItem
+ //
+ this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.newToolStripMenuItem,
+ this.openToolStripMenuItem,
+ this.toolStripSeparator,
+ this.saveToolStripMenuItem,
+ this.saveAsToolStripMenuItem,
+ this.toolStripSeparator1,
+ this.printToolStripMenuItem,
+ this.printPreviewToolStripMenuItem,
+ this.toolStripSeparator2,
+ this.exitToolStripMenuItem});
+ this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
+ this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20);
+ this.fileToolStripMenuItem.Text = "&File";
+ //
+ // newToolStripMenuItem
+ //
+ this.newToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("newToolStripMenuItem.Image")));
+ this.newToolStripMenuItem.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.newToolStripMenuItem.Name = "newToolStripMenuItem";
+ this.newToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.N)));
+ this.newToolStripMenuItem.Size = new System.Drawing.Size(146, 22);
+ this.newToolStripMenuItem.Text = "&New";
+ //
+ // openToolStripMenuItem
+ //
+ this.openToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("openToolStripMenuItem.Image")));
+ this.openToolStripMenuItem.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.openToolStripMenuItem.Name = "openToolStripMenuItem";
+ this.openToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O)));
+ this.openToolStripMenuItem.Size = new System.Drawing.Size(146, 22);
+ this.openToolStripMenuItem.Text = "&Open";
+ this.openToolStripMenuItem.Click += new System.EventHandler(this.openToolStripMenuItem_Click);
+ //
+ // toolStripSeparator
+ //
+ this.toolStripSeparator.Name = "toolStripSeparator";
+ this.toolStripSeparator.Size = new System.Drawing.Size(143, 6);
+ //
+ // saveToolStripMenuItem
+ //
+ this.saveToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("saveToolStripMenuItem.Image")));
+ this.saveToolStripMenuItem.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.saveToolStripMenuItem.Name = "saveToolStripMenuItem";
+ this.saveToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.S)));
+ this.saveToolStripMenuItem.Size = new System.Drawing.Size(146, 22);
+ this.saveToolStripMenuItem.Text = "&Save";
+ //
+ // saveAsToolStripMenuItem
+ //
+ this.saveAsToolStripMenuItem.Name = "saveAsToolStripMenuItem";
+ this.saveAsToolStripMenuItem.Size = new System.Drawing.Size(146, 22);
+ this.saveAsToolStripMenuItem.Text = "Save &As";
+ //
+ // toolStripSeparator1
+ //
+ this.toolStripSeparator1.Name = "toolStripSeparator1";
+ this.toolStripSeparator1.Size = new System.Drawing.Size(143, 6);
+ //
+ // printToolStripMenuItem
+ //
+ this.printToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("printToolStripMenuItem.Image")));
+ this.printToolStripMenuItem.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.printToolStripMenuItem.Name = "printToolStripMenuItem";
+ this.printToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.P)));
+ this.printToolStripMenuItem.Size = new System.Drawing.Size(146, 22);
+ this.printToolStripMenuItem.Text = "&Print";
+ //
+ // printPreviewToolStripMenuItem
+ //
+ this.printPreviewToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("printPreviewToolStripMenuItem.Image")));
+ this.printPreviewToolStripMenuItem.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.printPreviewToolStripMenuItem.Name = "printPreviewToolStripMenuItem";
+ this.printPreviewToolStripMenuItem.Size = new System.Drawing.Size(146, 22);
+ this.printPreviewToolStripMenuItem.Text = "Print Pre&view";
+ //
+ // toolStripSeparator2
+ //
+ this.toolStripSeparator2.Name = "toolStripSeparator2";
+ this.toolStripSeparator2.Size = new System.Drawing.Size(143, 6);
+ //
+ // exitToolStripMenuItem
+ //
+ this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
+ this.exitToolStripMenuItem.Size = new System.Drawing.Size(146, 22);
+ this.exitToolStripMenuItem.Text = "E&xit";
+ //
+ // editToolStripMenuItem
+ //
+ this.editToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.undoToolStripMenuItem,
+ this.redoToolStripMenuItem,
+ this.toolStripSeparator3,
+ this.cutToolStripMenuItem,
+ this.copyToolStripMenuItem,
+ this.pasteToolStripMenuItem,
+ this.toolStripSeparator4,
+ this.selectAllToolStripMenuItem});
+ this.editToolStripMenuItem.Name = "editToolStripMenuItem";
+ this.editToolStripMenuItem.Size = new System.Drawing.Size(39, 20);
+ this.editToolStripMenuItem.Text = "&Edit";
+ //
+ // undoToolStripMenuItem
+ //
+ this.undoToolStripMenuItem.Name = "undoToolStripMenuItem";
+ this.undoToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Z)));
+ this.undoToolStripMenuItem.Size = new System.Drawing.Size(144, 22);
+ this.undoToolStripMenuItem.Text = "&Undo";
+ //
+ // redoToolStripMenuItem
+ //
+ this.redoToolStripMenuItem.Name = "redoToolStripMenuItem";
+ this.redoToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Y)));
+ this.redoToolStripMenuItem.Size = new System.Drawing.Size(144, 22);
+ this.redoToolStripMenuItem.Text = "&Redo";
+ //
+ // toolStripSeparator3
+ //
+ this.toolStripSeparator3.Name = "toolStripSeparator3";
+ this.toolStripSeparator3.Size = new System.Drawing.Size(141, 6);
+ //
+ // cutToolStripMenuItem
+ //
+ this.cutToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("cutToolStripMenuItem.Image")));
+ this.cutToolStripMenuItem.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.cutToolStripMenuItem.Name = "cutToolStripMenuItem";
+ this.cutToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.X)));
+ this.cutToolStripMenuItem.Size = new System.Drawing.Size(144, 22);
+ this.cutToolStripMenuItem.Text = "Cu&t";
+ //
+ // copyToolStripMenuItem
+ //
+ this.copyToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("copyToolStripMenuItem.Image")));
+ this.copyToolStripMenuItem.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.copyToolStripMenuItem.Name = "copyToolStripMenuItem";
+ this.copyToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.C)));
+ this.copyToolStripMenuItem.Size = new System.Drawing.Size(144, 22);
+ this.copyToolStripMenuItem.Text = "&Copy";
+ //
+ // pasteToolStripMenuItem
+ //
+ this.pasteToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("pasteToolStripMenuItem.Image")));
+ this.pasteToolStripMenuItem.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.pasteToolStripMenuItem.Name = "pasteToolStripMenuItem";
+ this.pasteToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.V)));
+ this.pasteToolStripMenuItem.Size = new System.Drawing.Size(144, 22);
+ this.pasteToolStripMenuItem.Text = "&Paste";
+ //
+ // toolStripSeparator4
+ //
+ this.toolStripSeparator4.Name = "toolStripSeparator4";
+ this.toolStripSeparator4.Size = new System.Drawing.Size(141, 6);
+ //
+ // selectAllToolStripMenuItem
+ //
+ this.selectAllToolStripMenuItem.Name = "selectAllToolStripMenuItem";
+ this.selectAllToolStripMenuItem.Size = new System.Drawing.Size(144, 22);
+ this.selectAllToolStripMenuItem.Text = "Select &All";
+ //
+ // toolsToolStripMenuItem
+ //
+ this.toolsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.customizeToolStripMenuItem,
+ this.optionsToolStripMenuItem});
+ this.toolsToolStripMenuItem.Name = "toolsToolStripMenuItem";
+ this.toolsToolStripMenuItem.Size = new System.Drawing.Size(47, 20);
+ this.toolsToolStripMenuItem.Text = "&Tools";
+ //
+ // customizeToolStripMenuItem
+ //
+ this.customizeToolStripMenuItem.Name = "customizeToolStripMenuItem";
+ this.customizeToolStripMenuItem.Size = new System.Drawing.Size(130, 22);
+ this.customizeToolStripMenuItem.Text = "&Customize";
+ //
+ // optionsToolStripMenuItem
+ //
+ this.optionsToolStripMenuItem.Name = "optionsToolStripMenuItem";
+ this.optionsToolStripMenuItem.Size = new System.Drawing.Size(130, 22);
+ this.optionsToolStripMenuItem.Text = "&Options";
+ //
+ // helpToolStripMenuItem
+ //
+ this.helpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.contentsToolStripMenuItem,
+ this.indexToolStripMenuItem,
+ this.searchToolStripMenuItem,
+ this.toolStripSeparator5,
+ this.aboutToolStripMenuItem});
+ this.helpToolStripMenuItem.Name = "helpToolStripMenuItem";
+ this.helpToolStripMenuItem.Size = new System.Drawing.Size(44, 20);
+ this.helpToolStripMenuItem.Text = "&Help";
+ //
+ // contentsToolStripMenuItem
+ //
+ this.contentsToolStripMenuItem.Name = "contentsToolStripMenuItem";
+ this.contentsToolStripMenuItem.Size = new System.Drawing.Size(122, 22);
+ this.contentsToolStripMenuItem.Text = "&Contents";
+ //
+ // indexToolStripMenuItem
+ //
+ this.indexToolStripMenuItem.Name = "indexToolStripMenuItem";
+ this.indexToolStripMenuItem.Size = new System.Drawing.Size(122, 22);
+ this.indexToolStripMenuItem.Text = "&Index";
+ //
+ // searchToolStripMenuItem
+ //
+ this.searchToolStripMenuItem.Name = "searchToolStripMenuItem";
+ this.searchToolStripMenuItem.Size = new System.Drawing.Size(122, 22);
+ this.searchToolStripMenuItem.Text = "&Search";
+ //
+ // toolStripSeparator5
+ //
+ this.toolStripSeparator5.Name = "toolStripSeparator5";
+ this.toolStripSeparator5.Size = new System.Drawing.Size(119, 6);
+ //
+ // aboutToolStripMenuItem
+ //
+ this.aboutToolStripMenuItem.Name = "aboutToolStripMenuItem";
+ this.aboutToolStripMenuItem.Size = new System.Drawing.Size(122, 22);
+ this.aboutToolStripMenuItem.Text = "&About...";
+ //
+ // openFileDialog
+ //
+ this.openFileDialog.FileName = "openFileDialog";
+ this.openFileDialog.FileOk += new System.ComponentModel.CancelEventHandler(this.openFileDialog_FileOk);
+ //
+ // pictureBox1
+ //
+ this.pictureBox1.Location = new System.Drawing.Point(12, 40);
+ this.pictureBox1.Name = "pictureBox1";
+ this.pictureBox1.Size = new System.Drawing.Size(200, 344);
+ this.pictureBox1.TabIndex = 1;
+ this.pictureBox1.TabStop = false;
+ //
+ // button1
+ //
+ this.button1.Location = new System.Drawing.Point(13, 391);
+ this.button1.Name = "button1";
+ this.button1.Size = new System.Drawing.Size(75, 23);
+ this.button1.TabIndex = 2;
+ this.button1.Text = "Initialize";
+ this.button1.UseVisualStyleBackColor = true;
+ this.button1.Click += new System.EventHandler(this.button1_Click);
+ //
+ // button2
+ //
+ this.button2.Location = new System.Drawing.Point(95, 391);
+ this.button2.Name = "button2";
+ this.button2.Size = new System.Drawing.Size(75, 23);
+ this.button2.TabIndex = 3;
+ this.button2.Text = "Single Step";
+ this.button2.UseVisualStyleBackColor = true;
+ this.button2.Click += new System.EventHandler(this.button2_Click);
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(9, 24);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(73, 13);
+ this.label1.TabIndex = 5;
+ this.label1.Text = "Source Image";
+ //
+ // pictureBox2
+ //
+ this.pictureBox2.Location = new System.Drawing.Point(218, 40);
+ this.pictureBox2.Name = "pictureBox2";
+ this.pictureBox2.Size = new System.Drawing.Size(200, 344);
+ this.pictureBox2.TabIndex = 6;
+ this.pictureBox2.TabStop = false;
+ //
+ // label2
+ //
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(218, 24);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(50, 13);
+ this.label2.TabIndex = 7;
+ this.label2.Text = "Analyzed";
+ //
+ // textBox1
+ //
+ this.textBox1.Location = new System.Drawing.Point(630, 40);
+ this.textBox1.Multiline = true;
+ this.textBox1.Name = "textBox1";
+ this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Both;
+ this.textBox1.Size = new System.Drawing.Size(408, 344);
+ this.textBox1.TabIndex = 8;
+ //
+ // pictureBox3
+ //
+ this.pictureBox3.Location = new System.Drawing.Point(424, 40);
+ this.pictureBox3.Name = "pictureBox3";
+ this.pictureBox3.Size = new System.Drawing.Size(200, 344);
+ this.pictureBox3.TabIndex = 9;
+ this.pictureBox3.TabStop = false;
+ //
+ // label3
+ //
+ this.label3.AutoSize = true;
+ this.label3.Location = new System.Drawing.Point(421, 24);
+ this.label3.Name = "label3";
+ this.label3.Size = new System.Drawing.Size(50, 13);
+ this.label3.TabIndex = 10;
+ this.label3.Text = "Analyzed";
+ //
+ // MainForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(1051, 421);
+ this.Controls.Add(this.label3);
+ this.Controls.Add(this.pictureBox3);
+ this.Controls.Add(this.textBox1);
+ this.Controls.Add(this.label2);
+ this.Controls.Add(this.pictureBox2);
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.button2);
+ this.Controls.Add(this.button1);
+ this.Controls.Add(this.pictureBox1);
+ this.Controls.Add(this.mainMenu);
+ this.MainMenuStrip = this.mainMenu;
+ this.Name = "MainForm";
+ this.Text = "IIgs Sprite Compiler";
+ this.mainMenu.ResumeLayout(false);
+ this.mainMenu.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.pictureBox3)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.MenuStrip mainMenu;
+ private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem newToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem openToolStripMenuItem;
+ private System.Windows.Forms.ToolStripSeparator toolStripSeparator;
+ private System.Windows.Forms.ToolStripMenuItem saveToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem saveAsToolStripMenuItem;
+ private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
+ private System.Windows.Forms.ToolStripMenuItem printToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem printPreviewToolStripMenuItem;
+ private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
+ private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem editToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem undoToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem redoToolStripMenuItem;
+ private System.Windows.Forms.ToolStripSeparator toolStripSeparator3;
+ private System.Windows.Forms.ToolStripMenuItem cutToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem copyToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem pasteToolStripMenuItem;
+ private System.Windows.Forms.ToolStripSeparator toolStripSeparator4;
+ private System.Windows.Forms.ToolStripMenuItem selectAllToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem toolsToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem customizeToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem optionsToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem helpToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem contentsToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem indexToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem searchToolStripMenuItem;
+ private System.Windows.Forms.ToolStripSeparator toolStripSeparator5;
+ private System.Windows.Forms.ToolStripMenuItem aboutToolStripMenuItem;
+ private System.Windows.Forms.OpenFileDialog openFileDialog;
+ private System.Windows.Forms.PictureBox pictureBox1;
+ private System.Windows.Forms.Button button1;
+ private System.Windows.Forms.Button button2;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.PictureBox pictureBox2;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.TextBox textBox1;
+ private System.Windows.Forms.PictureBox pictureBox3;
+ private System.Windows.Forms.Label label3;
+ }
+}
+
diff --git a/SpriteCompiler.GUI/MainForm.cs b/SpriteCompiler.GUI/MainForm.cs
new file mode 100644
index 0000000..14c28c5
--- /dev/null
+++ b/SpriteCompiler.GUI/MainForm.cs
@@ -0,0 +1,185 @@
+using SpriteCompiler.AI;
+using SpriteCompiler.AI.Queue;
+using SpriteCompiler.Helpers;
+using SpriteCompiler.Problem;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace SpriteCompiler.GUI
+{
+ public partial class MainForm : Form
+ {
+ public MainForm()
+ {
+ InitializeComponent();
+ }
+
+ private void newToolStripMenuItem_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
+ {
+ }
+
+ private void openFileDialog_FileOk(object sender, CancelEventArgs e)
+ {
+ }
+
+ private Bitmap source = null;
+
+ private Bitmap Expand(Bitmap src, int width, int height)
+ {
+ // Create a bitmap exactly the same size as the picture box
+ var bitmap = new Bitmap(width, height);
+ using (var g = Graphics.FromImage(bitmap))
+ {
+ g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
+ g.DrawImage(src, 0, 0, bitmap.Width, bitmap.Height);
+ }
+
+ return bitmap;
+ }
+
+ private void openToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
+ {
+ // Load the image from the file
+ source = new Bitmap(openFileDialog.FileName);
+
+ pictureBox1.Image = Expand(source, pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height);
+ pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
+ // Load and analyze the image...
+ }
+ }
+
+ private SpriteGeneratorSearchProblem problem;
+ private SpriteGeneratorState initialState = null;
+ private ISearch search;
+ private InspectableTreeSearch strategy;
+
+ private void button1_Click(object sender, EventArgs e)
+ {
+ var bgcolor = source.GetPixel(0, 0);
+ var record = BrutalDeluxeClassifier.Decompose(source, bgcolor);
+
+ var classified = new Bitmap(source.Width, source.Height);
+ for (int y = 0; y < source.Height; y++)
+ {
+ for (int x = 0; x < (source.Width / 2); x++)
+ {
+ // A mask value of 255 (0xFF) is a tansparent pair of pixels, use the background color
+ var color = BrutalDeluxeClassifier.ToRGB(record.Classes[x, y]);
+ classified.SetPixel(2 * x, y, color);
+ classified.SetPixel(2 * x + 1, y, color);
+ }
+ }
+
+ pictureBox3.Image = Expand(classified, pictureBox3.ClientSize.Width, pictureBox3.ClientSize.Height); ;
+ pictureBox3.SizeMode = PictureBoxSizeMode.Zoom;
+
+
+ var rb_record = BrutalDeluxeClassifier.DecomposeIntoRedBlueImageMap(source, bgcolor);
+
+ var rb_classified = new Bitmap(source.Width, source.Height);
+ for (int y = 0; y < source.Height; y++)
+ {
+ for (int x = 0; x < (source.Width / 2); x++)
+ {
+ // A mask value of 255 (0xFF) is a tansparent pair of pixels, use the background color
+ var color = BrutalDeluxeClassifier.ToRGB(rb_record.Classes[x, y]);
+ rb_classified.SetPixel(2 * x, y, color);
+ rb_classified.SetPixel(2 * x + 1, y, color);
+ }
+ }
+
+ pictureBox2.Image = Expand(rb_classified, pictureBox2.ClientSize.Width, pictureBox2.ClientSize.Height); ;
+ pictureBox2.SizeMode = PictureBoxSizeMode.Zoom;
+
+ var histogram = BrutalDeluxeClassifier.GenerateStatistics(rb_record);
+ textBox1.Clear();
+
+ textBox1.AppendText("Most Common Values\n");
+ textBox1.AppendText("------------------\n");
+ foreach (var stat in histogram.OrderByDescending(x => x.Value).Take(10))
+ {
+ textBox1.AppendText(String.Format("0x{0:X} : {1}\n", stat.Key, stat.Value));
+ }
+
+ // Initialize the search
+ var maxCycles = 5 + source.Height * (3 + (source.Width / 4 * 31) - 1 + 41) - 1;
+
+ problem = SpriteGeneratorSearchProblem.CreateSearchProblem();
+ initialState = SpriteGeneratorState.Init(rb_record.SpriteData);
+
+ var expander = new SpriteGeneratorNodeExpander();
+ strategy = new InspectableTreeSearch(expander);
+
+ strategy.Initialize(problem, initialState);
+ }
+
+ private void button2_Click(object sender, EventArgs e)
+ {
+ // Execute one step of the search
+ var node = strategy.SearchStep(problem);
+ foreach (var n in strategy.Solution(node))
+ {
+ textBox1.AppendText(String.Format("{0}: {1} ; {2} cycles\n", n.Depth, n.Action, n.PathCost));
+ }
+ textBox1.AppendText(node.State.ToString() + "\n");
+ }
+
+ public class InspectableTreeSearch : AbstractSearchStrategy
+ {
+ private IQueue fringe;
+
+ public InspectableTreeSearch(INodeExpander expander)
+ : base(expander)
+ {
+ }
+
+ public void Initialize(SpriteGeneratorSearchProblem problem, SpriteGeneratorState initialState)
+ {
+ // Create a new fringe
+ // fringe = new Adapters.QueueAdapter();
+ fringe = new LIFO();
+
+ // Add the initial state to the fringe
+ fringe.Enqueue(Expander.CreateNode(initialState));
+ }
+
+ public SpriteGeneratorSearchNode SearchStep(SpriteGeneratorSearchProblem problem)
+ {
+ // If the fringe is empty, return null to indicate that no solution was found
+ if (fringe.Empty)
+ {
+ return null;
+ }
+
+ // Select the next node
+ var node = fringe.Remove();
+
+ // If the node is a solution, then we're done! Otherwise expand the node and add to the queue
+ if (!problem.IsGoal(node.State))
+ {
+ AddNodes(fringe, node, problem);
+ }
+
+ // Return the node that we selected to the caller
+ return node;
+ }
+
+ ///
+ /// Generic tree search. See page 72 in Russell and Norvig
+ ///
+ protected override void AddNodes(IQueue fringe, SpriteGeneratorSearchNode node, ISearchProblem problem)
+ {
+ fringe.AddRange(Expander.Expand(problem, node));
+ }
+ }
+ }
+}
diff --git a/SpriteCompiler.GUI/MainForm.resx b/SpriteCompiler.GUI/MainForm.resx
new file mode 100644
index 0000000..aa067c9
--- /dev/null
+++ b/SpriteCompiler.GUI/MainForm.resx
@@ -0,0 +1,244 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAERSURBVDhPrZDbSgJRGIXnpewd6jXsjSQvIrwoI0RQMChU
+ 0iiDPCGiE3ZCRkvR8VzTeBhnyR5/ccaZNnPhB4t9sdf6Ln5hb8QeathNJFVFKF5C8DqL4ksDVHWGDf7j
+ LHyPg6NjviSaFqlu5yQYR+KpupaIkrMknCxT3Y7v/NYYb0ITK1c3BarbWWhLQ7IR0cTKReyZ6lZ0XYei
+ ztHpK4bAc+h1FgQijzSxMptrGIxVSO0xX3AaStFki7bUMVFmaMm/eJMGfIH/MkGzLep0AXn4h/r3CJV3
+ mS9gn2bY4UY/UzQ7E9TqfeTFtnuB+XAfzSHKr11kSl/uBebDiZ89ZCst3OUkdwL28sIVsE83ock+EIQV
+ 2Mz2wxeg6/UAAAAASUVORK5CYII=
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAJHSURBVDhPxZBdSNNhFMb/F110ZZEVhVBgeeHNICiiuggp
+ olAUyyxI0oSaH1QYC3N+tKnp5ubm1JUua5uuqdNKMwr7kApFItTUkWZqVhSVYmao5Nevvy7UoYR3HXh4
+ 4XCe33nOKyy3lAY7l9RWMo0O/raWXxEyo5spVYTNvOGyfIRPfW+ptOkXqaPl6T83hcRmExSdgzAz3NVm
+ YWyoYla/B+1M9JtxWLPpaH22JORIjI6gKAMB0jyEimIdo4OlbuaprwVMOOMovammpDADc34qppwUrmnl
+ 5Kni3aFlFg2j3y1z5mnRTJccnNIltQhwq0jFry+mOXNtpWZWDx1Z1NhV3C3JwGFOw25SYjVe5oYhiUKd
+ HKMmwQUrMWUw/CF3NnZvvYKqUh1TvUroS3fXe7HXkwidMngTS2t5KLbregSzMY2f3Wr4qKW6LJvGR1rX
+ 0MLor8OhKYTJBn/GHvvxrliCTBrsOqXIoOBHh5K+hmSq7FqmexTQHuUytkaKxuNMNgYyVneA4Qd7GKjc
+ hjLaRzxH7gIU6JIZaEvgtk1D8wsxSWecCDgNzWFMvwxm/PkhRmr3Mli1nW9lvjRdWc0Jf+/5jzRmyWmv
+ S+GOLQu6U6BFjPvqKOP1AYw88WOoZif9DgmfLVtxaj1RSLdwNvrkPCA3M54KqxrnvRia9MKcGrUrqFOt
+ 5H7qKsqT1mGO9+Lqhc2ELdw+U/r0i+gVZ8hMiCDx3DHORwZyKnQ/hw/uYt9uCTskPvh6e7Fp41rWr/Fg
+ g6eHO+A/lyD8ARfG3mk9fv1YAAAAAElFTkSuQmCC
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIySURBVDhPrZLfS5NRGMfff6H7boIuuq2pMZyL1eAt11CW
+ DcOKsB9vpFmaLtNExco0av6CbIVLJ61Wk3BSkT/AFCkRZSpZmrmiJQ41xSaCwdfznL15XEUX0Reem5f3
+ 8znnec4j/Zc8fxYGla91CS3eRTx0z6OpMYS7jmnU1X6B/VYA18snUVoyjsKCt8jLHcH5c36ouCQR2NUJ
+ 1Nas4G9ZXlmFKbULh1Kf8lJxSfI+WeCCyopv6q+/h+DQ/DJ2WV5Ao1FgPegRAveDOS4oLfmq/h6dn/DH
+ 4AJizD4UXJrCAUuzEDgbZrjgou2DiohshIcnQtgme5GTPYbkJKcQ1N8OckHW2REVi+RXuM8fxGaDG4oy
+ ALPZIQQ11Z+5QDk1oKJ/hjv7P2FTfCMOH3mFxMQ6IbhROYWOdrCnBI4dfwPr0V4+bRoY9UzXppMjcDdS
+ rC8hy3YhuFI2gTYf2A4Aza4f7N2/o/zaLB8qDYx6zszwr8P7k1thNFYIweXCMXgeAfedq2xxwjClZUeV
+ Jd2GtDNFETiJwfs8MBjKhMCWN8pgoLoqzE8miH1GjE7G4PsZjE7OQsm9ij2mFg7rdrug1xcJAa2l4w7W
+ r00Cgk/n38S7wBwC04u4UGxHrMHF4CbEJtyDLj5fCDIzhljfSxzeavRgyw4Zj9t64GvvQ0d3P3pfD2Kv
+ 2QqNvgFxDN6urYdWmyMElJMnevh60obRktA701PRtGlg1DOdSkXwzrisaMG/RZLWAE60OMW5fNhvAAAA
+ AElFTkSuQmCC
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIpSURBVDhPtZL/T1JRGMb5p1itrVZbbRpqZbawnBENV1I0
+ jGlByTSyJTXJwq2oKZQb1KAv6JCYWSxvBrkkZUq4CeQEiRABFeLL072Xa0zRra31bO8v57zP5znnPYf1
+ X+TxhWF6O7VtGYcnwbSWijKPOLzYrPSvLPwLS3huGUMlT7o9wGD9grVUBj+icdid03S9tDmgNxNwTgVQ
+ J+rA8XNtWwM+uuZATMwxmQVRycuJFNyzIRitDlScugKzjSgFRGJJaIwEsrk8AsHIhnSL/Ssck37UNipQ
+ I5DjtuYV7uksRYhr2kebhx2eP6nrycFIEh5fBA/1Nvru8q5+PDaOovK0rABwfwugWzcErfkzHhjsePL6
+ E7q1VrTdNUDcrgGvSYlDZHN5XTNOnL8BVe8AJAoNDtZfLgDu9L1BPJmikzcrk81hlRwodZJwdBXziwnI
+ OrVoaOkiT8C8hKLHBPO7CbywOaE1jeC+bhAd6meQdvZC1KoG/5IS3MZ2HObLUHZSggvkWq3wOvbWiAqA
+ VpWeyStVfCUNf3AZ4zNhfHCFMEDMgye+hYr6FrDLzxQAUuVTpr0ocn74mchg5vsKRt1RcHp2Qv9+kZ78
+ UcE17KkWFgHNN/uQzgBkGKLJPBZiecyGchjzrmFwPIF++xJUbDbUQzEacIArLpopSRSP4CUN1Obf1Abz
+ uqob5KjiXwWH/GVl5HPt5zZh37GL2H1EiF1VZ7GDI6CNW5r/TSzWbwHYL0mKJ5czAAAAAElFTkSuQmCC
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGCSURBVDhPnZK9S0JRGMb9F1xb2gqaq6mhwCGDtvYIIyLI
+ cJOE1paoIYpMKUjFRDH87lpoakGlIZF9DA2hZJEQhJXl1xPn3HPV29WQfvBwOfA+P95zuDJ39A6/4wyl
+ YOOSMHvOcHGThuwvSKEVRvsR+pQqWD3R1pK98DUbl7Jm5hA8SfESd6S5xH5wycalrO4E0D8yWQuriLH6
+ E2xcSqlcoRJBxCpiTO5TNi4m/ZgDF4nDsOulsfujyGRzUsmWM8YqdcggKbveS3A88bEkslRye58RSzZt
+ IVarY/FFaPmlwp+fUaESYRNW5Vm3BPmpBpZNvppACDmTLbS6FbGAPFAj5OGI4PALOK/yZfIlAlk4j7n5
+ xdaCarWKj0KRXmE2+UklJEJZZ/RCPTPdWvBdLOP1rYD41QNcgRiVkKJQ1mjGsa2VNxeQb2OWDC7sh47p
+ ddQLeoyOTSFiVAAFvVhChsmv2k6Uvd3Icx1UolMNiDdpl4nhLiohW/xb0tMph2JwCJxjAz9A30JI8zYA
+ tAAAAABJRU5ErkJggg==
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGDSURBVDhPrZFNSwJRGIX9NYGbFoUlFElY1EJQKEYhCJsi
+ LaVsERnRF5iCaSZJO1toCDVGFkgoFpWQWWRR2aIvUxm1BKN1wSnHCFw4TOCzue+9nPNw4eVVnav4Izzb
+ QfxeGZ5TWaxT/rK3irzmC7CsusvC1G4IkbNLboIiDieF4GGUKeTeClDpppF8eeEu2PIfwfrzizSdw3Hk
+ EnKlFpkMzV2wH77AosOFTV8A+vkl9CiHuJeLJNNZjM8tYWB0FkTvMAwmy/8ERTR6CwjlGAi1Ccence6C
+ 1NsXzN4PKIxJLLgeIJ2MoXvmFraNBKK3eXZRIveJPvs7FIYniEkXZENOdE+GIZ2Ko10TwLK7tJmKmL0F
+ EEYarYM+NMnt0C1sQzpx/lcSEnZ2gcKY/gs0dlmZuWvmjjmpwA1qxVp2AWFIMAF/OAGBzMjMI7ZrtJCb
+ 4Df3o4Zfxy7QrdxDRFKol5khkpR2H4qmIOzUQNBGwrsXYxccnNOQqNbQ0KGGZ+eEPVwdeLxvqqrf4wGh
+ TNAAAAAASUVORK5CYII=
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHkSURBVDhPvZHfS1NhHIf3p5QypLr2D4goMwoMCi/qIugH
+ Xe1Cr7qKDIMkZixwNhfWLGWbnuki0kXKzLU023KubBNPJrbRdOzocm6e2dPOO21mMS+CHvjcvOf9PF++
+ 79H9M+7RT2iRRsIi9sEAXe43yAvf2LpSHq28G9uAnytNT4jMLewtcQ2Ht2pF8ps/aOt+gccX5lxD694S
+ +1BQFD1RkN5DSFa4Z3uONKbgHE3h8KZ4OJTC1J8UiSzmfhd2uf1CoJHbyKOsZokl0kKwm+aeJaov+wjO
+ rpQkVqdXfOz0bWAcVLghfaXxkUz3y2VxvpMGSwL3uMKh+gHezSSLEnNhX23vtYzKUirDfGyFj/Iy1mdx
+ UWqR8iKhwtQLxjgH659y4EwvVXWPiwJt3/Ws+muywRrlqvkDdx3zQrCN8l1ldnEd3/QqFmkS/akHJYGS
+ zjLzOUEwEsMf+sLI2zmaOou/93pPGoM5zvk7UU7fnBKxSBPoT7SXBNW1F/9Io2lKCNTCeomUyrS8xnBA
+ wfUqyf1eP5U1ptJD/o1LzeNCsHPydtqdr6k4aiwvOHvNSya3ibU/QIdrEkvfhJislc32MfYfuV1eUGPw
+ FF7bIVJVZ0N/soPK421UHGstlFvYd/hWecF/Qqf7CR0A5wwgSQA2AAAAAElFTkSuQmCC
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAJSSURBVDhPtZJrSJNRGMdf6IN9KbpQn/pUEH2JIoLqQ0Zh
+ FqYZRmJG1iKmUqKyLB2pqSm6vC1Nm5GXoeatEsVJ0RASR3eNzegikRq5lrV3857Fr/d9ddlICoL+8OfA
+ Oef/e57zcIT/os7WLMw302muSGJ2689qqi7A44q8IzjtNYzarzHQm8tZtT8FmRqu6LToMxN+B8qhCbGR
+ KVcDE85ajKUaxoaryEuL4UVXIudPB5Ko2oy98xjDptXERuz3hsgAOTzlqqMk6yjdllzE90UM9Wp5azlB
+ S1kwkeG+1CSv4mmBQPThfd6Ahqq8GYB4A11yBKmaMLQxoZyLDkGjDiZOFUhUuB+FsWsUQFiArzegtlzH
+ pFjPpMPA2GA2jucx2KqWK7ZWLqO7dBGP9D5KWLbfto3eAKMhi3FHBeP9GYy9PMXos4OIrYvJrzSRbWjm
+ wuV6EnVG4tLLiEzSExGf4w0oL05nZEDPaK+akceBuO9v4uPtFUrYo6npbzhdE/QPOQmNSiPouHYOUpaf
+ gvgqA/dDf9wd63G1r2SgUlAqyyq/1anYUGfG2mdXwne7bOwJUc1AinOS+NxzBpd5HWLbUhyNPvRdF5S2
+ v05/54tbqvzBifWNHUvPOwLC4/CXwrv2HsB3+w6EwosJOB5ESeElfGpayGD1AmwlArHSm+W2PR1clToo
+ MrbT0mFTVtlbN6xFuJQar3wQz5Q9VksD+7XyPctrJdx4p5s605M5gKz8lJPSDwtGFbKboJ1blAN52vKb
+ PdXm80/AfDokTVu+8DfPXv9XCcIPTvjvLQ8YoakAAAAASUVORK5CYII=
+
+
+
+ 127, 17
+
+
+ 179
+
+
\ No newline at end of file
diff --git a/SpriteCompiler.GUI/Program.cs b/SpriteCompiler.GUI/Program.cs
new file mode 100644
index 0000000..beced70
--- /dev/null
+++ b/SpriteCompiler.GUI/Program.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace SpriteCompiler.GUI
+{
+ static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ static void Main()
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new MainForm());
+ }
+ }
+}
diff --git a/SpriteCompiler.GUI/Properties/AssemblyInfo.cs b/SpriteCompiler.GUI/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..2c52a96
--- /dev/null
+++ b/SpriteCompiler.GUI/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("SpriteCompiler.GUI")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("SpriteCompiler.GUI")]
+[assembly: AssemblyCopyright("Copyright © 2018")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("da09c3ea-7742-4faf-bab9-6b39e994090d")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/SpriteCompiler.GUI/Properties/Resources.Designer.cs b/SpriteCompiler.GUI/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..ca9faf0
--- /dev/null
+++ b/SpriteCompiler.GUI/Properties/Resources.Designer.cs
@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace SpriteCompiler.GUI.Properties
+{
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources
+ {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources()
+ {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager
+ {
+ get
+ {
+ if ((resourceMan == null))
+ {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SpriteCompiler.GUI.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture
+ {
+ get
+ {
+ return resourceCulture;
+ }
+ set
+ {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/SpriteCompiler.GUI/Properties/Resources.resx b/SpriteCompiler.GUI/Properties/Resources.resx
new file mode 100644
index 0000000..af7dbeb
--- /dev/null
+++ b/SpriteCompiler.GUI/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/SpriteCompiler.GUI/Properties/Settings.Designer.cs b/SpriteCompiler.GUI/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..0e4e417
--- /dev/null
+++ b/SpriteCompiler.GUI/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace SpriteCompiler.GUI.Properties
+{
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+ {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default
+ {
+ get
+ {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/SpriteCompiler.GUI/Properties/Settings.settings b/SpriteCompiler.GUI/Properties/Settings.settings
new file mode 100644
index 0000000..3964565
--- /dev/null
+++ b/SpriteCompiler.GUI/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/SpriteCompiler.GUI/SpriteCompiler.GUI.csproj b/SpriteCompiler.GUI/SpriteCompiler.GUI.csproj
new file mode 100644
index 0000000..cc4f393
--- /dev/null
+++ b/SpriteCompiler.GUI/SpriteCompiler.GUI.csproj
@@ -0,0 +1,96 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {DA09C3EA-7742-4FAF-BAB9-6B39E994090D}
+ WinExe
+ Properties
+ SpriteCompiler.GUI
+ SpriteCompiler.GUI
+ v4.5.2
+ 512
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Form
+
+
+ MainForm.cs
+
+
+
+
+ MainForm.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ True
+ Resources.resx
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+
+
+
+ {56f54ca9-17c1-45c6-915b-6fabf4dae5d0}
+ SpriteCompiler
+
+
+
+
+
\ No newline at end of file
diff --git a/SpriteCompiler.Test/MarioTests.cs b/SpriteCompiler.Test/MarioTests.cs
index 7c1e7b7..8635053 100644
--- a/SpriteCompiler.Test/MarioTests.cs
+++ b/SpriteCompiler.Test/MarioTests.cs
@@ -70,7 +70,7 @@ namespace SpriteCompiler.Test
{
// Arrange
var problem = SpriteGeneratorSearchProblem.CreateSearchProblem();
- var search = SpriteGeneratorSearchProblem.Create(80); // max budget of 80 cycles
+ var search = SpriteGeneratorSearchProblem.Create(100); // max budget of 100 cycles
var sprite = new List
{
new SpriteByte(0x11, 0x00, 3),
@@ -97,6 +97,76 @@ namespace SpriteCompiler.Test
// Assert : The initial state IS the goal state
WriteOutSolution(solution);
+
+ // TCS ; 2 cycles
+ // LDA 04,s ; 5 cycles
+ // AND #$0F00 ; 3 cycles
+ // ORA #$1011 ; 3 cycles
+ // STA 04,s ; 5 cycles
+ // LDA #$1111 ; 3 cycles
+ // STA 03,s ; 5 cycles
+ // STA A2,s ; 5 cycles
+ // LDA A4,s ; 5 cycles
+ // AND #$0F00 ; 3 cycles
+ // ORA #$2011 ; 3 cycles
+ // STA A4,s ; 5 cycles
+ // TSC ; 2 cycles
+ // ADC #321 ; 3 cycles
+ // TCS ; 2 cycles
+ // LDA 00,s ; 5 cycles
+ // AND #$00F0 ; 3 cycles
+ // ORA #$1101 ; 3 cycles
+ // STA 00,s ; 5 cycles
+ // LDA 03,s ; 5 cycles
+ // AND #$0F00 ; 3 cycles
+ // ORA #$2012 ; 3 cycles
+ // STA 03,s ; 5 cycles
+ // LDA #$1211 ; 3 cycles
+ // STA 02,s ; 5 cycles
+ //; Total Cost = 94 cycles
+
+ initialHeuristic.Should().BeLessOrEqualTo(solution.Last().PathCost);
+ }
+
+ [TestMethod]
+ public void TestLines_1_To_4()
+ {
+ // Arrange
+ var problem = SpriteGeneratorSearchProblem.CreateSearchProblem();
+ var search = SpriteGeneratorSearchProblem.Create(200); // max budget of 200 cycles
+ var sprite = new List
+ {
+ new SpriteByte(0x11, 0x00, 3),
+ new SpriteByte(0x11, 0x00, 4),
+ new SpriteByte(0x10, 0x0F, 5),
+
+ new SpriteByte(0x11, 0x00, 162),
+ new SpriteByte(0x11, 0x00, 163),
+ new SpriteByte(0x11, 0x00, 164),
+ new SpriteByte(0x20, 0x0F, 165),
+
+ new SpriteByte(0x01, 0xF0, 321),
+ new SpriteByte(0x11, 0x00, 322),
+ new SpriteByte(0x11, 0x00, 323),
+ new SpriteByte(0x12, 0x00, 324),
+ new SpriteByte(0x20, 0x0F, 325),
+
+ new SpriteByte(0x01, 0xF0, 481),
+ new SpriteByte(0x11, 0x00, 482),
+ new SpriteByte(0x11, 0x00, 483),
+ new SpriteByte(0x11, 0x00, 484),
+ new SpriteByte(0x11, 0x00, 485),
+ new SpriteByte(0x11, 0x00, 486)
+ };
+
+ // Act : solve the problem
+ var initialState = SpriteGeneratorState.Init(sprite);
+ var initialHeuristic = problem.Heuristic(initialState);
+
+ var solution = search.Search(problem, initialState);
+
+ // Assert : The initial state IS the goal state
+ WriteOutSolution(solution);
}
private void WriteOutSolution(IEnumerable solution, IntegerCost h0 = null)
diff --git a/SpriteCompiler.sln b/SpriteCompiler.sln
index 720fdb2..e5a1340 100644
--- a/SpriteCompiler.sln
+++ b/SpriteCompiler.sln
@@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpriteCompiler.Test", "Spri
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AI.Test", "AI.Test\AI.Test.csproj", "{D76DB013-8897-4C2D-ACF3-FB55585D3677}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpriteCompiler.GUI", "SpriteCompiler.GUI\SpriteCompiler.GUI.csproj", "{DA09C3EA-7742-4FAF-BAB9-6B39E994090D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -27,6 +29,10 @@ Global
{D76DB013-8897-4C2D-ACF3-FB55585D3677}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D76DB013-8897-4C2D-ACF3-FB55585D3677}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D76DB013-8897-4C2D-ACF3-FB55585D3677}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DA09C3EA-7742-4FAF-BAB9-6B39E994090D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DA09C3EA-7742-4FAF-BAB9-6B39E994090D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DA09C3EA-7742-4FAF-BAB9-6B39E994090D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DA09C3EA-7742-4FAF-BAB9-6B39E994090D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/SpriteCompiler/AI/AbstractSearchNode.cs b/SpriteCompiler/AI/AbstractSearchNode.cs
index 5c5ad8e..4126d68 100644
--- a/SpriteCompiler/AI/AbstractSearchNode.cs
+++ b/SpriteCompiler/AI/AbstractSearchNode.cs
@@ -30,7 +30,7 @@
public int Depth { get { return depth; } }
public S State { get { return state; } }
- public C EstCost { get { return PathCost; } }
+ public virtual C EstCost { get { return PathCost; } }
public C StepCost
{
diff --git a/SpriteCompiler/AI/AbstractSearchStrategy.cs b/SpriteCompiler/AI/AbstractSearchStrategy.cs
index 530aed5..a308efc 100644
--- a/SpriteCompiler/AI/AbstractSearchStrategy.cs
+++ b/SpriteCompiler/AI/AbstractSearchStrategy.cs
@@ -59,7 +59,7 @@
///
///
///
- /// Must be initialize -- usually that means being empty
+ /// Must be initialized -- usually that means being empty
///
///
public virtual IEnumerable Search(ISearchProblem problem, IQueue fringe, S initialState)
@@ -80,12 +80,12 @@
AddNodes(fringe, node, problem);
}
- return Enumerable.Empty();
+ return Enumerable.Empty();
}
///
/// When it's time to actually expand a node and add the new states to the fringe, different
- /// algorhtms can make different choices
+ /// algorithms can make different choices
///
///
///
diff --git a/SpriteCompiler/AI/HeuristicSearchNode.cs b/SpriteCompiler/AI/HeuristicSearchNode.cs
index a3e5c25..ef92d9f 100644
--- a/SpriteCompiler/AI/HeuristicSearchNode.cs
+++ b/SpriteCompiler/AI/HeuristicSearchNode.cs
@@ -2,9 +2,12 @@
{
using System;
+ public interface IHeuristicSearchNodeWithMemory : IHeuristicSearchNode where C : ICost
+ {
+ C F { get; set; }
+ }
public interface IHeuristicSearchNode : ISearchNode where C : ICost
{
- C EstCost { get; }
}
public class HeuristicSearchNode : AbstractSearchNode, IHeuristicSearchNode
@@ -19,7 +22,7 @@
public C Heuristic { get; set; }
- public C EstCost
+ public override C EstCost
{
get
{
diff --git a/SpriteCompiler/AI/IPathCost.cs b/SpriteCompiler/AI/IPathCost.cs
index 56b53dd..16fadce 100644
--- a/SpriteCompiler/AI/IPathCost.cs
+++ b/SpriteCompiler/AI/IPathCost.cs
@@ -2,11 +2,21 @@
{
using System;
+ public static class CostExtensions
+ {
+ public static C Max(this C left, C right) where C : ICost
+ {
+ return (left.CompareTo(right) >= 0)
+ ? left
+ : right;
+ }
+ }
+
public interface ICost : IComparable
{
C Add(C value);
- // Number theoretic values, i.e. C + ZERO = C, C * ONE = C
+ // Number theoretic values, i.e. C + ZERO = C, C * ONE = C
C Zero();
C One();
C Maximum();
diff --git a/SpriteCompiler/AI/ISearch.cs b/SpriteCompiler/AI/ISearch.cs
index 5279f63..90aa91b 100644
--- a/SpriteCompiler/AI/ISearch.cs
+++ b/SpriteCompiler/AI/ISearch.cs
@@ -9,6 +9,6 @@
/// Perform a new search on the specified search problem using the given
/// initial state as a starting point. The method will return an empty
/// list on failure.
- IEnumerable Search(ISearchProblem problem, S initialState);
+ IEnumerable Search(ISearchProblem problem, S initialState);
}
}
diff --git a/SpriteCompiler/AI/Queue/IQueue.cs b/SpriteCompiler/AI/Queue/IQueue.cs
index e3d6ae7..1723a66 100644
--- a/SpriteCompiler/AI/Queue/IQueue.cs
+++ b/SpriteCompiler/AI/Queue/IQueue.cs
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
-
public interface IQueue
{
void Clear();
diff --git a/SpriteCompiler/AI/RecursiveBestFirstSearch.cs b/SpriteCompiler/AI/RecursiveBestFirstSearch.cs
new file mode 100644
index 0000000..051765e
--- /dev/null
+++ b/SpriteCompiler/AI/RecursiveBestFirstSearch.cs
@@ -0,0 +1,88 @@
+namespace SpriteCompiler.AI
+{
+ using Queue;
+ using System;
+ using System.Linq;
+
+#if False
+ public class RecursiveBestFirstSearch : BestFirstSearch
+ where T : IHeuristicSearchNodeWithMemory
+ where C : ICost, new()
+ {
+ public RecursiveBestFirstSearch(ISearchStrategy search, Func> fringe)
+ : base(search, fringe)
+ {
+ }
+ }
+
+ public class RecursiveBestFirstSearchStrategy : AbstractSearchStrategy
+ where T : IHeuristicSearchNodeWithMemory
+ where C : ICost, new()
+ {
+ private static readonly C Cost = new C();
+ private ISearchProblem problem;
+
+ public RecursiveBestFirstSearchStrategy(INodeExpander expander)
+ : base(expander)
+ {
+ }
+
+ public override System.Collections.Generic.IEnumerable Search(ISearchProblem problem, IQueue fringe, S initialState)
+ {
+ RBFS(Expander.CreateNode(initialState), Cost.Zero(), Cost.Maximum());
+ }
+
+ private C RBFS(T node, C F_N, C bound)
+ {
+ var f_N = problem.Heuristic(node.State);
+
+ if (f_N.CompareTo(bound) > 0)
+ {
+ return f_N;
+ }
+
+ if (problem.IsGoal(node.State))
+ {
+ throw new Exception();
+ }
+
+ var children = Expander.Expand(problem, node);
+ if (!children.Any())
+ {
+ return Cost.Maximum();
+ }
+
+ foreach (var N_i in children)
+ {
+ if (f_N.CompareTo(F_N) < 0)
+ {
+ N_i.F = F_N.Max(N_i.EstCost);
+ }
+ else
+ {
+ N_i.F = N_i.EstCost;
+ }
+ }
+
+ children = children.OrderBy(x => x.F);
+
+
+ /*
+ RBFS (node: N, value: F(N), bound: B)
+ IF f(N)>B, RETURN f(N)
+ IF N is a goal, EXIT algorithm
+ IF N has no children, RETURN infinity
+ FOR each child Ni of N,
+ IF f(N) : BestFirstSearch
+ where T : ISMASearchNode
+ where C : ICost, new()
+ {
+ public SimplifiedMemoryBoundedAStarSearch(ISearchStrategy search, Func> fringe)
+ : base(search, fringe)
+ {
+ }
+ }
+
+ public interface ISMASearchNode : IHeuristicSearchNode where C : ICost
+ {
+ C BestForgottenSuccessor { get; }
+ }
+}
diff --git a/SpriteCompiler/Adapters/QueueAdapter.cs b/SpriteCompiler/Adapters/QueueAdapter.cs
index e5cf0fe..fc87b18 100644
--- a/SpriteCompiler/Adapters/QueueAdapter.cs
+++ b/SpriteCompiler/Adapters/QueueAdapter.cs
@@ -14,7 +14,6 @@
where C : ICost
{
private readonly SimplePriorityQueue queue = new SimplePriorityQueue();
-
public bool Empty { get { return queue.Count == 0; } }
public void AddRange(IEnumerable items)
diff --git a/SpriteCompiler/Helpers/BrutalDeluxeClassifier.cs b/SpriteCompiler/Helpers/BrutalDeluxeClassifier.cs
new file mode 100644
index 0000000..f6f82e8
--- /dev/null
+++ b/SpriteCompiler/Helpers/BrutalDeluxeClassifier.cs
@@ -0,0 +1,426 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SpriteCompiler.Helpers
+{
+ public static class BrutalDeluxeClassifier
+ {
+ public static bool IsYellow(this ByteColor color) { return ByteColor.YELLOW.Equals(color); }
+ public static bool IsOrange(this ByteColor color) { return ByteColor.ORANGE.Equals(color); }
+ public static bool IsRed(this ByteColor color) { return ByteColor.RED.Equals(color); }
+ public static bool IsBlue(this ByteColor color) { return ByteColor.BLUE.Equals(color); }
+ public static bool IsGreen(this ByteColor color) { return ByteColor.GREEN.Equals(color); }
+
+ public sealed class Palette
+ {
+ private IDictionary palette = new Dictionary();
+ public Color? MaskColor { get; set; }
+
+ public int this[Color key]
+ {
+ get
+ {
+ return palette[key];
+ }
+ set
+ {
+ palette[key] = value;
+ }
+ }
+
+ public bool Contains(Color color)
+ {
+ return palette.ContainsKey(color);
+ }
+
+ public int Count { get { return palette.Count; } }
+
+ public int RGBToData(Color rgb)
+ {
+ if (MaskColor.HasValue && rgb.Equals(MaskColor.Value))
+ {
+ // Always set masked data values to zero by convention
+ return 0x0;
+ }
+ else
+ {
+ return palette[rgb];
+ }
+ }
+
+ public int RGBToMask(Color rgb)
+ {
+ if (MaskColor.HasValue && rgb.Equals(MaskColor.Value))
+ {
+ return 0xF;
+ }
+ else
+ {
+ return 0x0;
+ }
+ }
+ }
+
+ public enum ByteColor
+ {
+ GREEN,
+ YELLOW,
+ BLUE,
+ ORANGE,
+ PURPLE,
+ RED
+ }
+
+ public static Color ToRGB(ByteColor color)
+ {
+ switch (color)
+ {
+ case ByteColor.GREEN: return Color.PaleGreen;
+ case ByteColor.YELLOW: return Color.Yellow;
+ case ByteColor.BLUE: return Color.Blue;
+ case ByteColor.ORANGE: return Color.Orange;
+ case ByteColor.PURPLE: return Color.Purple;
+ case ByteColor.RED: return Color.Red;
+ }
+
+ return Color.Transparent;
+ }
+
+ public struct ColoredSpriteData
+ {
+ public int Offset;
+ public ByteColor Color;
+ public int Data;
+ public int Mask;
+ }
+
+ public static ByteColor RedBlueClassifier(int mask)
+ {
+ if (mask == 0x00) return ByteColor.RED;
+ if (mask == 0xFF) return ByteColor.GREEN;
+ return ByteColor.BLUE;
+ }
+
+ public static ByteColor YellowBlueClassifier(int mask)
+ {
+ if (mask == 0x00) return ByteColor.YELLOW;
+ if (mask == 0xFF) return ByteColor.GREEN;
+ return ByteColor.BLUE;
+ }
+
+ public static Tuple, List>[] ReductionRules = new []
+ {
+ // B-R-R-B -> P-P-P-P
+ Tuple.Create(new List { ByteColor.BLUE, ByteColor.RED, ByteColor.RED, ByteColor.GREEN }, new List { ByteColor.PURPLE, ByteColor.PURPLE, ByteColor.PURPLE, ByteColor.PURPLE }),
+ Tuple.Create(new List { ByteColor.GREEN, ByteColor.RED, ByteColor.GREEN }, new List { ByteColor.GREEN, ByteColor.YELLOW, ByteColor.YELLOW })
+ };
+
+ public static List Extend(List neighbors, ByteColor current)
+ {
+ // Base case: Just append to an empty list
+ int n = neighbors.Count;
+ if (n == 0)
+ {
+ neighbors.Add(current);
+ return neighbors;
+ }
+
+ // Interesting cases
+ //
+ // ORANGE + ORANGE + YELLOW + YELLOW = RED + RED + RED + RED
+ // RED + YELLOW + YELLOW = RED + RED + RED
+ // YELLOW + YELLOW = ORANGE + ORANGE
+ // YELLOW + BLUE = PURPLE + PURPLE
+ // BLUE + YELLOW = PURPLE + PURPLE
+ // BLUE + BLUE = PURPLE + PURPLE
+ //
+ // Everything else just appends the current classification
+ if (current.IsYellow())
+ {
+ // Check to see if we have a new red span
+ if (n >= 3 && neighbors[n-1].IsYellow() && neighbors[n-2].IsOrange() && neighbors[n-3].IsOrange())
+ {
+ neighbors[n - 3] = neighbors[n - 2] = neighbors[n - 1] = ByteColor.RED;
+ neighbors.Add(ByteColor.RED);
+ return neighbors;
+ }
+
+ if (n >= 2 && neighbors[n-1].IsYellow() && neighbors[n-2].IsRed())
+ {
+ neighbors[n - 1] = ByteColor.RED;
+ neighbors.Add(ByteColor.RED);
+ return neighbors;
+ }
+
+ if (neighbors[n-1].IsYellow())
+ {
+ neighbors[n - 1] = ByteColor.ORANGE;
+ neighbors.Add(ByteColor.ORANGE);
+ return neighbors;
+ }
+
+ if (neighbors[n - 1].IsBlue())
+ {
+ neighbors[n - 1] = ByteColor.PURPLE;
+ neighbors.Add(ByteColor.PURPLE);
+ return neighbors;
+ }
+ }
+ else if (current.IsBlue())
+ {
+ if (neighbors[n - 1].IsYellow())
+ {
+ neighbors[n - 1] = ByteColor.ORANGE;
+ neighbors.Add(ByteColor.ORANGE);
+ return neighbors;
+ }
+
+ if (neighbors[n - 1].IsBlue())
+ {
+ neighbors[n - 1] = ByteColor.PURPLE;
+ neighbors.Add(ByteColor.PURPLE);
+ return neighbors;
+ }
+ }
+
+ neighbors.Add(current);
+ return neighbors;
+ }
+
+ public static Palette ExtractColorPalette(Bitmap bitmap, Color? maskColor = null)
+ {
+ var palette = new Palette();
+ int nextIndex = 1;
+
+ for (int r = 0; r < bitmap.Height; r++)
+ {
+ for (int w = 0; w < bitmap.Width; w++)
+ {
+ var rgb = bitmap.GetPixel(w, r);
+
+ if (!palette.Contains(rgb))
+ {
+ if (palette.Count >= 16)
+ {
+ throw new Exception("Image cannot have more than 15 unique colors");
+ }
+
+ palette[rgb] = nextIndex++;
+ }
+ }
+ }
+
+ palette.MaskColor = maskColor;
+ return palette;
+ }
+
+ public static int[,] Extract4BitPixelData(Bitmap bitmap, Palette palette)
+ {
+ var data_buffer = new int[bitmap.Width, bitmap.Height];
+
+ for (int r = 0; r < bitmap.Height; r++)
+ {
+ for (int w = 0; w < bitmap.Width; w++)
+ {
+ data_buffer[w, r] = palette.RGBToData(bitmap.GetPixel(w, r));
+ }
+ }
+
+ return data_buffer;
+ }
+
+ public static int[,] Extract4BitPixelMask(Bitmap bitmap, Palette palette)
+ {
+ var mask_buffer = new int[bitmap.Width, bitmap.Height];
+
+ for (int r = 0; r < bitmap.Height; r++)
+ {
+ for (int w = 0; w < bitmap.Width; w++)
+ {
+ mask_buffer[w, r] = palette.RGBToMask(bitmap.GetPixel(w, r));
+ }
+ }
+
+ return mask_buffer;
+ }
+
+ public static int[,] Decimate(int[,] fourBitData)
+ {
+ // Conver the 4-bit data into bytes
+ int width = fourBitData.GetLength(0);
+ int height = fourBitData.GetLength(1);
+ var byte_buffer = new int[width / 2, height];
+
+ for (int r = 0; r SpriteData;
+ }
+
+ public static IDictionary GenerateStatistics(SpriteBitmapRecord record)
+ {
+ // Look at the data and find top 16-bit patterns in the image (scan the data/mask arrays and create a list of all 16-bit values)
+ // NOTE: These are overlapping values, a pattern of 0xAA 0x55 0xAA will give values of 0x55AA and 0xAA55
+ var data = record.Data;
+ var mask = record.Mask;
+
+ var width = data.GetLength(0);
+ var height = data.GetLength(1);
+
+ var histogram = new Dictionary();
+ for (int row = 0; row < height; row++)
+ {
+ for (int col = 1; col < width; col++)
+ {
+ if (mask[col, row] == 0x00 && mask[col - 1, row] == 0x00)
+ {
+ var value = data[col, row] * 256 + data[col - 1, row];
+ if (!histogram.ContainsKey(value))
+ {
+ histogram[value] = 0;
+ }
+
+ histogram[value] += 1;
+ }
+ }
+ }
+
+ return histogram;
+ }
+
+ public static SpriteBitmapRecord DecomposeIntoRedBlueImageMap(Bitmap bitmap, Color? maskColor = null)
+ {
+ var sprite = new List();
+
+ // Build the palette of this sprite image
+ var palette = ExtractColorPalette(bitmap, maskColor);
+
+ // Get the 4-bit data and mask arrays
+ var data = Decimate(Extract4BitPixelData(bitmap, palette));
+ var mask = Decimate(Extract4BitPixelMask(bitmap, palette));
+
+ // Start scanning the data, row by row. Each line is so far away from the
+ // previous line that we always reset
+ var width = data.GetLength(0);
+ var height = data.GetLength(1);
+ var classes = new ByteColor[width, height];
+
+ for (int row = 0; row < height; row++)
+ {
+ for (int col = 0; col < width; col++)
+ {
+ classes[col, row] = RedBlueClassifier(mask[col, row]);
+ }
+
+ // Once a full row is classified, create the sprite data
+ for (int col = 0; col < width; col++)
+ {
+ if (mask[col, row] == 0xFF)
+ {
+ continue;
+ }
+
+ sprite.Add(new ColoredSpriteData
+ {
+ Color = classes[col, row],
+ Offset = row * 160 + col,
+ Mask = mask[col, row],
+ Data = data[col, row]
+ });
+ }
+ }
+
+ return new SpriteBitmapRecord
+ {
+ Data = data,
+ Mask = mask,
+ Classes = classes,
+ SpriteData = sprite
+ };
+ }
+
+ public static SpriteBitmapRecord Decompose(Bitmap bitmap, Color? maskColor = null)
+ {
+ // Classified the data according to the method described at http://www.brutaldeluxe.fr/products/crossdevtools/mrspritetech/index.html
+ //
+ // GREEN : is skipped (this is a sparse structure)
+ // YELLOW : Solid, isolated byte (-- -- XX -- --)
+ // BLUE : Mixed, isolated byte (-- -- -X -- --)
+ // ORANGE : Solid, isolated word (-- -- XX XX -- --)
+ // PURPLE : Mixed, isolated word (-- -- -X XX -- --) at least 1 pixel out of 4.
+ // RED: : At least 4 solid pixels (-- -- XX XX XX XX ...)
+ //
+ // A yellow can turn into a purple or red
+ // A blue can be turned into a purple
+ var sprite = new List();
+
+ // Build the palette of this sprite image
+ var palette = ExtractColorPalette(bitmap, maskColor);
+
+ // Get the 4-bit data and mask arrays
+ var data = Decimate(Extract4BitPixelData(bitmap, palette));
+ var mask = Decimate(Extract4BitPixelMask(bitmap, palette));
+
+ // Start scanning the data, row by row. Each line is so far away from the
+ // previous line that we always reset
+ var width = data.GetLength(0);
+ var height = data.GetLength(1);
+ var classes = new ByteColor[width, height];
+
+ for (int row = 0; row < height; row++)
+ {
+ var classification = new List();
+
+ for (int col = 0; col < width; col++)
+ {
+ classes[col, row] = YellowBlueClassifier(mask[col, row]);
+ classification = Extend(classification, classes[col, row]);
+ }
+
+ // Once a full row is classified, create the sprite data
+ for (int col = 0; col < width; col++)
+ {
+ classes[col, row] = classification[col];
+
+ if (classification[col].IsGreen())
+ {
+ continue;
+ }
+
+ sprite.Add(new ColoredSpriteData
+ {
+ Color = classification[col],
+ Offset = row * 160 + col,
+ Mask = mask[col, row],
+ Data = data[col, row]
+ });
+ }
+ }
+
+ return new SpriteBitmapRecord
+ {
+ Data = data,
+ Mask = mask,
+ Classes = classes,
+ SpriteData = sprite
+ };
+ }
+ }
+}
diff --git a/SpriteCompiler/Problem/SpriteGeneratorSearchProblem.cs b/SpriteCompiler/Problem/SpriteGeneratorSearchProblem.cs
index 130a394..b29e7bc 100644
--- a/SpriteCompiler/Problem/SpriteGeneratorSearchProblem.cs
+++ b/SpriteCompiler/Problem/SpriteGeneratorSearchProblem.cs
@@ -4,16 +4,21 @@
using SpriteCompiler.AI.Queue;
using System;
- public sealed class SpriteGeneratorSearchProblem
+ public sealed class SpriteGeneratorSearchProblem : SearchProblem
{
- public static ISearchProblem CreateSearchProblem()
+ public SpriteGeneratorSearchProblem()
+ : base(
+ new SpriteGeneratorGoalTest(),
+ new SpriteGeneratorStepCost(),
+ new SpriteGeneratorSuccessorFunction(),
+ new SpriteGeneratorHeuristicFunction()
+ )
{
- var goalTest = new SpriteGeneratorGoalTest();
- var stepCost = new SpriteGeneratorStepCost();
- var successors = new SpriteGeneratorSuccessorFunction();
- var heuristic = new SpriteGeneratorHeuristicFunction();
+ }
- return new SearchProblem(goalTest, stepCost, successors, heuristic);
+ public static SpriteGeneratorSearchProblem CreateSearchProblem()
+ {
+ return new SpriteGeneratorSearchProblem();
}
public static ISearch Create()
diff --git a/SpriteCompiler/Problem/SpriteGeneratorState.cs b/SpriteCompiler/Problem/SpriteGeneratorState.cs
index 399473b..f17e276 100644
--- a/SpriteCompiler/Problem/SpriteGeneratorState.cs
+++ b/SpriteCompiler/Problem/SpriteGeneratorState.cs
@@ -16,6 +16,11 @@
// Histogram of all possible words -- includes overlaps, e.g. $11 $11 $11 $11 = ($1111, 3)
public static IDictionary DATASET_SOLID_WORDS = null;
+ public static SpriteGeneratorState Init(IEnumerable bytes)
+ {
+ return Init(bytes.Select(x => new SpriteByte((byte)x.Data, (byte)x.Mask, (ushort)x.Offset)));
+ }
+
public static SpriteGeneratorState Init(IEnumerable bytes)
{
DATASET = bytes.OrderBy(x => x.Offset).ToList();
@@ -139,7 +144,9 @@
// Flag that is cleared whenever there is a switch from
// 8/16-bit mode. It is reset once a PHA or STA occurs.
// A PEA instruction has no effect. This gates allowable
- // state transition to prevent long REP/SEP seqences.
+ // state transition to prevent long REP/SEP seqences by ensuring
+ // that switching modes (2 cycles) is always treated as more
+ // expensive than a data store.
public bool AllowModeChange { get; set; }
public const byte LONG_A = 0x10;
diff --git a/SpriteCompiler/Problem/SpriteGeneratorSuccessorFunction.cs b/SpriteCompiler/Problem/SpriteGeneratorSuccessorFunction.cs
index 478fde8..ecb0ce6 100644
--- a/SpriteCompiler/Problem/SpriteGeneratorSuccessorFunction.cs
+++ b/SpriteCompiler/Problem/SpriteGeneratorSuccessorFunction.cs
@@ -81,7 +81,7 @@
// Get the list of remaining bytes by removing the closed list from the global sprite dataset
var open = state.RemainingBytes();
- // If the open list is empty, there can be only one reaons -- we're in an 8-bit
+ // If the open list is empty, there can be only one reason -- we're in 8-bit
// mode.
if (!open.Any())
{
@@ -107,11 +107,11 @@
// bytes and moving the stack forward a bit can allow the code to reach them without needing
// a second stack adjustment.
//
- // 3. Set the stack to the first, right-most offset that end a sequence of solid bytes
+ // 3. Set the stack to the first, right-most offset that ends a sequence of solid bytes
if (!state.S.IsScreenOffset && state.A.IsScreenOffset && state.LongA)
{
// If the first byte is within 255 bytes of the accumulator, propose setting
- // the stack to the accumulator value
+ // the stack to the accumulator value, which is the fastest
var delta = firstByte.Offset - state.A.Value;
if (delta >= 0 && delta < 256)
{
diff --git a/SpriteCompiler/Program.cs b/SpriteCompiler/Program.cs
index cdfc543..157a54a 100644
--- a/SpriteCompiler/Program.cs
+++ b/SpriteCompiler/Program.cs
@@ -1,6 +1,7 @@
namespace SpriteCompiler
{
using Fclp;
+ using SpriteCompiler.Helpers;
using SpriteCompiler.Problem;
using System;
using System.Collections.Generic;
@@ -9,6 +10,31 @@
public static class ExtensionMethods
{
+ public static void Dump(this SpriteCompiler.Helpers.BrutalDeluxeClassifier.ByteColor[,] array)
+ {
+ var rows = array.GetLength(1);
+ var cols = array.GetLength(0);
+
+ for (int r = 0; r < rows; r++)
+ {
+ for (int c = 0; c < cols; c++)
+ {
+ char chr = ' ';
+ switch (array[c, r])
+ {
+ case BrutalDeluxeClassifier.ByteColor.BLUE: chr = 'B'; break;
+ case BrutalDeluxeClassifier.ByteColor.GREEN: chr = 'G'; break;
+ case BrutalDeluxeClassifier.ByteColor.ORANGE: chr = 'O'; break;
+ case BrutalDeluxeClassifier.ByteColor.PURPLE: chr = 'P'; break;
+ case BrutalDeluxeClassifier.ByteColor.RED: chr = 'R'; break;
+ case BrutalDeluxeClassifier.ByteColor.YELLOW: chr = 'Y'; break;
+ }
+ Console.Write(chr);
+ }
+ Console.Write(Environment.NewLine);
+ }
+ }
+
public static void Dump(this int[,] array)
{
var rows = array.GetLength(1);
@@ -18,13 +44,13 @@
{
for (int c = 0; c < cols; c++)
{
- Console.Write(array[c, r].ToString("X1"));
+ Console.Write(array[c, r].ToString("X2"));
}
Console.Write(Environment.NewLine);
}
}
-
}
+
public class ApplicationArguments
{
public List Data { get; set; }
@@ -91,84 +117,16 @@
// Handle the difference command line cases
if (!String.IsNullOrEmpty(filename))
{
- var palette = new Dictionary();
- int nextIndex = 1;
-
- // Convert the image / mask to a paletted image
var bitmap = new Bitmap(filename);
- int[,] data_buffer = new int[bitmap.Width, bitmap.Height];
- int[,] mask_buffer = new int[bitmap.Width, bitmap.Height];
+ var record = BrutalDeluxeClassifier.Decompose(bitmap, maskColor);
- Console.WriteLine(String.Format(" Image is {0} x {1}", bitmap.Width, bitmap.Height));
-
- if (maskColor.HasValue)
- {
- palette[maskColor.Value] = 0;
- }
-
- for (int r = 0; r < bitmap.Height; r++)
- {
- for (int w = 0; w < bitmap.Width; w++)
- {
- var rgb = bitmap.GetPixel(w, r);
-
- if (!palette.ContainsKey(rgb))
- {
- if (palette.Count >= 15)
- {
- throw new Exception("Image cannot have more than 15 unique colors");
- }
- palette[rgb] = nextIndex++;
- }
-
- data_buffer[w, r] = palette[rgb];
-
- if (maskColor.HasValue)
- {
- if (rgb.Equals(maskColor.Value))
- {
- data_buffer[w, r] = 0x0;
- mask_buffer[w, r] = 0xF;
- }
- else
- {
- data_buffer[w, r] = palette[rgb];
- mask_buffer[w, r] = 0x0;
- }
- }
- else
- {
- data_buffer[w, r] = palette[rgb];
- }
- }
- }
-
- data_buffer.Dump();
+ record.Data.Dump();
Console.WriteLine();
- mask_buffer.Dump();
+ record.Mask.Dump();
+ Console.WriteLine();
+ record.Classes.Dump();
- // Pair up pixels to build bytes
- for (int r = 0; r < bitmap.Height; r++)
- {
- for (int w = 0; w < bitmap.Width; w += 2)
- {
- var mask_byte = (byte)((mask_buffer[w, r] << 4) + mask_buffer[w + 1, r]);
- var data_byte = (byte)((data_buffer[w, r] << 4) + data_buffer[w + 1, r]);
- var offset = (ushort)(r * 160 + (w / 2));
-
- // Skip fully transparent bytes
- if (mask_byte == 0xFF)
- {
- continue;
- }
-
- Console.WriteLine(String.Format("Adding ({0:X2}, {1:X2}, {2})", data_byte, mask_byte, offset));
-
- sprite.Add(new SpriteByte(data_byte, mask_byte, offset));
- }
- }
-
- initialState = SpriteGeneratorState.Init(sprite);
+ //initialState = SpriteGeneratorState.Init(sprite);
}
else if (data.Count == mask.Count)
{
@@ -179,9 +137,8 @@
initialState = SpriteGeneratorState.Init(data);
}
- var solution = search.Search(problem, initialState);
-
- WriteOutSolution(solution);
+ //var solution = search.Search(problem, initialState);
+ //WriteOutSolution(solution);
}
}
}
diff --git a/SpriteCompiler/Samples/Barbarian.gif b/SpriteCompiler/Samples/Barbarian.gif
new file mode 100644
index 0000000..b86bdc7
Binary files /dev/null and b/SpriteCompiler/Samples/Barbarian.gif differ
diff --git a/SpriteCompiler/Samples/FullSprite.gif b/SpriteCompiler/Samples/FullSprite.gif
new file mode 100644
index 0000000..9ab2f2b
Binary files /dev/null and b/SpriteCompiler/Samples/FullSprite.gif differ
diff --git a/SpriteCompiler/Samples/Sprite.gif b/SpriteCompiler/Samples/Sprite.gif
new file mode 100644
index 0000000..88144b0
Binary files /dev/null and b/SpriteCompiler/Samples/Sprite.gif differ
diff --git a/SpriteCompiler/SpriteCompiler.csproj b/SpriteCompiler/SpriteCompiler.csproj
index 0f4a7ae..f9307db 100644
--- a/SpriteCompiler/SpriteCompiler.csproj
+++ b/SpriteCompiler/SpriteCompiler.csproj
@@ -37,13 +37,22 @@
..\packages\FluentCommandLineParser.1.4.3\lib\net35\FluentCommandLineParser.dll
True
-
- ..\packages\OptimizedPriorityQueue.4.0.0\lib\net45\Priority Queue.dll
+
+ ..\packages\NLog.4.5.0\lib\net45\NLog.dll
+ True
+
+
+ ..\packages\OptimizedPriorityQueue.4.1.1\lib\net45\Priority Queue.dll
True
+
+
+
+
+
@@ -57,6 +66,8 @@
+
+
@@ -88,6 +99,7 @@
+
diff --git a/SpriteCompiler/packages.config b/SpriteCompiler/packages.config
index 501c013..1e4a89d 100644
--- a/SpriteCompiler/packages.config
+++ b/SpriteCompiler/packages.config
@@ -1,5 +1,6 @@
-
+
+
\ No newline at end of file