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