From f4ad787721c5d72c22f488ea604adaeae9c0272c Mon Sep 17 00:00:00 2001 From: Sour Date: Sun, 10 Mar 2019 23:45:56 -0400 Subject: [PATCH] UI: Added support for zip/7z files --- InteropDLL/EmuApiWrapper.cpp | 2 +- UI/EmuRunner.cs | 4 + UI/Forms/frmSelectRom.cs | 137 +++++++++++++++++++++ UI/Forms/frmSelectRom.designer.cs | 196 ++++++++++++++++++++++++++++++ UI/Forms/frmSelectRom.resx | 126 +++++++++++++++++++ UI/Interop/EmuApi.cs | 2 + UI/UI.csproj | 10 ++ UI/Utilities/ArchiveHelper.cs | 92 ++++++++++++++ Utilities/VirtualFile.cpp | 2 +- 9 files changed, 569 insertions(+), 2 deletions(-) create mode 100644 UI/Forms/frmSelectRom.cs create mode 100644 UI/Forms/frmSelectRom.designer.cs create mode 100644 UI/Forms/frmSelectRom.resx create mode 100644 UI/Utilities/ArchiveHelper.cs diff --git a/InteropDLL/EmuApiWrapper.cpp b/InteropDLL/EmuApiWrapper.cpp index e27d122..176ec13 100644 --- a/InteropDLL/EmuApiWrapper.cpp +++ b/InteropDLL/EmuApiWrapper.cpp @@ -95,7 +95,7 @@ extern "C" { std::ostringstream out; shared_ptr reader = ArchiveReader::GetReader(filename); if(reader) { - for(string romName : reader->GetFileList({ ".sfc" })) { + for(string romName : reader->GetFileList(VirtualFile::RomExtensions)) { out << romName << "[!|!]"; } } diff --git a/UI/EmuRunner.cs b/UI/EmuRunner.cs index 76d9d94..77136e3 100644 --- a/UI/EmuRunner.cs +++ b/UI/EmuRunner.cs @@ -15,6 +15,10 @@ namespace Mesen.GUI public static void LoadRom(ResourcePath romPath, ResourcePath? patchPath = null) { + if(!frmSelectRom.SelectRom(ref romPath)) { + return; + } + EmuApi.LoadRom(romPath, patchPath); ConfigManager.Config.RecentFiles.AddRecentFile(romPath, patchPath); diff --git a/UI/Forms/frmSelectRom.cs b/UI/Forms/frmSelectRom.cs new file mode 100644 index 0000000..d70dcb4 --- /dev/null +++ b/UI/Forms/frmSelectRom.cs @@ -0,0 +1,137 @@ +using Mesen.GUI.Utilities; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Forms +{ + public partial class frmSelectRom : BaseForm + { + private List _romFiles; + private int SelectedIndex { get; set; } + private string _previousSearch = ""; + + private frmSelectRom(List romFiles) + { + InitializeComponent(); + + _romFiles = romFiles; + lblRomCount.Text = ResourceHelper.GetMessage("RomsFound", _romFiles.Count.ToString()); + + lstRoms.Sorted = true; + this.DialogResult = DialogResult.Cancel; + + UpdateList(); + } + + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) + { + if(txtSearch.Focused) { + if(keyData == Keys.Down || keyData == Keys.PageDown || keyData == Keys.Up || keyData == Keys.PageUp) { + lstRoms.Focus(); + if(lstRoms.Items.Count > 0) { + lstRoms.SelectedIndex = 0; + } + return true; + } + } else if(lstRoms.Focused && lstRoms.SelectedIndex <= 0) { + if(keyData == Keys.Up || keyData == Keys.PageUp) { + txtSearch.Focus(); + txtSearch.SelectAll(); + return true; + } + } + + if(keyData == Keys.Enter && lstRoms.Focused && lstRoms.SelectedItems.Count == 1) { + this.DialogResult = DialogResult.OK; + this.Close(); + return true; + } + + return base.ProcessCmdKey(ref msg, keyData); + } + + private void UpdateList() + { + lstRoms.Items.Clear(); + if(string.IsNullOrWhiteSpace(_previousSearch)) { + lstRoms.Items.AddRange(_romFiles.ToArray()); + } else { + List romsToAdd = new List(); + foreach(ArchiveRomEntry rom in _romFiles) { + if(rom.Filename.IndexOf(_previousSearch, StringComparison.InvariantCultureIgnoreCase) >= 0) { + romsToAdd.Add(rom); + } + } + lstRoms.Items.AddRange(romsToAdd.ToArray()); + } + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + + txtSearch.Focus(); + } + + public static bool SelectRom(ref ResourcePath resource) + { + List archiveRomList = ArchiveHelper.GetArchiveRomList(resource.Path); + + if(archiveRomList.Select(entry => entry.Filename).Contains(resource.InnerFile)) { + return true; + } + + if(archiveRomList.Count > 1) { + using(frmSelectRom frm = new frmSelectRom(archiveRomList)) { + if(frm.ShowDialog(null, Application.OpenForms[0]) == DialogResult.OK) { + ArchiveRomEntry entry = frm.lstRoms.SelectedItem as ArchiveRomEntry; + resource.InnerFile = entry.Filename; + if(!entry.IsUtf8) { + resource.InnerFileIndex = archiveRomList.IndexOf(entry) + 1; + } + } else { + return false; + } + } + } else if(archiveRomList.Count == 1) { + resource.InnerFile = archiveRomList[0].Filename; + if(!archiveRomList[0].IsUtf8) { + resource.InnerFileIndex = 1; + } + } else { + resource.InnerFile = ""; + } + + return true; + } + + private void lstRoms_SelectedIndexChanged(object sender, EventArgs e) + { + btnOK.Enabled = lstRoms.SelectedItems.Count > 0; + } + + private void tmrSearch_Tick(object sender, EventArgs e) + { + if(txtSearch.Text.Trim() != _previousSearch) { + _previousSearch = txtSearch.Text.Trim(); + UpdateList(); + } + } + + private void lstRoms_DoubleClick(object sender, EventArgs e) + { + if(lstRoms.SelectedItems.Count == 1) { + this.DialogResult = DialogResult.OK; + this.Close(); + } + } + } +} diff --git a/UI/Forms/frmSelectRom.designer.cs b/UI/Forms/frmSelectRom.designer.cs new file mode 100644 index 0000000..923457b --- /dev/null +++ b/UI/Forms/frmSelectRom.designer.cs @@ -0,0 +1,196 @@ +namespace Mesen.GUI.Forms +{ + partial class frmSelectRom + { + /// + /// 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() + { + this.components = new System.ComponentModel.Container(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.lstRoms = new System.Windows.Forms.ListBox(); + this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.lblRomCount = new System.Windows.Forms.Label(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.txtSearch = new System.Windows.Forms.TextBox(); + this.lblSearch = new System.Windows.Forms.Label(); + this.tmrSearch = new System.Windows.Forms.Timer(this.components); + this.tableLayoutPanel1.SuspendLayout(); + this.flowLayoutPanel1.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 2; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Controls.Add(this.lstRoms, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.flowLayoutPanel1, 1, 2); + this.tableLayoutPanel1.Controls.Add(this.lblRomCount, 0, 2); + this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel2, 0, 0); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 3; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(463, 242); + this.tableLayoutPanel1.TabIndex = 0; + // + // lstRoms + // + this.tableLayoutPanel1.SetColumnSpan(this.lstRoms, 2); + this.lstRoms.Dock = System.Windows.Forms.DockStyle.Fill; + this.lstRoms.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lstRoms.FormattingEnabled = true; + this.lstRoms.ItemHeight = 16; + this.lstRoms.Location = new System.Drawing.Point(3, 28); + this.lstRoms.Name = "lstRoms"; + this.lstRoms.Size = new System.Drawing.Size(457, 182); + this.lstRoms.TabIndex = 1; + this.lstRoms.SelectedIndexChanged += new System.EventHandler(this.lstRoms_SelectedIndexChanged); + this.lstRoms.DoubleClick += new System.EventHandler(this.lstRoms_DoubleClick); + // + // flowLayoutPanel1 + // + this.flowLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.flowLayoutPanel1.AutoSize = true; + this.flowLayoutPanel1.Controls.Add(this.btnOK); + this.flowLayoutPanel1.Controls.Add(this.btnCancel); + this.flowLayoutPanel1.Location = new System.Drawing.Point(301, 213); + this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); + this.flowLayoutPanel1.Name = "flowLayoutPanel1"; + this.flowLayoutPanel1.Size = new System.Drawing.Size(162, 29); + this.flowLayoutPanel1.TabIndex = 2; + // + // btnOK + // + this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btnOK.Enabled = false; + this.btnOK.Location = new System.Drawing.Point(3, 3); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(75, 23); + this.btnOK.TabIndex = 0; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(84, 3); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(75, 23); + this.btnCancel.TabIndex = 1; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // lblRomCount + // + this.lblRomCount.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblRomCount.AutoSize = true; + this.lblRomCount.Location = new System.Drawing.Point(3, 221); + this.lblRomCount.Name = "lblRomCount"; + this.lblRomCount.Size = new System.Drawing.Size(83, 13); + this.lblRomCount.TabIndex = 2; + this.lblRomCount.Text = "### roms found"; + // + // tableLayoutPanel2 + // + this.tableLayoutPanel2.ColumnCount = 2; + this.tableLayoutPanel1.SetColumnSpan(this.tableLayoutPanel2, 2); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.Controls.Add(this.txtSearch, 1, 0); + this.tableLayoutPanel2.Controls.Add(this.lblSearch, 0, 0); + this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel2.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel2.Margin = new System.Windows.Forms.Padding(0); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + this.tableLayoutPanel2.RowCount = 1; + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.Size = new System.Drawing.Size(463, 25); + this.tableLayoutPanel2.TabIndex = 5; + // + // txtSearch + // + this.txtSearch.Dock = System.Windows.Forms.DockStyle.Fill; + this.txtSearch.Location = new System.Drawing.Point(53, 3); + this.txtSearch.Name = "txtSearch"; + this.txtSearch.Size = new System.Drawing.Size(407, 20); + this.txtSearch.TabIndex = 1; + // + // lblSearch + // + this.lblSearch.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblSearch.AutoSize = true; + this.lblSearch.Location = new System.Drawing.Point(3, 6); + this.lblSearch.Name = "lblSearch"; + this.lblSearch.Size = new System.Drawing.Size(44, 13); + this.lblSearch.TabIndex = 3; + this.lblSearch.Text = "Search:"; + // + // tmrSearch + // + this.tmrSearch.Enabled = true; + this.tmrSearch.Tick += new System.EventHandler(this.tmrSearch_Tick); + // + // frmSelectRom + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(463, 242); + this.Controls.Add(this.tableLayoutPanel1); + this.Name = "frmSelectRom"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Select Rom..."; + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.flowLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel2.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.ListBox lstRoms; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; + private System.Windows.Forms.Button btnOK; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Label lblRomCount; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.TextBox txtSearch; + private System.Windows.Forms.Label lblSearch; + private System.Windows.Forms.Timer tmrSearch; + } +} \ No newline at end of file diff --git a/UI/Forms/frmSelectRom.resx b/UI/Forms/frmSelectRom.resx new file mode 100644 index 0000000..1dde2e8 --- /dev/null +++ b/UI/Forms/frmSelectRom.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + 107, 17 + + \ No newline at end of file diff --git a/UI/Interop/EmuApi.cs b/UI/Interop/EmuApi.cs index cc1d815..2f9b928 100644 --- a/UI/Interop/EmuApi.cs +++ b/UI/Interop/EmuApi.cs @@ -44,6 +44,8 @@ namespace Mesen.GUI [DllImport(DllPath, EntryPoint = "GetLog")] private static extern IntPtr GetLogWrapper(); public static string GetLog() { return Utf8Marshaler.PtrToStringUtf8(EmuApi.GetLogWrapper()).Replace("\n", Environment.NewLine); } + + [DllImport(DllPath)] public static extern IntPtr GetArchiveRomList([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string filename); } public struct ScreenSize diff --git a/UI/UI.csproj b/UI/UI.csproj index aa7c255..d07c5a9 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -491,6 +491,12 @@ frmMain.cs + + Form + + + frmSelectRom.cs + @@ -506,6 +512,7 @@ + @@ -615,6 +622,9 @@ frmMain.cs + + frmSelectRom.cs + ResXFileCodeGenerator Resources.Designer.cs diff --git a/UI/Utilities/ArchiveHelper.cs b/UI/Utilities/ArchiveHelper.cs new file mode 100644 index 0000000..1d840d2 --- /dev/null +++ b/UI/Utilities/ArchiveHelper.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Mesen.GUI.Utilities +{ + class ArchiveHelper + { + public static List GetArchiveRomList(string archivePath) + { + //Split the array on the [!|!] delimiter + byte[] buffer = PtrToByteArray(EmuApi.GetArchiveRomList(archivePath)); + List> filenames = new List>(); + List filenameBytes = new List(); + for(int i = 0; i < buffer.Length - 5; i++) { + if(buffer[i] == '[' && buffer[i + 1] == '!' && buffer[i + 2] == '|' && buffer[i + 3] == '!' && buffer[i + 4] == ']') { + if(filenameBytes.Count > 0) { + filenames.Add(filenameBytes); + } + filenameBytes = new List(); + i += 4; + } else { + filenameBytes.Add(buffer[i]); + } + } + if(filenameBytes.Count > 0) { + filenames.Add(filenameBytes); + } + + List entries = new List(); + + //Check whether or not each string is a valid utf8 filename, if not decode it using the system's default encoding. + //This is necessary because zip files do not have any rules when it comes to encoding filenames + for(int i = 0; i < filenames.Count; i++) { + byte[] originalBytes = filenames[i].ToArray(); + string utf8Filename = Encoding.UTF8.GetString(originalBytes); + byte[] convertedBytes = Encoding.UTF8.GetBytes(utf8Filename); + bool equal = true; + if(originalBytes.Length == convertedBytes.Length) { + for(int j = 0; j < convertedBytes.Length; j++) { + if(convertedBytes[j] != originalBytes[j]) { + equal = false; + break; + } + } + } else { + equal = false; + } + + if(!equal) { + //String doesn't appear to be an utf8 string, use the system's default encoding + entries.Add(new ArchiveRomEntry() { Filename = Encoding.Default.GetString(originalBytes), IsUtf8 = false }); + } else { + entries.Add(new ArchiveRomEntry() { Filename = utf8Filename, IsUtf8 = true }); + } + } + + return entries; + } + + private static byte[] PtrToByteArray(IntPtr ptr) + { + if(ptr == IntPtr.Zero) { + return new byte[0]; + } + + int len = 0; + while(Marshal.ReadByte(ptr, len) != 0) { + len++; + } + + byte[] array = new byte[len]; + Marshal.Copy(ptr, array, 0, len); + + return array; + } + } + + public class ArchiveRomEntry + { + public string Filename; + public bool IsUtf8; + + public override string ToString() + { + return Filename; + } + } +} diff --git a/Utilities/VirtualFile.cpp b/Utilities/VirtualFile.cpp index 59b7676..0ac1509 100644 --- a/Utilities/VirtualFile.cpp +++ b/Utilities/VirtualFile.cpp @@ -11,7 +11,7 @@ #include "../Utilities/UpsPatcher.h" //TODO -const std::initializer_list VirtualFile::RomExtensions = { ".nes", ".fds", ".nsf", ".nsfe", "*.unf", "*.unif" }; +const std::initializer_list VirtualFile::RomExtensions = { ".sfc", ".smc", ".swc", ".fig" }; VirtualFile::VirtualFile() {