diff --git a/BizHawk.Emulation/Util.cs b/BizHawk.Emulation/Util.cs
index d5fba37bd3..e7a5da6615 100644
--- a/BizHawk.Emulation/Util.cs
+++ b/BizHawk.Emulation/Util.cs
@@ -478,6 +478,40 @@ namespace BizHawk
return outStream.ToArray();
}
+
+ public static string FormatFileSize(long filesize)
+ {
+ Decimal size = (Decimal)filesize;
+
+ Decimal OneKiloByte = 1024M;
+ Decimal OneMegaByte = OneKiloByte * 1024M;
+ Decimal OneGigaByte = OneMegaByte * 1024M;
+
+ string suffix;
+ if (size > 1024*1024*1024)
+ {
+ size /= 1024*1024*1024;
+ suffix = "GB";
+ }
+ else if (size > 1024*1024)
+ {
+ size /= 1024*1024;
+ suffix = "MB";
+ }
+ else if (size > 1024)
+ {
+ size /= 1024;
+ suffix = "KB";
+ }
+ else
+ {
+ suffix = " B";
+ }
+
+ string precision = "2";
+ return String.Format("{0:N" + precision + "}{1}", size, suffix);
+ }
+
}
diff --git a/BizHawk.MultiClient/ArchiveChooser.Designer.cs b/BizHawk.MultiClient/ArchiveChooser.Designer.cs
new file mode 100644
index 0000000000..4387082b29
--- /dev/null
+++ b/BizHawk.MultiClient/ArchiveChooser.Designer.cs
@@ -0,0 +1,123 @@
+namespace BizHawk.MultiClient
+{
+ partial class ArchiveChooser
+ {
+ ///
+ /// 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.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
+ this.btnCancel = new System.Windows.Forms.Button();
+ this.btnOK = new System.Windows.Forms.Button();
+ this.lvMembers = new System.Windows.Forms.ListView();
+ this.colSize = new System.Windows.Forms.ColumnHeader();
+ this.colName = new System.Windows.Forms.ColumnHeader();
+ this.flowLayoutPanel1.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // flowLayoutPanel1
+ //
+ this.flowLayoutPanel1.AutoSize = true;
+ this.flowLayoutPanel1.Controls.Add(this.btnCancel);
+ this.flowLayoutPanel1.Controls.Add(this.btnOK);
+ this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+ this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft;
+ this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 276);
+ this.flowLayoutPanel1.Name = "flowLayoutPanel1";
+ this.flowLayoutPanel1.Size = new System.Drawing.Size(528, 29);
+ this.flowLayoutPanel1.TabIndex = 0;
+ //
+ // btnCancel
+ //
+ this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.btnCancel.Location = new System.Drawing.Point(450, 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;
+ this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
+ //
+ // btnOK
+ //
+ this.btnOK.Location = new System.Drawing.Point(369, 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;
+ this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
+ //
+ // lvMembers
+ //
+ this.lvMembers.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+ this.colSize,
+ this.colName});
+ this.lvMembers.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.lvMembers.FullRowSelect = true;
+ this.lvMembers.Location = new System.Drawing.Point(0, 0);
+ this.lvMembers.Name = "lvMembers";
+ this.lvMembers.Size = new System.Drawing.Size(528, 276);
+ this.lvMembers.TabIndex = 0;
+ this.lvMembers.UseCompatibleStateImageBehavior = false;
+ this.lvMembers.View = System.Windows.Forms.View.Details;
+ this.lvMembers.ItemActivate += new System.EventHandler(this.lvMembers_ItemActivate);
+ //
+ // colSize
+ //
+ this.colSize.Text = "Size";
+ //
+ // colName
+ //
+ this.colName.Text = "Name";
+ this.colName.Width = 409;
+ //
+ // ArchiveChooser
+ //
+ 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(528, 305);
+ this.Controls.Add(this.lvMembers);
+ this.Controls.Add(this.flowLayoutPanel1);
+ this.Name = "ArchiveChooser";
+ this.Text = "Choose File From Archive";
+ this.flowLayoutPanel1.ResumeLayout(false);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
+ private System.Windows.Forms.ListView lvMembers;
+ private System.Windows.Forms.Button btnCancel;
+ private System.Windows.Forms.Button btnOK;
+ private System.Windows.Forms.ColumnHeader colSize;
+ private System.Windows.Forms.ColumnHeader colName;
+ }
+}
\ No newline at end of file
diff --git a/BizHawk.MultiClient/ArchiveChooser.cs b/BizHawk.MultiClient/ArchiveChooser.cs
new file mode 100644
index 0000000000..64ba9f93ba
--- /dev/null
+++ b/BizHawk.MultiClient/ArchiveChooser.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+
+namespace BizHawk.MultiClient
+{
+ public partial class ArchiveChooser : Form
+ {
+ public ArchiveChooser(HawkFile hawkfile)
+ {
+ InitializeComponent();
+ foreach (var item in hawkfile.ArchiveItems)
+ {
+ var lvi = new ListViewItem();
+ lvi.Tag = item;
+ lvi.SubItems.Add(new ListViewItem.ListViewSubItem());
+ lvi.Text = Util.FormatFileSize(item.size);
+ lvi.SubItems[1].Text = item.name;
+ lvMembers.Items.Add(lvi);
+ }
+ }
+
+ public int SelectedMemberIndex
+ {
+ get
+ {
+ if (lvMembers.SelectedIndices.Count == 0) return -1;
+ var ai = lvMembers.SelectedItems[0].Tag as HawkFile.ArchiveItem;
+ return ai.index;
+ }
+ }
+
+ private void btnOK_Click(object sender, EventArgs e)
+ {
+ DialogResult = DialogResult.OK;
+ Close();
+ }
+
+ private void btnCancel_Click(object sender, EventArgs e)
+ {
+ DialogResult = DialogResult.Cancel;
+ }
+
+ private void lvMembers_ItemActivate(object sender, EventArgs e)
+ {
+ DialogResult = DialogResult.OK;
+ Close();
+ }
+ }
+}
diff --git a/BizHawk.MultiClient/ArchiveChooser.resx b/BizHawk.MultiClient/ArchiveChooser.resx
new file mode 100644
index 0000000000..ff31a6db56
--- /dev/null
+++ b/BizHawk.MultiClient/ArchiveChooser.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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/BizHawk.MultiClient/BizHawk.MultiClient.csproj b/BizHawk.MultiClient/BizHawk.MultiClient.csproj
index 5dae115f79..835f4bfe26 100644
--- a/BizHawk.MultiClient/BizHawk.MultiClient.csproj
+++ b/BizHawk.MultiClient/BizHawk.MultiClient.csproj
@@ -3,7 +3,7 @@
Debug
AnyCPU
- 9.0.21022
+ 9.0.30729
2.0
{DD448B37-BA3F-4544-9754-5406E8094723}
Exe
@@ -73,6 +73,12 @@
AboutBox.cs
+
+ Form
+
+
+ ArchiveChooser.cs
+
@@ -259,6 +265,9 @@
AboutBox.cs
Designer
+
+ ArchiveChooser.cs
+
InputConfig.cs
Designer
diff --git a/BizHawk.MultiClient/HawkFile.cs b/BizHawk.MultiClient/HawkFile.cs
index fa80f19e32..33ea862ba7 100644
--- a/BizHawk.MultiClient/HawkFile.cs
+++ b/BizHawk.MultiClient/HawkFile.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections;
+using System.Collections.Generic;
using System.IO;
namespace BizHawk.MultiClient
@@ -54,19 +56,41 @@ namespace BizHawk.MultiClient
///
public string Extension { get { return Path.GetExtension(Name); } }
- //---
- bool rootExists;
- string rootPath;
- string memberPath;
- Stream rootStream, boundStream;
- SevenZip.SevenZipExtractor extractor;
+ ///
+ /// Indicates whether this file is an archive
+ ///
+ public bool IsArchive { get { return extractor != null; } }
public static bool PathExists(string path)
{
using (var hf = new HawkFile(path))
return hf.Exists;
}
-
+
+ public class ArchiveItem
+ {
+ public string name;
+ public long size;
+ public int index;
+ }
+
+ public IEnumerable ArchiveItems
+ {
+ get
+ {
+ if (!IsArchive) throw new InvalidOperationException("Cant get archive items from non-archive");
+ return archiveItems;
+ }
+ }
+
+ //---
+ bool rootExists;
+ string rootPath;
+ string memberPath;
+ Stream rootStream, boundStream;
+ SevenZip.SevenZipExtractor extractor;
+ List archiveItems;
+
public HawkFile(string path)
{
string autobind = null;
@@ -89,6 +113,8 @@ namespace BizHawk.MultiClient
if (extractor == null)
{
rootStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
+ //we could autobind here, but i dont want to
+ //bind it later with the desired extensions.
}
if (autobind != null)
@@ -96,7 +122,7 @@ namespace BizHawk.MultiClient
autobind = autobind.ToUpperInvariant();
for (int i = 0; i < extractor.ArchiveFileData.Count; i++)
{
- if (extractor.ArchiveFileNames[i].ToUpperInvariant() == autobind)
+ if (FixArchiveFilename(extractor.ArchiveFileNames[i]).ToUpperInvariant() == autobind)
{
BindArchiveMember(i);
return;
@@ -131,13 +157,26 @@ namespace BizHawk.MultiClient
else return string.Format("{0}|{1}", root, member);
}
- void BindArchiveMember(int index)
+ string FixArchiveFilename(string fn)
{
+ return fn.Replace('\\', '/');
+ }
+
+ ///
+ /// binds the selected archive index
+ ///
+ public HawkFile BindArchiveMember(int archiveIndex)
+ {
+ if (!rootExists) return this;
+ if (boundStream != null) throw new InvalidOperationException("stream already bound!");
+
boundStream = new MemoryStream();
- extractor.ExtractFile(index, boundStream);
+ extractor.ExtractFile(archiveIndex, boundStream);
boundStream.Position = 0;
- memberPath = extractor.ArchiveFileNames[index];
+ memberPath = FixArchiveFilename(extractor.ArchiveFileNames[archiveIndex]); //TODO - maybe go through our own list of names? maybe not, its indexes dont match..
Console.WriteLine("bound " + CanonicalName);
+
+ return this;
}
///
@@ -150,6 +189,9 @@ namespace BizHawk.MultiClient
memberPath = null;
}
+ ///
+ /// causes the root to be bound (in the case of non-archive files
+ ///
void BindRoot()
{
boundStream = rootStream;
@@ -166,9 +208,23 @@ namespace BizHawk.MultiClient
}
///
- /// Binds the first item in the archive (or the file itself) if the extension matches one of the supplied templates
+ /// binds one of the supplied extensions if there is only one match in the archive
+ ///
+ public HawkFile BindSoleItemOf(params string[] extensions)
+ {
+ return BindByExtensionCore(false, extensions);
+ }
+
+ ///
+ /// Binds the first item in the archive (or the file itself) if the extension matches one of the supplied templates.
+ /// You probably should not use this. use BindSoleItemOf or the archive chooser instead
///
public HawkFile BindFirstOf(params string[] extensions)
+ {
+ return BindByExtensionCore(true, extensions);
+ }
+
+ HawkFile BindByExtensionCore(bool first, params string[] extensions)
{
if (!rootExists) return this;
if (boundStream != null) throw new InvalidOperationException("stream already bound!");
@@ -177,44 +233,59 @@ namespace BizHawk.MultiClient
{
//open uncompressed file
string extension = Path.GetExtension(rootPath).Substring(1).ToUpperInvariant();
- if (extensions.Length==0 || extension.In(extensions))
+ if (extensions.Length == 0 || extension.In(extensions))
{
BindRoot();
}
return this;
}
- for(int i=0;i();
+ for (int i = 0; i < extractor.ArchiveFileData.Count; i++)
{
var e = extractor.ArchiveFileData[i];
- var extension = Path.GetExtension(e.FileName).Substring(1).ToUpperInvariant();
+ if (e.IsDirectory) continue;
+ var extension = Path.GetExtension(e.FileName).ToUpperInvariant();
+ extension = extension.TrimStart('.');
if (extensions.Length == 0 || extension.In(extensions))
{
- BindArchiveMember(i);
- return this;
+ if (first)
+ {
+ BindArchiveMember(i);
+ return this;
+ }
+ candidates.Add(i);
}
}
-
+ if (candidates.Count == 1)
+ BindArchiveMember(candidates[0]);
return this;
}
- private void AnalyzeArchive(string path)
- {
- try
+ void ScanArchive()
+ {
+ archiveItems = new List();
+ for (int i = 0; i < extractor.ArchiveFileData.Count; i++)
{
- SevenZip.FileChecker.ThrowExceptions = false;
- int offset;
- bool isExecutable;
- if (SevenZip.FileChecker.CheckSignature(path, out offset, out isExecutable) != SevenZip.InArchiveFormat.None)
- {
- extractor = new SevenZip.SevenZipExtractor(path);
- //now would be a good time to scan the archive..
- }
+ var afd = extractor.ArchiveFileData[i];
+ var ai = new ArchiveItem();
+ ai.name = FixArchiveFilename(afd.FileName);
+ ai.size = (long)afd.Size; //ulong. obnoxious.
+ ai.index = i;
+ archiveItems.Add(ai);
}
- catch
+ }
+
+ private void AnalyzeArchive(string path)
+ {
+ SevenZip.FileChecker.ThrowExceptions = false;
+ int offset;
+ bool isExecutable;
+ if (SevenZip.FileChecker.CheckSignature(path, out offset, out isExecutable) != SevenZip.InArchiveFormat.None)
{
- //must not be an archive. is there a better way to determine this? the exceptions are as annoying as hell
+ extractor = new SevenZip.SevenZipExtractor(path);
+ ScanArchive();
}
}
diff --git a/BizHawk.MultiClient/MainForm.cs b/BizHawk.MultiClient/MainForm.cs
index 87b4da9c06..4b254a24a5 100644
--- a/BizHawk.MultiClient/MainForm.cs
+++ b/BizHawk.MultiClient/MainForm.cs
@@ -393,8 +393,24 @@ namespace BizHawk.MultiClient
{
using (var file = new HawkFile(path))
{
+ //if the provided file doesnt even exist, give up!
if (!file.RootExists) return false;
+ //try binding normal rom extensions first
+ if (!file.IsBound)
+ file.BindSoleItemOf("SMS", "PCE", "SGX", "GG", "SG", "BIN", "SMD", "GB", "NES");
+
+ //if we have an archive and need to bind something, then pop the dialog
+ if (file.IsArchive && !file.IsBound)
+ {
+ var ac = new ArchiveChooser(file);
+ if (ac.ShowDialog(this) == DialogResult.OK)
+ {
+ file.BindArchiveMember(ac.SelectedMemberIndex);
+ }
+ else return false;
+ }
+
CloseGame();
var game = new RomGame(file);
diff --git a/BizHawk.MultiClient/RomGame.cs b/BizHawk.MultiClient/RomGame.cs
index 5b6d2326ab..2e698651b5 100644
--- a/BizHawk.MultiClient/RomGame.cs
+++ b/BizHawk.MultiClient/RomGame.cs
@@ -18,8 +18,6 @@ namespace BizHawk.MultiClient
public RomGame(HawkFile file, string patch)
{
- if(!file.IsBound)
- file.BindFirstOf("SMS", "PCE", "SGX", "GG", "SG", "BIN", "SMD", "GB", "NES");
if (!file.Exists)
throw new Exception("The file needs to exist, yo.");