From 40ae0b66fe31f304e345c2bdcd7dd59e3d9dc1b1 Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Sat, 12 Oct 2024 23:35:04 -0400 Subject: [PATCH] INI file saving and loading Preset directory selection use APPNAME instead of "Binxelview" for dialog captions --- BinxelviewForm.Designer.cs | 149 ++++++++++----- BinxelviewForm.cs | 373 ++++++++++++++++++++++++++++++++----- readme.txt | 54 +++++- 3 files changed, 487 insertions(+), 89 deletions(-) diff --git a/BinxelviewForm.Designer.cs b/BinxelviewForm.Designer.cs index 7fb7297..b1370a1 100644 --- a/BinxelviewForm.Designer.cs +++ b/BinxelviewForm.Designer.cs @@ -40,20 +40,25 @@ private void InitializeComponent() this.exitFileMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.presetToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.reloadPresetMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.setDirectoryPresetMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.presetMenuSeparator = new System.Windows.Forms.ToolStripSeparator(); this.optionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.decimalPositionOptionsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.hexadecimalPositionOptionsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.optionsMenuSeparator1 = new System.Windows.Forms.ToolStripSeparator(); - this.snapScrollToNextStrideOptionsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.verticalLayoutOptionsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.horizontalLayoutOptionsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.optionsMenuSeparator2 = new System.Windows.Forms.ToolStripSeparator(); this.gridOptionsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.snapScrollToNextStrideOptionsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.optionsMenuSeparator3 = new System.Windows.Forms.ToolStripSeparator(); - this.verticalLayoutOptionsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.horizontalLayoutOptionsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.optionsMenuSeparator4 = new System.Windows.Forms.ToolStripSeparator(); this.twiddleZOptionsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.twiddleNOptionsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.optionsMenuSeparator4 = new System.Windows.Forms.ToolStripSeparator(); + this.loadOptionsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.saveOptionsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.saveOnExitOptionsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.defaultOptionsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.aboutHelpMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.tableTop = new System.Windows.Forms.TableLayoutPanel(); @@ -125,6 +130,7 @@ private void InitializeComponent() this.pixelsToPaletteContextItem = new System.Windows.Forms.ToolStripMenuItem(); this.pixelScroll = new System.Windows.Forms.VScrollBar(); this.toolTip = new System.Windows.Forms.ToolTip(this.components); + this.saveCurrentOptionsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.menuStripMain.SuspendLayout(); this.tableTop.SuspendLayout(); this.groupPacking.SuspendLayout(); @@ -229,6 +235,7 @@ private void InitializeComponent() // this.presetToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.reloadPresetMenuItem, + this.setDirectoryPresetMenuItem, this.presetMenuSeparator}); this.presetToolStripMenuItem.Name = "presetToolStripMenuItem"; this.presetToolStripMenuItem.Size = new System.Drawing.Size(51, 20); @@ -237,14 +244,21 @@ private void InitializeComponent() // reloadPresetMenuItem // this.reloadPresetMenuItem.Name = "reloadPresetMenuItem"; - this.reloadPresetMenuItem.Size = new System.Drawing.Size(110, 22); + this.reloadPresetMenuItem.Size = new System.Drawing.Size(150, 22); this.reloadPresetMenuItem.Text = "&Reload"; - this.reloadPresetMenuItem.Click += new System.EventHandler(this.reloadPresetsToolStripMenuItem_Click); + this.reloadPresetMenuItem.Click += new System.EventHandler(this.reloadPresetMenuItem_Click); + // + // setDirectoryPresetMenuItem + // + this.setDirectoryPresetMenuItem.Name = "setDirectoryPresetMenuItem"; + this.setDirectoryPresetMenuItem.Size = new System.Drawing.Size(150, 22); + this.setDirectoryPresetMenuItem.Text = "Set &Directory..."; + this.setDirectoryPresetMenuItem.Click += new System.EventHandler(this.setDirectoryPresetMenuItem_Click); // // presetMenuSeparator // this.presetMenuSeparator.Name = "presetMenuSeparator"; - this.presetMenuSeparator.Size = new System.Drawing.Size(107, 6); + this.presetMenuSeparator.Size = new System.Drawing.Size(147, 6); // // optionsToolStripMenuItem // @@ -252,15 +266,20 @@ private void InitializeComponent() this.decimalPositionOptionsMenuItem, this.hexadecimalPositionOptionsMenuItem, this.optionsMenuSeparator1, - this.snapScrollToNextStrideOptionsMenuItem, + this.verticalLayoutOptionsMenuItem, + this.horizontalLayoutOptionsMenuItem, this.optionsMenuSeparator2, this.gridOptionsMenuItem, + this.snapScrollToNextStrideOptionsMenuItem, this.optionsMenuSeparator3, - this.verticalLayoutOptionsMenuItem, - this.horizontalLayoutOptionsMenuItem, - this.optionsMenuSeparator4, this.twiddleZOptionsMenuItem, - this.twiddleNOptionsMenuItem}); + this.twiddleNOptionsMenuItem, + this.optionsMenuSeparator4, + this.loadOptionsMenuItem, + this.saveOptionsMenuItem, + this.saveCurrentOptionsMenuItem, + this.saveOnExitOptionsMenuItem, + this.defaultOptionsMenuItem}); this.optionsToolStripMenuItem.Name = "optionsToolStripMenuItem"; this.optionsToolStripMenuItem.Size = new System.Drawing.Size(61, 20); this.optionsToolStripMenuItem.Text = "&Options"; @@ -286,14 +305,21 @@ private void InitializeComponent() this.optionsMenuSeparator1.Name = "optionsMenuSeparator1"; this.optionsMenuSeparator1.Size = new System.Drawing.Size(200, 6); // - // snapScrollToNextStrideOptionsMenuItem + // verticalLayoutOptionsMenuItem // - this.snapScrollToNextStrideOptionsMenuItem.Checked = true; - this.snapScrollToNextStrideOptionsMenuItem.CheckState = System.Windows.Forms.CheckState.Checked; - this.snapScrollToNextStrideOptionsMenuItem.Name = "snapScrollToNextStrideOptionsMenuItem"; - this.snapScrollToNextStrideOptionsMenuItem.Size = new System.Drawing.Size(203, 22); - this.snapScrollToNextStrideOptionsMenuItem.Text = "&Snap scroll to next stride"; - this.snapScrollToNextStrideOptionsMenuItem.Click += new System.EventHandler(this.snapScrollToNextStrideOptionsMenuItem_Click); + this.verticalLayoutOptionsMenuItem.Checked = true; + this.verticalLayoutOptionsMenuItem.CheckState = System.Windows.Forms.CheckState.Checked; + this.verticalLayoutOptionsMenuItem.Name = "verticalLayoutOptionsMenuItem"; + this.verticalLayoutOptionsMenuItem.Size = new System.Drawing.Size(203, 22); + this.verticalLayoutOptionsMenuItem.Text = "&Vertical layout"; + this.verticalLayoutOptionsMenuItem.Click += new System.EventHandler(this.verticalLayoutOptionsMenuItem_Click); + // + // horizontalLayoutOptionsMenuItem + // + this.horizontalLayoutOptionsMenuItem.Name = "horizontalLayoutOptionsMenuItem"; + this.horizontalLayoutOptionsMenuItem.Size = new System.Drawing.Size(203, 22); + this.horizontalLayoutOptionsMenuItem.Text = "H&orizontal layout"; + this.horizontalLayoutOptionsMenuItem.Click += new System.EventHandler(this.horizontalLayoutOptionsMenuItem_Click); // // optionsMenuSeparator2 // @@ -309,32 +335,20 @@ private void InitializeComponent() this.gridOptionsMenuItem.Text = "&Grid Padding"; this.gridOptionsMenuItem.Click += new System.EventHandler(this.gridOptionsMenuItem_Click); // + // snapScrollToNextStrideOptionsMenuItem + // + this.snapScrollToNextStrideOptionsMenuItem.Checked = true; + this.snapScrollToNextStrideOptionsMenuItem.CheckState = System.Windows.Forms.CheckState.Checked; + this.snapScrollToNextStrideOptionsMenuItem.Name = "snapScrollToNextStrideOptionsMenuItem"; + this.snapScrollToNextStrideOptionsMenuItem.Size = new System.Drawing.Size(203, 22); + this.snapScrollToNextStrideOptionsMenuItem.Text = "Sna&p scroll to next stride"; + this.snapScrollToNextStrideOptionsMenuItem.Click += new System.EventHandler(this.snapScrollToNextStrideOptionsMenuItem_Click); + // // optionsMenuSeparator3 // this.optionsMenuSeparator3.Name = "optionsMenuSeparator3"; this.optionsMenuSeparator3.Size = new System.Drawing.Size(200, 6); // - // verticalLayoutOptionsMenuItem - // - this.verticalLayoutOptionsMenuItem.Checked = true; - this.verticalLayoutOptionsMenuItem.CheckState = System.Windows.Forms.CheckState.Checked; - this.verticalLayoutOptionsMenuItem.Name = "verticalLayoutOptionsMenuItem"; - this.verticalLayoutOptionsMenuItem.Size = new System.Drawing.Size(203, 22); - this.verticalLayoutOptionsMenuItem.Text = "&Vertical layout"; - this.verticalLayoutOptionsMenuItem.Click += new System.EventHandler(this.verticalLayoutOptionsMenuItem_Click); - // - // horizontalLayoutOptionsMenuItem - // - this.horizontalLayoutOptionsMenuItem.Name = "horizontalLayoutOptionsMenuItem"; - this.horizontalLayoutOptionsMenuItem.Size = new System.Drawing.Size(203, 22); - this.horizontalLayoutOptionsMenuItem.Text = "H&orizontal layout"; - this.horizontalLayoutOptionsMenuItem.Click += new System.EventHandler(this.horizontalLayoutOptionsMenuItem_Click); - // - // optionsMenuSeparator4 - // - this.optionsMenuSeparator4.Name = "optionsMenuSeparator4"; - this.optionsMenuSeparator4.Size = new System.Drawing.Size(200, 6); - // // twiddleZOptionsMenuItem // this.twiddleZOptionsMenuItem.Name = "twiddleZOptionsMenuItem"; @@ -349,6 +363,41 @@ private void InitializeComponent() this.twiddleNOptionsMenuItem.Text = "Twiddle &N"; this.twiddleNOptionsMenuItem.Click += new System.EventHandler(this.twiddleNOptionsMenuItem_Click); // + // optionsMenuSeparator4 + // + this.optionsMenuSeparator4.Name = "optionsMenuSeparator4"; + this.optionsMenuSeparator4.Size = new System.Drawing.Size(200, 6); + // + // loadOptionsMenuItem + // + this.loadOptionsMenuItem.Name = "loadOptionsMenuItem"; + this.loadOptionsMenuItem.Size = new System.Drawing.Size(203, 22); + this.loadOptionsMenuItem.Text = "&Load Options File..."; + this.loadOptionsMenuItem.Click += new System.EventHandler(this.loadOptionsMenuItem_Click); + // + // saveOptionsMenuItem + // + this.saveOptionsMenuItem.Name = "saveOptionsMenuItem"; + this.saveOptionsMenuItem.Size = new System.Drawing.Size(203, 22); + this.saveOptionsMenuItem.Text = "&Save Options File..."; + this.saveOptionsMenuItem.Click += new System.EventHandler(this.saveOptionsMenuItem_Click); + // + // saveOnExitOptionsMenuItem + // + this.saveOnExitOptionsMenuItem.Checked = true; + this.saveOnExitOptionsMenuItem.CheckState = System.Windows.Forms.CheckState.Checked; + this.saveOnExitOptionsMenuItem.Name = "saveOnExitOptionsMenuItem"; + this.saveOnExitOptionsMenuItem.Size = new System.Drawing.Size(203, 22); + this.saveOnExitOptionsMenuItem.Text = "Save Options on &Exit"; + this.saveOnExitOptionsMenuItem.Click += new System.EventHandler(this.saveOnExitOptionsMenuItem_Click); + // + // defaultOptionsMenuItem + // + this.defaultOptionsMenuItem.Name = "defaultOptionsMenuItem"; + this.defaultOptionsMenuItem.Size = new System.Drawing.Size(203, 22); + this.defaultOptionsMenuItem.Text = "&Reset to Default"; + this.defaultOptionsMenuItem.Click += new System.EventHandler(this.defaultOptionsMenuItem_Click); + // // helpToolStripMenuItem // this.helpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { @@ -1333,6 +1382,13 @@ private void InitializeComponent() this.pixelScroll.TabStop = true; this.pixelScroll.Scroll += new System.Windows.Forms.ScrollEventHandler(this.pixelScroll_Scroll); // + // saveCurrentOptionsMenuItem + // + this.saveCurrentOptionsMenuItem.Name = "saveCurrentOptionsMenuItem"; + this.saveCurrentOptionsMenuItem.Size = new System.Drawing.Size(203, 22); + this.saveCurrentOptionsMenuItem.Text = "Save Current Options"; + this.saveCurrentOptionsMenuItem.Click += new System.EventHandler(this.saveCurrentOptionsMenuItem_Click); + // // BinxelviewForm // this.AllowDrop = true; @@ -1345,6 +1401,7 @@ private void InitializeComponent() this.MainMenuStrip = this.menuStripMain; this.Name = "BinxelviewForm"; this.Text = "Binxelview"; + this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.BinxelviewForm_FormClosed); this.Load += new System.EventHandler(this.BinxelviewForm_Load); this.DragDrop += new System.Windows.Forms.DragEventHandler(this.BinxelviewForm_DragDrop); this.DragEnter += new System.Windows.Forms.DragEventHandler(this.BinxelviewForm_DragEnter); @@ -1470,15 +1527,14 @@ private void InitializeComponent() private System.Windows.Forms.Button buttonBitPos; private System.Windows.Forms.Button buttonZoom; private System.Windows.Forms.Button buttonZero; - private System.Windows.Forms.ToolStripSeparator optionsMenuSeparator1; + private System.Windows.Forms.ToolStripSeparator optionsMenuSeparator2; private System.Windows.Forms.ToolStripMenuItem snapScrollToNextStrideOptionsMenuItem; - private System.Windows.Forms.ToolStripSeparator optionsMenuSeparator3; private System.Windows.Forms.ToolStripMenuItem verticalLayoutOptionsMenuItem; private System.Windows.Forms.ToolStripMenuItem horizontalLayoutOptionsMenuItem; private System.Windows.Forms.ToolStripMenuItem twiddleZOptionsMenuItem; private System.Windows.Forms.ToolStripSeparator optionsMenuSeparator4; private System.Windows.Forms.ToolStripMenuItem twiddleNOptionsMenuItem; - private System.Windows.Forms.ToolStripSeparator optionsMenuSeparator2; + private System.Windows.Forms.ToolStripSeparator optionsMenuSeparator3; private System.Windows.Forms.ToolStripMenuItem gridOptionsMenuItem; private System.Windows.Forms.ToolStripMenuItem exportBinaryChunkFileMenuItem; private System.Windows.Forms.ToolStripSeparator fileMenuSeparator; @@ -1487,6 +1543,13 @@ private void InitializeComponent() private System.Windows.Forms.Button buttonAutoPal; private System.Windows.Forms.ComboBox comboBoxPalette; private System.Windows.Forms.ToolStripMenuItem pixelsToPaletteContextItem; + private System.Windows.Forms.ToolStripMenuItem setDirectoryPresetMenuItem; + private System.Windows.Forms.ToolStripMenuItem loadOptionsMenuItem; + private System.Windows.Forms.ToolStripMenuItem saveOptionsMenuItem; + private System.Windows.Forms.ToolStripMenuItem defaultOptionsMenuItem; + private System.Windows.Forms.ToolStripSeparator optionsMenuSeparator1; + private System.Windows.Forms.ToolStripMenuItem saveOnExitOptionsMenuItem; + private System.Windows.Forms.ToolStripMenuItem saveCurrentOptionsMenuItem; } } diff --git a/BinxelviewForm.cs b/BinxelviewForm.cs index 6a3242f..8f632f0 100644 --- a/BinxelviewForm.cs +++ b/BinxelviewForm.cs @@ -5,11 +5,15 @@ using System.Drawing.Imaging; using System.IO; using System.Windows.Forms; +using static System.Windows.Forms.VisualStyles.VisualStyleElement.ProgressBar; +using System.Xml.Linq; namespace Binxelview { public partial class BinxelviewForm : Form { + const string APPNAME = "Binxelview"; + const string APPDATA_FOLDER = "binxelview"; const int MAX_BPP = 32; const int PRESET_VERSION = 2; const int PALETTE_BITS = 14; // maximum bits to fill 128 x 128 square @@ -24,6 +28,14 @@ enum PaletteMode PALETTE_GREY, PALETTE_CUBEHELIX, }; + static readonly string[] PaletteModeString = + { + "Custom", + "RGB", + "Random", + "Grey", + "Cubehelix", + }; byte[] data = {}; string data_path = ""; @@ -36,12 +48,15 @@ enum PaletteMode long selected_pos = -1; int selected_palette_filter = 0; int option_palette_type; - string palette_path = ""; + string ini_path = ""; + int palette_path_type = -1; Preset preset; Preset default_preset; List presets; + DirectoryInfo dir_cwd, dir_exe, dir_loc; + string ini_exe, ini_loc; Bitmap palette_bmp = new Bitmap(PALETTE_DIM, PALETTE_DIM); Bitmap pixel_bmp = null; Color[] palette = new Color[PALETTE_DIM * PALETTE_DIM]; @@ -68,6 +83,9 @@ enum PaletteMode // If something more important needs a PRESET_VERSION 3, we should add it then to the preset. // We could maybe place rare options like this into an "advanced" menu on the menu bar, // instead of requiring UI panel space for it. + string palette_path = ""; + string preset_dir = ""; + bool save_ini = true; // not changed by defaultOption // // Preset @@ -144,6 +162,7 @@ public Preset copy() public bool saveFile(string path) { + Debug.WriteLine("Preset.saveFile(\""+path+"\")"); try { using (StreamWriter sw = File.CreateText(path)) @@ -176,6 +195,7 @@ public bool saveFile(string path) public bool loadFile(string path) { + Debug.WriteLine("Preset.loadFile(\""+path+"\")"); empty(); try { @@ -195,7 +215,11 @@ public bool loadFile(string path) { string l = tr.ReadLine(); int v = int.Parse(l); - if (v > PRESET_VERSION) return false; + if (v > PRESET_VERSION) + { + last_error = string.Format("Unknown preset version: {0} > {1}",v,PRESET_VERSION); + return false; + } int version = v; l = tr.ReadLine(); @@ -309,6 +333,9 @@ void defaultOption() snap_scroll = true; horizontal_layout = false; twiddle = 0; + palette_path = ""; + preset_dir = ""; + // note: save_ini is not changed, intentionally, default options shouldn't alter the current ini read-only state } string parseOption(string optline, string base_path, bool ini_file) @@ -319,6 +346,30 @@ string parseOption(string optline, string base_path, bool ini_file) string val = optline.Substring(eqpos+1); string valu = val.ToUpperInvariant(); + if (opt == "INI" && !ini_file) + { + string path = val; + if (!Path.IsPathRooted(path)) path = Path.Combine(base_path,path); + ini_path = path; + string result = loadIni(path); + if (result.Length > 0) return "-ini error: "+path+"\n"+result; + return ""; + } + if (opt == "SAVEINI") + { + int v; + if (!int.TryParse(val,out v)) return "Could not parse integer for saveini: "+val; + if (v != 0 && v != 1) return "Saveini value must be 0 or 1: "+val; + save_ini = (v != 0); + return ""; + } + if (opt == "PRESETDIR") // set preset library location and reload presets, selects default if it exists + { + string path = val; + if (!Path.IsPathRooted(path)) path = Path.Combine(base_path,path); + preset_dir = path; + if (reloadPresets()) preset = default_preset.copy(); + } if (opt == "PRESETFILE") // load preset file to replace default { string path = val; @@ -439,6 +490,111 @@ string parseOption(string optline, string base_path, bool ini_file) return "Invalid option: "+optline; } + string loadIni(string path) + { + Debug.WriteLine("loadIni(\""+path+"\")"); + string ini_base = Path.GetDirectoryName(path); + option_palette_type = -1; + string ini_err = ""; + try + { + using (TextReader tr = File.OpenText(path)) + { + string l; + while ((l = tr.ReadLine()) != null) + { + l = l.Trim(); // eliminate leading and trailing whitespace + if (l.Length < 1) continue; + if (l.StartsWith("#")) continue; // comment line + string opt_err = parseOption(l,ini_base,true); + if (opt_err.Length > 0 && ini_err.Length > 0) ini_err += "\n"; + ini_err += opt_err; + } + } + } + catch (Exception ex) + { + if (ini_err.Length > 0) ini_err += "\n"; + return ini_err + ex.ToString(); + } + return ini_err; + } + + string saveIni(string path) + { + Debug.WriteLine("saveIni(\""+path+"\")"); + string fullpath = Path.GetFullPath(path); + string fulldir = Path.GetDirectoryName(fullpath); + // note: Path.GetRelativePath unavailable in .NET framework 4 + try + { + using (StreamWriter sw = File.CreateText(path)) + { + sw.WriteLine("# Binxelview options file"); + sw.WriteLine("# " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); + sw.WriteLine(string.Format("saveini={0}", save_ini ? 1 : 0)); + if (preset_dir.Length > 0) + { + string p = Path.GetFullPath(preset_dir); + if (p.StartsWith(fulldir)) p = p.Substring(fulldir.Length+1); + sw.WriteLine("presetdir=" + p); + } + if (palette_mode == PaletteMode.PALETTE_CUSTOM && palette_path.Length > 0) + { + sw.WriteLine(string.Format("paltype={0}",palette_path_type)); + string p = Path.GetFullPath(palette_path); + if (p.StartsWith(fulldir)) p = p.Substring(fulldir.Length+1); + sw.WriteLine("pal={0}", p); + } + else if (palette_mode != PaletteMode.PALETTE_CUSTOM) + { + sw.WriteLine("autopal=" + PaletteModeString[(int)palette_mode]); + } + sw.WriteLine(string.Format("background={0:X6}",background_raw & 0x00FFFFFF)); + sw.WriteLine(string.Format("zoom={0}",zoom)); + sw.WriteLine(string.Format("grid={0}",hidegrid ? 0 : 1)); + sw.WriteLine(string.Format("hexpos={0}",decimal_position ? 0 : 1)); + sw.WriteLine(string.Format("snapscroll={0}",snap_scroll ? 1 : 0)); + sw.WriteLine(string.Format("horizontal={0}",horizontal_layout ? 1 : 0)); + sw.WriteLine(string.Format("twiddle={0}",twiddle)); + sw.WriteLine("# end"); + } + } + catch (Exception ex) + { + return ex.ToString(); + } + return ""; + } + + string saveCurrentIni() + { + if (ini_path.Length > 0) // if a default ini was found, or loaded with -ini, save back to it + { + string ini_err = saveIni(ini_path); + if (ini_err.Length < 1) return ""; + return "Save options error: "+ini_path+"\n"+ini_err; + } + else // try to create a new ini + { + // first try to save in the executable directory + string ini_exe_err = saveIni(ini_exe); + if(ini_exe_err.Length < 1) return ""; + + // if that fails try to save in appdata local + // try to create directory first, ignore errors + try { + Directory.CreateDirectory(Path.GetDirectoryName(ini_loc)); + } catch { } + string ini_loc_err = saveIni(ini_loc); + if(ini_loc_err.Length < 1) return ""; + + return "Save options attempt 1: "+ini_exe+"\n"+ini_exe_err+ + "\n\n"+ + "Save options attempt 2: "+ini_loc+"\n"+ini_loc_err; + } + } + // // Pixel building // @@ -1010,7 +1166,7 @@ bool openFile(string path) } catch (Exception ex) { - MessageBox.Show("Unable to open file:\n" + path + "\n\n" + ex.ToString(), "Binxelview"); + MessageBox.Show("Unable to open file:\n" + path + "\n\n" + ex.ToString(), APPNAME); return false; } data = read_data; @@ -1023,27 +1179,38 @@ bool openFile(string path) scrollRange(); data_path = path; data_file = Path.GetFileName(path); - this.Text = "Binxelview (" + data_file + ")"; + this.Text = APPNAME + " (" + data_file + ")"; redrawPixels(); return true; } - void reloadPresets() + bool reloadPresets() // returns true if default found { - // remove everything but Reload and separator - while (presetToolStripMenuItem.DropDownItems.Count > 2) + // remove everything but Reload, Set Directory and separator + while (presetToolStripMenuItem.DropDownItems.Count > 3) { - presetToolStripMenuItem.DropDownItems.RemoveAt(2); + presetToolStripMenuItem.DropDownItems.RemoveAt(3); } - presets = new List(); - DirectoryInfo d_cwd = new DirectoryInfo("."); - DirectoryInfo d_app = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory); - FileInfo[] files_cwd = d_cwd.GetFiles("*.bxp"); - FileInfo[] files_app = d_app.GetFiles("*.bxp"); - FileInfo[] files = new FileInfo[files_cwd.Length+files_app.Length]; - Array.Copy(files_cwd,0,files,0,files_cwd.Length); - Array.Copy(files_app,0,files,files_cwd.Length,files_app.Length); + + // gather files + FileInfo[] files; + if (preset_dir.Length < 1) // default looks in current directory, then executable directory + { + FileInfo[] files_cwd = dir_cwd.GetFiles("*.bxp"); + FileInfo[] files_app = dir_exe.GetFiles("*.bxp"); + files = new FileInfo[files_cwd.Length+files_app.Length]; + Array.Copy(files_cwd,0,files,0,files_cwd.Length); + Array.Copy(files_app,0,files,files_cwd.Length,files_app.Length); + } + else // preset_dir + { + DirectoryInfo d_preset = new DirectoryInfo(preset_dir); + files = d_preset.GetFiles("*.bxp"); + } + + // load files + bool default_found = false; foreach (FileInfo file in files) { Preset p = new Preset(); @@ -1064,6 +1231,7 @@ void reloadPresets() presets.Add(p); if (p.name.ToLower()=="default") { + default_found = true; default_preset = p.copy(); scrollRange(); } @@ -1081,6 +1249,8 @@ void reloadPresets() item.Click += presetMenu_Select; presetToolStripMenuItem.DropDownItems.Add(item); } + + return default_found; } void initCustomPalette() @@ -1100,6 +1270,7 @@ bool loadPalette(string path, int filetype) // 1: image (BMP, GIF, PNG, TIF) // 2: VGA RGB18 stored in the low 6 bits of each byte. // 3: Microsoft RIFF PAL + if (filetype < 0 || filetype >=4) filetype = 0; if (filetype == 1) // image { @@ -1131,6 +1302,7 @@ bool loadPalette(string path, int filetype) img.Dispose(); palette_path = Path.GetFullPath(path); + palette_path_type = filetype; return true; } @@ -1232,6 +1404,7 @@ bool loadPalette(string path, int filetype) } palette_path = Path.GetFullPath(path); + palette_path_type = filetype; return true; } @@ -1427,6 +1600,7 @@ void redrawOptions() // make sure the UI state matches current options twiddleZOptionsMenuItem.Checked = twiddle == 1; twiddleNOptionsMenuItem.Checked = twiddle == 2; bgBox.BackColor = background; + saveOnExitOptionsMenuItem.Checked = save_ini; } // @@ -1483,7 +1657,7 @@ private void saveAllVisibleFileMenuItem_Click(object sender, EventArgs e) } catch (Exception ex) { - MessageBox.Show("Unable to save image:\n" + d.FileName + "\n\n" + ex.ToString(), "Binxelview"); + MessageBox.Show("Unable to save image:\n" + d.FileName + "\n\n" + ex.ToString(), APPNAME); } redrawPixels(); @@ -1500,11 +1674,31 @@ private void exitFileMenuItem_Click(object sender, EventArgs e) { this.Close(); } - private void reloadPresetsToolStripMenuItem_Click(object sender, EventArgs e) + private void reloadPresetMenuItem_Click(object sender, EventArgs e) { reloadPresets(); } + private void setDirectoryPresetMenuItem_Click(object sender, EventArgs e) + { + // Note: Semantically this should be FolderBrowserDialog but practically it's UI is unusuable. + // Instead, using OpenFileDialog as a kludgy workaround. + // You can't "open" a folder, but choosing a file inside a folder works at least. + // Typing any non-empty filename also works. + // More intuitive variations of this approach appear to require large library depenencies. + OpenFileDialog d = new OpenFileDialog(); + d.Title = "Select Preset Folder"; + d.ValidateNames = false; // avoid validation that will reject folders + d.CheckFileExists = false; // folder is not a file + d.CheckPathExists = true; // folder is a valid path + d.FileName = " current folder "; // pre-filling this acts like a selected file in the current folder if not changed by the user + if (d.ShowDialog() == DialogResult.OK) + { + preset_dir = Path.GetDirectoryName(d.FileName); + reloadPresets(); + } + } + void presetMenu_Select(object sender, EventArgs e) // clicking on a generated preset menu item { ToolStripMenuItem item = (ToolStripMenuItem)sender; @@ -1531,32 +1725,32 @@ private void hexadecimalPositionOptionsMenuItem_Click(object sender, EventArgs e updatePos(); } - private void snapScrollToNextStrideOptionsMenuItem_Click(object sender, EventArgs e) + private void verticalLayoutOptionsMenuItem_Click(object sender, EventArgs e) { - snap_scroll = !snap_scroll; + horizontal_layout = false; redrawOptions(); - scrollRange(); + redrawPixels(); } - private void gridOptionsMenuItem_Click(object sender, EventArgs e) + private void horizontalLayoutOptionsMenuItem_Click(object sender, EventArgs e) { - hidegrid = !hidegrid; + horizontal_layout = true; redrawOptions(); redrawPixels(); } - private void verticalLayoutOptionsMenuItem_Click(object sender, EventArgs e) + private void gridOptionsMenuItem_Click(object sender, EventArgs e) { - horizontal_layout = false; + hidegrid = !hidegrid; redrawOptions(); redrawPixels(); } - private void horizontalLayoutOptionsMenuItem_Click(object sender, EventArgs e) + private void snapScrollToNextStrideOptionsMenuItem_Click(object sender, EventArgs e) { - horizontal_layout = true; + snap_scroll = !snap_scroll; redrawOptions(); - redrawPixels(); + scrollRange(); } private void twiddleZOptionsMenuItem_Click(object sender, EventArgs e) @@ -1575,6 +1769,76 @@ private void twiddleNOptionsMenuItem_Click(object sender, EventArgs e) redrawPixels(); } + private void loadOptionsMenuItem_Click(object sender, EventArgs e) + { + OpenFileDialog d = new OpenFileDialog(); + d.Title = "Load Options"; + d.DefaultExt = "ini"; + d.Filter = "Binxelview Options (*.ini)|*.ini|All files (*.*)|*.*"; + if (d.ShowDialog() == DialogResult.OK) + { + disable_pixel_redraw = true; + string ini_err = loadIni(d.FileName); + if (ini_err.Length > 0) + { + MessageBox.Show("Options load errors:\n" + d.FileName + "\n\n" + ini_err, APPNAME); + } + reloadPresets(); + autoPalette(); + scrollRange(); + redrawOptions(); + redrawPreset(); + redrawPalette(); + disable_pixel_redraw = false; + redrawPixels(); + } + } + + private void saveOptionsMenuItem_Click(object sender, EventArgs e) + { + SaveFileDialog d = new SaveFileDialog(); + d.Title = "Save Options"; + d.DefaultExt = "ini"; + d.Filter = "Binxelview Options (*.ini)|*.ini|All files (*.*)|*.*"; + if (d.ShowDialog() == DialogResult.OK) + { + string ini_err = saveIni(d.FileName); + if (ini_err.Length > 0) + { + MessageBox.Show("Unable to save options:\n" + d.FileName + "\n\n" + ini_err, APPNAME); + } + } + } + + private void saveCurrentOptionsMenuItem_Click(object sender, EventArgs e) + { + string ini_err = saveCurrentIni(); + if (ini_err.Length > 0) + { + MessageBox.Show("Unable to save current options:\n\n" + ini_err, APPNAME); + } + } + + private void saveOnExitOptionsMenuItem_Click(object sender, EventArgs e) + { + save_ini = !save_ini; + redrawOptions(); + } + + private void defaultOptionsMenuItem_Click(object sender, EventArgs e) + { + disable_pixel_redraw = true; + defaultOption(); // set default options + reloadPresets(); + autoPalette(); + scrollRange(); + redrawOptions(); + redrawPreset(); + redrawPalette(); + disable_pixel_redraw = false; + redrawPixels(); + } + private void aboutHelpMenuItem_Click(object sender, EventArgs e) { string about = @@ -1582,7 +1846,7 @@ private void aboutHelpMenuItem_Click(object sender, EventArgs e) System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString() + "\n" + "\n" + "https://github.com/bbbradsmith/binxelview"; - MessageBox.Show(about, "Binxelview"); + MessageBox.Show(about, APPNAME); } // @@ -1825,7 +2089,7 @@ private void buttonLoadPreset_Click(object sender, EventArgs e) } else { - MessageBox.Show("Unable to load preset:\n" + d.FileName + "\n\n" + Preset.last_error, "Binxelview"); + MessageBox.Show("Unable to load preset:\n" + d.FileName + "\n\n" + Preset.last_error, APPNAME); } } } @@ -1840,7 +2104,7 @@ private void buttonSavePreset_Click(object sender, EventArgs e) { if (!preset.saveFile(d.FileName)) { - MessageBox.Show("Unable to save preset:\n" + d.FileName + "\n\n" + Preset.last_error, "Binxelview"); + MessageBox.Show("Unable to save preset:\n" + d.FileName + "\n\n" + Preset.last_error, APPNAME); } else { @@ -2012,7 +2276,7 @@ private void buttonLoadPal_Click(object sender, EventArgs e) } else { - MessageBox.Show("Unable to load palette:\n" + d.FileName + "\n\n" + palette_error, "Binxelview"); + MessageBox.Show("Unable to load palette:\n" + d.FileName + "\n\n" + palette_error, APPNAME); } } } @@ -2029,7 +2293,7 @@ private void buttonSavePal_Click(object sender, EventArgs e) { if (!savePalette(d.FileName)) { - MessageBox.Show("Unable to save palette:\n" + d.FileName + "\n\n" + palette_error, "Binxelview"); + MessageBox.Show("Unable to save palette:\n" + d.FileName + "\n\n" + palette_error, APPNAME); } } } @@ -2160,7 +2424,7 @@ private void saveImageContextItem_Click(object sender, EventArgs e) if (selected_tile < 0) // shouldn't happen, but giving an error just in case { - MessageBox.Show("No image selected?", "Binxelview"); + MessageBox.Show("No image selected?", APPNAME); return; } @@ -2195,7 +2459,7 @@ private void saveImageContextItem_Click(object sender, EventArgs e) } catch (Exception ex) { - MessageBox.Show("Unable to save image:\n" + d.FileName + "\n\n" + ex.ToString(), "Binxelview"); + MessageBox.Show("Unable to save image:\n" + d.FileName + "\n\n" + ex.ToString(), APPNAME); } redrawPixels(); @@ -2285,6 +2549,11 @@ public BinxelviewForm() private void BinxelviewForm_Load(object sender, EventArgs e) { + // current directory, executable directory, appdata + dir_cwd = new DirectoryInfo("."); + dir_exe = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory); + dir_loc = new DirectoryInfo(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),APPDATA_FOLDER)); + // suppress unnecessary redraws during setup disable_pixel_redraw = true; @@ -2299,22 +2568,30 @@ private void BinxelviewForm_Load(object sender, EventArgs e) defaultOption(); // setup presets + preset.empty(); default_preset.empty(); - reloadPresets(); // loads "Default" preset if it exists - preset = default_preset.copy(); + if (reloadPresets()) preset = default_preset.copy(); // load default palette if it exists - DirectoryInfo dir_cwd = new DirectoryInfo("."); - DirectoryInfo dir_app = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory); string pal_cwd = Path.Combine(dir_cwd.ToString(),"default.pal"); - string pal_app = Path.Combine(dir_app.ToString(),"default.pal"); + string pal_exe = Path.Combine(dir_exe.ToString(),"default.pal"); bool success = true; if (File.Exists(pal_cwd)) { success = loadPalette(pal_cwd,0); } - else if (File.Exists(pal_app)) { success = loadPalette(pal_app,0); } - if (!success) MessageBox.Show("Error opening default palette:\n" + palette_error,"Binxelview"); + else if (File.Exists(pal_exe)) { success = loadPalette(pal_exe,0); } + if (!success) MessageBox.Show("Error opening default palette:\n" + palette_error, APPNAME); + palette_path = ""; // default palette doesn't count as a saved option // parse INI file - // TODO find INI, 3 locations + ini_path = ""; + string ini_cwd = Path.Combine(dir_cwd.ToString(),"binxelview.ini"); + ini_exe = Path.Combine(dir_cwd.ToString(),"binxelview.ini"); + ini_loc = Path.Combine(dir_cwd.ToString(),"binxelview.ini"); + if (File.Exists(ini_cwd)) { ini_path = ini_cwd; } + else if (File.Exists(ini_exe)) { ini_path = ini_exe; } + else if (File.Exists(ini_loc)) { ini_path = ini_loc; } + string ini_err = ""; + if (ini_path.Length > 0) ini_err = loadIni(ini_path); + if (ini_err.Length > 0) MessageBox.Show("Error opening options file: " + ini_path + "\n" + ini_err, APPNAME); // parse the command line options string[] args = Environment.GetCommandLineArgs(); @@ -2336,7 +2613,7 @@ private void BinxelviewForm_Load(object sender, EventArgs e) arg_err += opt_err; } } - if (arg_err.Length > 0) MessageBox.Show("Command line error:\n" + arg_err,"Binxelview"); + if (arg_err.Length > 0) MessageBox.Show("Command line error:\n" + arg_err, APPNAME); autoPalette(); scrollRange(); @@ -2346,5 +2623,13 @@ private void BinxelviewForm_Load(object sender, EventArgs e) disable_pixel_redraw = false; // finally allow redraw of pixels redrawPixels(); } + + private void BinxelviewForm_FormClosed(object sender, FormClosedEventArgs e) + { + if (save_ini) + { + saveCurrentIni(); // no errors reported + } + } } } diff --git a/readme.txt b/readme.txt index 428667a..a88b48f 100644 --- a/readme.txt +++ b/readme.txt @@ -164,8 +164,8 @@ Morton (Z/N) ordering, commonly seen in square textures "twiddled" or "swizzled" GPU cache coherence. -Command Line Options --------------------- +Command Line Options and INI Configuration Files +------------------------------------------------ To open a file, just add that file's path to the command line. @@ -173,6 +173,19 @@ Options can be set with a command line argument beginning with '-', followed by the option name, an '=' separator, and the value for the option. If a space is required in the value, you should enclose the entire argument in quotes. Paths can be either absolute or relative to the current working directory. +For an INI file, paths can be either absolute or relative to the INI file's directory. + + "-ini=mysettings.ini" + Loads an applies settings from another INI file besides the default. + This option can only be used from the command line, and not within an INI file. + If the save on exit option is enabled (see -saveini below), + it will save to this file used with -ini, rather than the default location. + (This only applies to the command line. INI files loaded from the Options menu do not + become the save on exit file.) + + -saveini=1 + If this option is enabled (1) current settings will be saved back to the loaded INI when you quit. + If this option is disabled (0) your current settings will not be remembered when you quit. "-presetfile=C:\mypreset.bxp" Replaces the default preset with one from a file. @@ -180,6 +193,11 @@ Paths can be either absolute or relative to the current working directory. "-preset=Atari ST 4BPP" Chooses a named preset from your preset library instead of the default. + "-presetdir=C:\mypresets" + Discards the preset library and reloads a new library from this directory only. + If a default preset exists in this directory, it will be selected. + If also using -preset or -presetfile, these options should come after -presetdir. + "-pal=my palettes\red.pal" Loads a palette file. If -paltype is not used (see below) it will detect the type by the file extension. If the extension is .BMP .GIF .PNG or .TIF it will load it as an palette from an image. @@ -220,6 +238,37 @@ Paths can be either absolute or relative to the current working directory. Set the twiddle option (0 off, 1 twiddle Z, 2 twiddle N). +INI files provide the same options as the command line, with minor differences: + - One option may be used per line, there is no leading - for an option like the command line. + - Blank lines, or lines starting with # will be ignored. + - Paths can be either absolute or relative to the INI file's directory. + - The 'ini' option cannot be used to load an INI file from within an INI file. + +The default INI file location will be the first found in one of 3 locations, checked in order: + - The current working directory. + - The executable directory. + - The appdata local folder. + +When save on exit is applied, options will be saved back to the first INI file that was found, +or the INI selected from the command line -ini option. If no INI was found, 2 locations will be +tried in order. If a write to the executable directory fails, appdata will be attempted instead. + - The executable directory. + - The appdata local folder. (%LOCALAPPDATA%\binxelview) + + +Because save on exit is an option saved to the INI file, if you want to create a "read only" +INI that gives you an unchanging default setup, turn off save on exit, then "Save Current Options" +from the menu. This will save the current options now, since disabling that option will prevent +them from being saved when you exit the program. + + +You can create "workspaces" for Binxelview by putting a binxelview.ini, presets, etc. into +a folder, then opening Binxelview with that folder as the current working directory. + +Alternatively, you might use the -ini command line option to create a shortcut that applies +a specific INI setup. + + Changes ------- @@ -249,6 +298,7 @@ Changes - Fix image loaded as palette not releasing file handle. - Remember last used file type filter from the load palette dialog. - Microsoft RIFF palette support. +- Option persistence, INI file save and load. 1.5.0.0 (2020-07-31) - Twiddle option for inspecting textures stored with morton ordering of pixels.