some minor code cleanups in BizHawk.Common

This commit is contained in:
adelikat 2014-02-04 21:15:33 +00:00
parent 8439d13236
commit e71d729626
8 changed files with 392 additions and 322 deletions

View File

@ -52,8 +52,8 @@ namespace BizHawk.Client.Common
var ai = new HawkFileArchiveItem var ai = new HawkFileArchiveItem
{ {
name = HawkFile.Util_FixArchiveFilename(afd.FileName), Name = HawkFile.Util_FixArchiveFilename(afd.FileName),
size = (long)afd.Size, archiveIndex = i, index = ret.Count Size = (long)afd.Size, ArchiveIndex = i, Index = ret.Count
}; };
ret.Add(ai); ret.Add(ai);

View File

@ -993,9 +993,9 @@ namespace BizHawk.Client.Common
string platform = "SNES"; string platform = "SNES";
foreach (var item in hf.ArchiveItems) foreach (var item in hf.ArchiveItems)
{ {
if (item.name == "authors") if (item.Name == "authors")
{ {
hf.BindArchiveMember(item.index); hf.BindArchiveMember(item.Index);
var stream = hf.GetStream(); var stream = hf.GetStream();
string authors = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)); string authors = Encoding.UTF8.GetString(Util.ReadAllBytes(stream));
string author_list = ""; string author_list = "";
@ -1028,25 +1028,25 @@ namespace BizHawk.Client.Common
m.Header[HeaderKeys.AUTHOR] = author_list; m.Header[HeaderKeys.AUTHOR] = author_list;
hf.Unbind(); hf.Unbind();
} }
else if (item.name == "coreversion") else if (item.Name == "coreversion")
{ {
hf.BindArchiveMember(item.index); hf.BindArchiveMember(item.Index);
var stream = hf.GetStream(); var stream = hf.GetStream();
string coreversion = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)).Trim(); string coreversion = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)).Trim();
m.Header.Comments.Add(COREORIGIN + " " + coreversion); m.Header.Comments.Add(COREORIGIN + " " + coreversion);
hf.Unbind(); hf.Unbind();
} }
else if (item.name == "gamename") else if (item.Name == "gamename")
{ {
hf.BindArchiveMember(item.index); hf.BindArchiveMember(item.Index);
var stream = hf.GetStream(); var stream = hf.GetStream();
string gamename = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)).Trim(); string gamename = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)).Trim();
m.Header[HeaderKeys.GAMENAME] = gamename; m.Header[HeaderKeys.GAMENAME] = gamename;
hf.Unbind(); hf.Unbind();
} }
else if (item.name == "gametype") else if (item.Name == "gametype")
{ {
hf.BindArchiveMember(item.index); hf.BindArchiveMember(item.Index);
var stream = hf.GetStream(); var stream = hf.GetStream();
string gametype = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)).Trim(); string gametype = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)).Trim();
// TODO: Handle the other types. // TODO: Handle the other types.
@ -1069,9 +1069,9 @@ namespace BizHawk.Client.Common
m.Header[HeaderKeys.PAL] = pal.ToString(); m.Header[HeaderKeys.PAL] = pal.ToString();
hf.Unbind(); hf.Unbind();
} }
else if (item.name == "input") else if (item.Name == "input")
{ {
hf.BindArchiveMember(item.index); hf.BindArchiveMember(item.Index);
var stream = hf.GetStream(); var stream = hf.GetStream();
string input = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)); string input = Encoding.UTF8.GetString(Util.ReadAllBytes(stream));
int lineNum = 0; int lineNum = 0;
@ -1095,9 +1095,9 @@ namespace BizHawk.Client.Common
} }
hf.Unbind(); hf.Unbind();
} }
else if (item.name.StartsWith("moviesram.")) else if (item.Name.StartsWith("moviesram."))
{ {
hf.BindArchiveMember(item.index); hf.BindArchiveMember(item.Index);
var stream = hf.GetStream(); var stream = hf.GetStream();
byte[] moviesram = Util.ReadAllBytes(stream); byte[] moviesram = Util.ReadAllBytes(stream);
if (moviesram.Length != 0) if (moviesram.Length != 0)
@ -1108,33 +1108,33 @@ namespace BizHawk.Client.Common
} }
hf.Unbind(); hf.Unbind();
} }
else if (item.name == "port1") else if (item.Name == "port1")
{ {
hf.BindArchiveMember(item.index); hf.BindArchiveMember(item.Index);
var stream = hf.GetStream(); var stream = hf.GetStream();
string port1 = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)).Trim(); string port1 = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)).Trim();
m.Header[PORT1] = port1; m.Header[PORT1] = port1;
hf.Unbind(); hf.Unbind();
} }
else if (item.name == "port2") else if (item.Name == "port2")
{ {
hf.BindArchiveMember(item.index); hf.BindArchiveMember(item.Index);
var stream = hf.GetStream(); var stream = hf.GetStream();
string port2 = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)).Trim(); string port2 = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)).Trim();
m.Header[PORT2] = port2; m.Header[PORT2] = port2;
hf.Unbind(); hf.Unbind();
} }
else if (item.name == "projectid") else if (item.Name == "projectid")
{ {
hf.BindArchiveMember(item.index); hf.BindArchiveMember(item.Index);
var stream = hf.GetStream(); var stream = hf.GetStream();
string projectid = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)).Trim(); string projectid = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)).Trim();
m.Header[PROJECTID] = projectid; m.Header[PROJECTID] = projectid;
hf.Unbind(); hf.Unbind();
} }
else if (item.name == "rerecords") else if (item.Name == "rerecords")
{ {
hf.BindArchiveMember(item.index); hf.BindArchiveMember(item.Index);
var stream = hf.GetStream(); var stream = hf.GetStream();
string rerecords = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)); string rerecords = Encoding.UTF8.GetString(Util.ReadAllBytes(stream));
int rerecordCount; int rerecordCount;
@ -1150,24 +1150,24 @@ namespace BizHawk.Client.Common
m.Header.Rerecords = (ulong)rerecordCount; m.Header.Rerecords = (ulong)rerecordCount;
hf.Unbind(); hf.Unbind();
} }
else if (item.name.EndsWith(".sha256")) else if (item.Name.EndsWith(".sha256"))
{ {
hf.BindArchiveMember(item.index); hf.BindArchiveMember(item.Index);
var stream = hf.GetStream(); var stream = hf.GetStream();
string rom = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)).Trim(); string rom = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)).Trim();
int pos = item.name.LastIndexOf(".sha256"); int pos = item.Name.LastIndexOf(".sha256");
string name = item.name.Substring(0, pos); string name = item.Name.Substring(0, pos);
m.Header[SHA256 + "_" + name] = rom; m.Header[SHA256 + "_" + name] = rom;
hf.Unbind(); hf.Unbind();
} }
else if (item.name == "savestate") else if (item.Name == "savestate")
{ {
errorMsg = "Movies that begin with a savestate are not supported."; errorMsg = "Movies that begin with a savestate are not supported.";
return null; return null;
} }
else if (item.name == "subtitles") else if (item.Name == "subtitles")
{ {
hf.BindArchiveMember(item.index); hf.BindArchiveMember(item.Index);
var stream = hf.GetStream(); var stream = hf.GetStream();
string subtitles = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)); string subtitles = Encoding.UTF8.GetString(Util.ReadAllBytes(stream));
using (StringReader reader = new StringReader(subtitles)) using (StringReader reader = new StringReader(subtitles))
@ -1178,25 +1178,25 @@ namespace BizHawk.Client.Common
} }
hf.Unbind(); hf.Unbind();
} }
else if (item.name == "starttime.second") else if (item.Name == "starttime.second")
{ {
hf.BindArchiveMember(item.index); hf.BindArchiveMember(item.Index);
var stream = hf.GetStream(); var stream = hf.GetStream();
string startSecond = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)).Trim(); string startSecond = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)).Trim();
m.Header[STARTSECOND] = startSecond; m.Header[STARTSECOND] = startSecond;
hf.Unbind(); hf.Unbind();
} }
else if (item.name == "starttime.subsecond") else if (item.Name == "starttime.subsecond")
{ {
hf.BindArchiveMember(item.index); hf.BindArchiveMember(item.Index);
var stream = hf.GetStream(); var stream = hf.GetStream();
string startSubSecond = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)).Trim(); string startSubSecond = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)).Trim();
m.Header[STARTSUBSECOND] = startSubSecond; m.Header[STARTSUBSECOND] = startSubSecond;
hf.Unbind(); hf.Unbind();
} }
else if (item.name == "systemid") else if (item.Name == "systemid")
{ {
hf.BindArchiveMember(item.index); hf.BindArchiveMember(item.Index);
var stream = hf.GetStream(); var stream = hf.GetStream();
string systemid = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)).Trim(); string systemid = Encoding.UTF8.GetString(Util.ReadAllBytes(stream)).Trim();
m.Header.Comments.Add(EMULATIONORIGIN + " " + systemid); m.Header.Comments.Add(EMULATIONORIGIN + " " + systemid);

View File

@ -36,9 +36,9 @@ namespace BizHawk.Client.EmuHawk
var item = items[i]; var item = items[i];
var lvi = new ListViewItem { Tag = i }; var lvi = new ListViewItem { Tag = i };
lvi.SubItems.Add(new ListViewItem.ListViewSubItem()); lvi.SubItems.Add(new ListViewItem.ListViewSubItem());
lvi.Text = item.name; lvi.Text = item.Name;
long size = item.size; long size = item.Size;
var extension = Path.GetExtension(item.name); var extension = Path.GetExtension(item.Name);
if (extension != null && (size % 1024 == 16 && extension.ToUpper() == ".NES")) if (extension != null && (size % 1024 == 16 && extension.ToUpper() == ".NES"))
size -= 16; size -= 16;
lvi.SubItems[1].Text = Util.FormatFileSize(size); lvi.SubItems[1].Text = Util.FormatFileSize(size);

View File

@ -3,15 +3,29 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
//the HawkFile class is excessively engineered with the IHawkFileArchiveHandler to decouple the archive handling from the basic file handling. // the HawkFile class is excessively engineered with the IHawkFileArchiveHandler to decouple the archive handling from the basic file handling.
//This is so we could drop in an unamanged dearchiver library optionally later as a performance optimization without ruining the portability of the code. // This is so we could drop in an unamanged dearchiver library optionally later as a performance optimization without ruining the portability of the code.
//Also, we want to be able to use HawkFiles in BizHawk.Common withuot bringing in a large 7-zip dependency // Also, we want to be able to use HawkFiles in BizHawk.Common withuot bringing in a large 7-zip dependency
namespace BizHawk.Common namespace BizHawk.Common
{ {
//todo: // TODO:
//split into "bind" and "open (the bound thing)" // split into "bind" and "open (the bound thing)"
//scan archive to flatten interior directories down to a path (maintain our own archive item list) // scan archive to flatten interior directories down to a path (maintain our own archive item list)
/// <summary>
/// Bridge between HawkFile and the frontend's implementation of archive management
/// </summary>
public interface IHawkFileArchiveHandler : IDisposable
{
// TODO - could this receive a hawkfile itself? possibly handy, in very clever scenarios of mounting fake files
bool CheckSignature(string fileName, out int offset, out bool isExecutable);
List<HawkFileArchiveItem> Scan();
IHawkFileArchiveHandler Construct(string path);
void ExtractFile(int index, Stream stream);
}
/// <summary> /// <summary>
/// HawkFile allows a variety of objects (actual files, archive members) to be treated as normal filesystem objects to be opened, closed, and read. /// HawkFile allows a variety of objects (actual files, archive members) to be treated as normal filesystem objects to be opened, closed, and read.
@ -21,10 +35,92 @@ namespace BizHawk.Common
/// </summary> /// </summary>
public sealed class HawkFile : IDisposable public sealed class HawkFile : IDisposable
{ {
private bool _exists;
private bool _rootExists;
private string _rootPath;
private string _memberPath;
private Stream _rootStream, _boundStream;
private IHawkFileArchiveHandler _extractor;
private List<HawkFileArchiveItem> _archiveItems;
private int? _boundIndex;
public HawkFile() { }
/// <summary> /// <summary>
/// Set this with an instance which can construct archive handlers as necessary for archive handling. /// Set this with an instance which can construct archive handlers as necessary for archive handling.
/// </summary> /// </summary>
public static IHawkFileArchiveHandler ArchiveHandlerFactory; public static IHawkFileArchiveHandler ArchiveHandlerFactory { get; set; }
/// <summary>
/// Gets a value indicating whether a bound file exists. if there is no bound file, it can't exist
/// </summary>
public bool Exists { get { return _exists; } }
/// <summary>
/// Gets the directory containing the root
/// </summary>
public string Directory { get { return Path.GetDirectoryName(_rootPath); } }
/// <summary>
/// Gets a value indicating whether this instance is bound
/// </summary>
public bool IsBound { get { return _boundStream != null; } }
/// <summary>
/// returns the complete canonical full path ("c:\path\to\archive|member") of the bound file
/// </summary>
public string CanonicalFullPath { get { return MakeCanonicalName(_rootPath, _memberPath); } }
/// <summary>
/// returns the complete canonical name ("archive|member") of the bound file
/// </summary>
public string CanonicalName { get { return MakeCanonicalName(Path.GetFileName(_rootPath), _memberPath); } }
/// <summary>
/// returns the virtual name of the bound file (disregarding the archive)
/// </summary>
public string Name { get { return GetBoundNameFromCanonical(MakeCanonicalName(_rootPath, _memberPath)); } }
/// <summary>
/// returns the extension of Name
/// </summary>
public string Extension { get { return Path.GetExtension(Name).ToUpper(); } }
/// <summary>
/// Indicates whether this file is an archive
/// </summary>
public bool IsArchive { get { return _extractor != null; } }
public IList<HawkFileArchiveItem> ArchiveItems
{
get
{
if (!IsArchive)
{
throw new InvalidOperationException("Cant get archive items from non-archive");
}
return _archiveItems;
}
}
/// <summary>
/// returns a stream for the currently bound file
/// </summary>
public Stream GetStream()
{
if (_boundStream == null)
{
throw new InvalidOperationException("HawkFile: Can't call GetStream() before youve successfully bound something!");
}
return _boundStream;
}
public int? GetBoundIndex()
{
return _boundIndex;
}
/// <summary> /// <summary>
/// Utility: Uses full HawkFile processing to determine whether a file exists at the provided path /// Utility: Uses full HawkFile processing to determine whether a file exists at the provided path
@ -44,154 +140,77 @@ namespace BizHawk.Common
{ {
using (var file = new HawkFile(path)) using (var file = new HawkFile(path))
{ {
if (!file.Exists) throw new FileNotFoundException(path); if (!file.Exists)
{
throw new FileNotFoundException(path);
}
using (Stream stream = file.GetStream()) using (Stream stream = file.GetStream())
{ {
MemoryStream ms = new MemoryStream((int)stream.Length); var ms = new MemoryStream((int)stream.Length);
stream.CopyTo(ms); stream.CopyTo(ms);
return ms.GetBuffer(); return ms.GetBuffer();
} }
} }
} }
/// <summary>
/// returns whether a bound file exists. if there is no bound file, it can't exist
/// </summary>
public bool Exists { get { return exists; } }
/// <summary>
/// gets the directory containing the root
/// </summary>
public string Directory { get { return Path.GetDirectoryName(rootPath); } }
/// <summary>
/// returns a stream for the currently bound file
/// </summary>
public Stream GetStream()
{
if (boundStream == null)
throw new InvalidOperationException("HawkFile: Can't call GetStream() before youve successfully bound something!");
return boundStream;
}
/// <summary>
/// indicates whether this instance is bound
/// </summary>
public bool IsBound { get { return boundStream != null; } }
/// <summary>
/// returns the complete canonical full path ("c:\path\to\archive|member") of the bound file
/// </summary>
public string CanonicalFullPath { get { return MakeCanonicalName(rootPath, memberPath); } }
/// <summary>
/// returns the complete canonical name ("archive|member") of the bound file
/// </summary>
public string CanonicalName { get { return MakeCanonicalName(Path.GetFileName(rootPath), memberPath); } }
/// <summary>
/// returns the virtual name of the bound file (disregarding the archive)
/// </summary>
public string Name { get { return GetBoundNameFromCanonical(MakeCanonicalName(rootPath, memberPath)); } }
/// <summary>
/// returns the extension of Name
/// </summary>
public string Extension { get { return Path.GetExtension(Name).ToUpper(); } }
/// <summary>
/// Indicates whether this file is an archive
/// </summary>
public bool IsArchive { get { return extractor != null; } }
int? BoundIndex;
public int? GetBoundIndex()
{
return BoundIndex;
}
//public class ArchiveItem
//{
// public string name;
// public long size;
// public int index;
//}
public IList<HawkFileArchiveItem> ArchiveItems
{
get
{
if (!IsArchive) throw new InvalidOperationException("Cant get archive items from non-archive");
return archiveItems;
}
}
/// <summary> /// <summary>
/// these extensions won't even be tried as archives (removes spurious archive detects since some of the signatures are pretty damn weak) /// these extensions won't even be tried as archives (removes spurious archive detects since some of the signatures are pretty damn weak)
/// </summary> /// </summary>
public string[] NonArchiveExtensions = new string[] { }; public string[] NonArchiveExtensions = new string[] { };
//---
bool exists;
bool rootExists;
string rootPath;
string memberPath;
Stream rootStream, boundStream;
IHawkFileArchiveHandler extractor;
List<HawkFileArchiveItem> archiveItems;
public HawkFile()
{
}
public void Open(string path) public void Open(string path)
{ {
if (rootPath != null) throw new InvalidOperationException("Don't reopen a HawkFile."); if (_rootPath != null)
{
throw new InvalidOperationException("Don't reopen a HawkFile.");
}
string autobind = null; string autobind = null;
bool isArchivePath = IsCanonicalArchivePath(path); bool isArchivePath = IsCanonicalArchivePath(path);
if (isArchivePath) if (isArchivePath)
{ {
string[] parts = path.Split('|'); var parts = path.Split('|');
path = parts[0]; path = parts[0];
autobind = parts[1]; autobind = parts[1];
} }
var fi = new FileInfo(path); var fi = new FileInfo(path);
rootExists = fi.Exists; _rootExists = fi.Exists;
if (fi.Exists == false) if (fi.Exists == false)
{
return; return;
}
rootPath = path; _rootPath = path;
exists = true; _exists = true;
AnalyzeArchive(path); AnalyzeArchive(path);
if (extractor == null) if (_extractor == null)
{ {
rootStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); _rootStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
//we could autobind here, but i dont want to // we could autobind here, but i dont want to
//bind it later with the desired extensions. // bind it later with the desired extensions.
} }
if (autobind == null) if (autobind == null)
{ {
//non-archive files can be automatically bound this way // non-archive files can be automatically bound this way
if (!isArchivePath) if (!isArchivePath)
{
BindRoot(); BindRoot();
}
} }
else else
{ {
autobind = autobind.ToUpperInvariant(); autobind = autobind.ToUpperInvariant();
if (extractor != null) if (_extractor != null)
{ {
var scanResults = extractor.Scan(); var scanResults = _extractor.Scan();
for (int i = 0; i < scanResults.Count; i++) for (int i = 0; i < scanResults.Count; i++)
{ {
if (scanResults[i].name.ToUpperInvariant() == autobind) if (scanResults[i].Name.ToUpperInvariant() == autobind)
{ {
BindArchiveMember(i); BindArchiveMember(i);
return; return;
@ -199,7 +218,7 @@ namespace BizHawk.Common
} }
} }
exists = false; _exists = false;
} }
} }
@ -211,13 +230,12 @@ namespace BizHawk.Common
Open(path); Open(path);
} }
/// <summary> /// <summary>
/// binds the specified ArchiveItem which you should have gotten by interrogating an archive hawkfile /// binds the specified ArchiveItem which you should have gotten by interrogating an archive hawkfile
/// </summary> /// </summary>
public HawkFile BindArchiveMember(HawkFileArchiveItem item) public HawkFile BindArchiveMember(HawkFileArchiveItem item)
{ {
return BindArchiveMember(item.archiveIndex); return BindArchiveMember(item.ArchiveIndex);
} }
/// <summary> /// <summary>
@ -225,7 +243,7 @@ namespace BizHawk.Common
/// </summary> /// </summary>
public HawkFileArchiveItem FindArchiveMember(string name) public HawkFileArchiveItem FindArchiveMember(string name)
{ {
return ArchiveItems.FirstOrDefault(ai => ai.name == name); return ArchiveItems.FirstOrDefault(ai => ai.Name == name);
} }
/// <summary> /// <summary>
@ -234,8 +252,12 @@ namespace BizHawk.Common
public HawkFile BindArchiveMember(string name) public HawkFile BindArchiveMember(string name)
{ {
var ai = FindArchiveMember(name); var ai = FindArchiveMember(name);
if (ai == null) return null; if (ai == null)
else return BindArchiveMember(ai); {
return null;
}
return BindArchiveMember(ai);
} }
/// <summary> /// <summary>
@ -243,16 +265,23 @@ namespace BizHawk.Common
/// </summary> /// </summary>
public HawkFile BindArchiveMember(int index) public HawkFile BindArchiveMember(int index)
{ {
if (!rootExists) return this; if (!_rootExists)
if (boundStream != null) throw new InvalidOperationException("stream already bound!"); {
return this;
}
boundStream = new MemoryStream(); if (_boundStream != null)
int archiveIndex = archiveItems[index].archiveIndex; {
extractor.ExtractFile(archiveIndex, boundStream); throw new InvalidOperationException("stream already bound!");
boundStream.Position = 0; }
memberPath = archiveItems[index].name; //TODO - maybe go through our own list of names? maybe not, its indexes dont match..
_boundStream = new MemoryStream();
int archiveIndex = _archiveItems[index].ArchiveIndex;
_extractor.ExtractFile(archiveIndex, _boundStream);
_boundStream.Position = 0;
_memberPath = _archiveItems[index].Name; // TODO - maybe go through our own list of names? maybe not, its indexes dont match..
Console.WriteLine("HawkFile bound " + CanonicalFullPath); Console.WriteLine("HawkFile bound " + CanonicalFullPath);
BoundIndex = archiveIndex; _boundIndex = archiveIndex;
return this; return this;
} }
@ -261,18 +290,22 @@ namespace BizHawk.Common
/// </summary> /// </summary>
public void Unbind() public void Unbind()
{ {
if (boundStream != null && boundStream != rootStream) boundStream.Close(); if (_boundStream != null && _boundStream != _rootStream)
boundStream = null; {
memberPath = null; _boundStream.Close();
BoundIndex = null; }
_boundStream = null;
_memberPath = null;
_boundIndex = null;
} }
/// <summary> /// <summary>
/// causes the root to be bound (in the case of non-archive files) /// causes the root to be bound (in the case of non-archive files)
/// </summary> /// </summary>
void BindRoot() private void BindRoot()
{ {
boundStream = rootStream; _boundStream = _rootStream;
Console.WriteLine("HawkFile bound " + CanonicalFullPath); Console.WriteLine("HawkFile bound " + CanonicalFullPath);
} }
@ -295,34 +328,42 @@ namespace BizHawk.Common
/// <summary> /// <summary>
/// Binds the first item in the archive (or the file itself) if the extension matches one of the supplied templates. /// 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 /// You probably should not use use BindSoleItemOf or the archive chooser instead
/// </summary> /// </summary>
public HawkFile BindFirstOf(params string[] extensions) public HawkFile BindFirstOf(params string[] extensions)
{ {
return BindByExtensionCore(true, extensions); return BindByExtensionCore(true, extensions);
} }
HawkFile BindByExtensionCore(bool first, params string[] extensions) private HawkFile BindByExtensionCore(bool first, params string[] extensions)
{ {
if (!rootExists) return this; if (!_rootExists)
if (boundStream != null) throw new InvalidOperationException("stream already bound!");
if (extractor == null)
{ {
//open uncompressed file return this;
string extension = Path.GetExtension(rootPath).Substring(1).ToUpperInvariant(); }
if (_boundStream != null)
{
throw new InvalidOperationException("stream already bound!");
}
if (_extractor == null)
{
// open uncompressed file
var extension = Path.GetExtension(_rootPath).Substring(1).ToUpperInvariant();
if (extensions.Length == 0 || extension.In(extensions)) if (extensions.Length == 0 || extension.In(extensions))
{ {
BindRoot(); BindRoot();
} }
return this; return this;
} }
var candidates = new List<int>(); var candidates = new List<int>();
for (int i = 0; i < archiveItems.Count; i++) for (int i = 0; i < _archiveItems.Count; i++)
{ {
var e = archiveItems[i]; var e = _archiveItems[i];
var extension = Path.GetExtension(e.name).ToUpperInvariant(); var extension = Path.GetExtension(e.Name).ToUpperInvariant();
extension = extension.TrimStart('.'); extension = extension.TrimStart('.');
if (extensions.Length == 0 || extension.In(extensions)) if (extensions.Length == 0 || extension.In(extensions))
{ {
@ -331,24 +372,31 @@ namespace BizHawk.Common
BindArchiveMember(i); BindArchiveMember(i);
return this; return this;
} }
candidates.Add(i); candidates.Add(i);
} }
} }
if (candidates.Count == 1) if (candidates.Count == 1)
{
BindArchiveMember(candidates[0]); BindArchiveMember(candidates[0]);
}
return this; return this;
} }
void ScanArchive() private void ScanArchive()
{ {
archiveItems = extractor.Scan(); _archiveItems = _extractor.Scan();
} }
private void AnalyzeArchive(string path) private void AnalyzeArchive(string path)
{ {
//no archive handler == no analysis // no archive handler == no analysis
if (ArchiveHandlerFactory == null) if (ArchiveHandlerFactory == null)
{
return; return;
}
int offset; int offset;
bool isExecutable; bool isExecutable;
@ -359,16 +407,16 @@ namespace BizHawk.Common
if (ArchiveHandlerFactory.CheckSignature(path, out offset, out isExecutable)) if (ArchiveHandlerFactory.CheckSignature(path, out offset, out isExecutable))
{ {
extractor = ArchiveHandlerFactory.Construct(path); _extractor = ArchiveHandlerFactory.Construct(path);
try try
{ {
ScanArchive(); ScanArchive();
} }
catch catch
{ {
extractor.Dispose(); _extractor.Dispose();
extractor = null; _extractor = null;
archiveItems = null; _archiveItems = null;
} }
} }
} }
@ -377,11 +425,18 @@ namespace BizHawk.Common
{ {
Unbind(); Unbind();
if (extractor != null) extractor.Dispose(); if (_extractor != null)
if (rootStream != null) rootStream.Dispose(); {
_extractor.Dispose();
}
extractor = null; if (_rootStream != null)
rootStream = null; {
_rootStream.Dispose();
}
_extractor = null;
_rootStream = null;
} }
/// <summary> /// <summary>
@ -389,7 +444,7 @@ namespace BizHawk.Common
/// </summary> /// </summary>
static bool IsCanonicalArchivePath(string path) static bool IsCanonicalArchivePath(string path)
{ {
return (path.IndexOf('|') != -1); return path.IndexOf('|') != -1;
} }
/// <summary> /// <summary>
@ -405,7 +460,7 @@ namespace BizHawk.Common
/// </summary> /// </summary>
static string GetBoundNameFromCanonical(string canonical) static string GetBoundNameFromCanonical(string canonical)
{ {
string[] parts = canonical.Split('|'); var parts = canonical.Split('|');
return parts[parts.Length - 1]; return parts[parts.Length - 1];
} }
@ -414,27 +469,14 @@ namespace BizHawk.Common
/// </summary> /// </summary>
string MakeCanonicalName(string root, string member) string MakeCanonicalName(string root, string member)
{ {
if (member == null) return root; if (member == null)
else return string.Format("{0}|{1}", root, member); {
return root;
}
return string.Format("{0}|{1}", root, member);
} }
}
} //class HawkFile
/// <summary>
/// Bridge between HawkFile and the frontend's implementation of archive management
/// </summary>
public interface IHawkFileArchiveHandler : IDisposable
{
//todo - could this receive a hawkfile itself? possibly handy, in very clever scenarios of mounting fake files
bool CheckSignature(string fileName, out int offset, out bool isExecutable);
List<HawkFileArchiveItem> Scan();
IHawkFileArchiveHandler Construct(string path);
void ExtractFile(int index, Stream stream);
}
/// <summary> /// <summary>
/// Members returned by IHawkFileArchiveHandler /// Members returned by IHawkFileArchiveHandler
@ -442,25 +484,24 @@ namespace BizHawk.Common
public class HawkFileArchiveItem public class HawkFileArchiveItem
{ {
/// <summary> /// <summary>
/// member name /// Gets or sets the member name
/// </summary> /// </summary>
public string name; public string Name { get; set; }
/// <summary> /// <summary>
/// size of member file /// Gets or sets the size of member file
/// </summary> /// </summary>
public long size; public long Size { get; set; }
/// <summary> /// <summary>
/// the index of this archive item /// Gets or sets the index of this archive item
/// </summary> /// </summary>
public int index; public int Index { get; set; }
/// <summary> /// <summary>
/// the index WITHIN THE ARCHIVE (for internal tracking by a IHawkFileArchiveHandler) of the member /// Gets or sets the index WITHIN THE ARCHIVE (for internal tracking by a IHawkFileArchiveHandler) of the member
/// </summary> /// </summary>
public int archiveIndex; public int ArchiveIndex { get; set; }
} }
}
} //namespace BizHawk.Common

View File

@ -2,52 +2,61 @@
{ {
public class MruStack<T> public class MruStack<T>
{ {
private readonly T[] store; private readonly T[] _store;
private int count; private int _count;
private int head; private int _head;
public int Count { get { return count; } }
public MruStack(int capacity) public MruStack(int capacity)
{ {
store = new T[capacity]; _store = new T[capacity];
Clear(); Clear();
} }
public int Count { get { return _count; } }
public void Clear() public void Clear()
{ {
head = 0; _head = 0;
count = 0; _count = 0;
for (int i = 0; i < store.Length; i++) for (int i = 0; i < _store.Length; i++)
store[i] = default(T); {
_store[i] = default(T);
}
} }
public void Push(T value) public void Push(T value)
{ {
store[head] = value; _store[_head] = value;
head = (head + 1) % store.Length; _head = (_head + 1) % _store.Length;
if (count < store.Length) if (_count < _store.Length)
count++; {
_count++;
}
} }
public T Pop() public T Pop()
{ {
if (count == 0) if (_count == 0)
{
return default(T); return default(T);
}
head--; _head--;
if (head < 0) if (_head < 0)
head = store.Length - 1; {
count--; _head = _store.Length - 1;
T value = store[head]; }
store[head] = default(T);
_count--;
T value = _store[_head];
_store[_head] = default(T);
return value; return value;
} }
public bool HasElements() public bool HasElements()
{ {
return count > 0; return _count > 0;
} }
} }
} }

View File

@ -1,4 +1,3 @@
using System;
using System.IO; using System.IO;
namespace BizHawk.Common namespace BizHawk.Common
@ -12,56 +11,60 @@ namespace BizHawk.Common
/// </summary> /// </summary>
public class SwitcherStream : Stream public class SwitcherStream : Stream
{ {
//switchstream method? flush old stream? // switchstream method? flush old stream?
Stream CurrStream = null; private Stream _currStream;
public void SetCurrStream(Stream str) { CurrStream = str; }
public SwitcherStream() public SwitcherStream()
{ {
} }
public override bool CanRead { get { return CurrStream.CanRead; } } public override bool CanRead { get { return _currStream.CanRead; } }
public override bool CanSeek { get { return CurrStream.CanSeek; } } public override bool CanSeek { get { return _currStream.CanSeek; } }
public override bool CanWrite { get { return CurrStream.CanWrite; } } public override bool CanWrite { get { return _currStream.CanWrite; } }
public override void Flush()
{
CurrStream.Flush();
}
public override long Length { get { return CurrStream.Length; } } public override long Length { get { return _currStream.Length; } }
public override long Position public override long Position
{ {
get get
{ {
return CurrStream.Position; return _currStream.Position;
} }
set set
{ {
CurrStream.Position = Position; _currStream.Position = Position;
} }
} }
public void SetCurrStream(Stream str)
{
_currStream = str;
}
public override void Flush()
{
_currStream.Flush();
}
public override int Read(byte[] buffer, int offset, int count) public override int Read(byte[] buffer, int offset, int count)
{ {
return CurrStream.Read(buffer, offset, count); return _currStream.Read(buffer, offset, count);
} }
public override long Seek(long offset, SeekOrigin origin) public override long Seek(long offset, SeekOrigin origin)
{ {
return CurrStream.Seek(offset, origin); return _currStream.Seek(offset, origin);
} }
public override void SetLength(long value) public override void SetLength(long value)
{ {
CurrStream.SetLength(value); _currStream.SetLength(value);
} }
public override void Write(byte[] buffer, int offset, int count) public override void Write(byte[] buffer, int offset, int count)
{ {
CurrStream.Write(buffer, offset, count); _currStream.Write(buffer, offset, count);
} }
} }
} }

View File

@ -6,9 +6,7 @@ namespace BizHawk.Common
public class UndoHistory<T> public class UndoHistory<T>
{ {
private List<List<T>> _history = new List<List<T>>(); private List<List<T>> _history = new List<List<T>>();
private int curPos; //1-based private int _curPos; // 1-based
public bool Enabled { get; private set; }
public UndoHistory(bool enabled) public UndoHistory(bool enabled)
{ {
@ -21,20 +19,16 @@ namespace BizHawk.Common
Enabled = enabled; Enabled = enabled;
} }
public void Clear() public bool Enabled { get; private set; }
{
_history = new List<List<T>>();
curPos = 0;
}
public bool CanUndo public bool CanUndo
{ {
get { return Enabled && curPos > 1; } get { return Enabled && _curPos > 1; }
} }
public bool CanRedo public bool CanRedo
{ {
get { return Enabled && curPos < _history.Count; } get { return Enabled && _curPos < _history.Count; }
} }
public bool HasHistory public bool HasHistory
@ -42,20 +36,26 @@ namespace BizHawk.Common
get { return Enabled && _history.Any(); } get { return Enabled && _history.Any(); }
} }
public void Clear()
{
_history = new List<List<T>>();
_curPos = 0;
}
public void AddState(IEnumerable<T> newState) public void AddState(IEnumerable<T> newState)
{ {
if (Enabled) if (Enabled)
{ {
if (curPos < _history.Count) if (_curPos < _history.Count)
{ {
for (int i = curPos + 1; i <= _history.Count; i++) for (var i = _curPos + 1; i <= _history.Count; i++)
{ {
_history.Remove(_history[i - 1]); _history.Remove(_history[i - 1]);
} }
} }
_history.Add(newState.ToList()); _history.Add(newState.ToList());
curPos = _history.Count; _curPos = _history.Count;
} }
} }
@ -63,26 +63,22 @@ namespace BizHawk.Common
{ {
if (CanUndo && Enabled) if (CanUndo && Enabled)
{ {
curPos--; _curPos--;
return _history[curPos - 1]; return _history[_curPos - 1];
}
else
{
return null;
} }
return Enumerable.Empty<T>();
} }
public IEnumerable<T> Redo() public IEnumerable<T> Redo()
{ {
if (CanRedo && Enabled) if (CanRedo && Enabled)
{ {
curPos++; _curPos++;
return _history[curPos - 1]; return _history[_curPos - 1];
}
else
{
return null;
} }
return Enumerable.Empty<T>();
} }
} }
} }

View File

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Collections.Generic;
namespace BizHawk.Common namespace BizHawk.Common
{ {
@ -10,14 +10,14 @@ namespace BizHawk.Common
private static readonly char[] HexConvArr = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; private static readonly char[] HexConvArr = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
private static System.Runtime.InteropServices.GCHandle HexConvHandle; private static System.Runtime.InteropServices.GCHandle HexConvHandle;
public static char* HexConvPtr { get; set; }
static Util() static Util()
{ {
HexConvHandle = System.Runtime.InteropServices.GCHandle.Alloc(HexConvArr, System.Runtime.InteropServices.GCHandleType.Pinned); HexConvHandle = System.Runtime.InteropServices.GCHandle.Alloc(HexConvArr, System.Runtime.InteropServices.GCHandleType.Pinned);
HexConvPtr = (char*)HexConvHandle.AddrOfPinnedObject().ToPointer(); HexConvPtr = (char*)HexConvHandle.AddrOfPinnedObject().ToPointer();
} }
public static char* HexConvPtr { get; set; }
public static string Hash_MD5(byte[] data, int offset, int len) public static string Hash_MD5(byte[] data, int offset, int len)
{ {
using (var md5 = System.Security.Cryptography.MD5.Create()) using (var md5 = System.Security.Cryptography.MD5.Create())
@ -58,11 +58,11 @@ namespace BizHawk.Common
public static int SaveRamBytesUsed(byte[] saveRam) public static int SaveRamBytesUsed(byte[] saveRam)
{ {
for (int j = saveRam.Length - 1; j >= 0; j--) for (var i = saveRam.Length - 1; i >= 0; i--)
{ {
if (saveRam[j] != 0) if (saveRam[i] != 0)
{ {
return j + 1; return i + 1;
} }
} }
@ -73,7 +73,7 @@ namespace BizHawk.Common
public static string ReadStringFixedAscii(this BinaryReader r, int bytes) public static string ReadStringFixedAscii(this BinaryReader r, int bytes)
{ {
var read = new byte[bytes]; var read = new byte[bytes];
for (int b = 0; b < bytes; b++) for (var b = 0; b < bytes; b++)
{ {
read[b] = r.ReadByte(); read[b] = r.ReadByte();
} }
@ -84,7 +84,7 @@ namespace BizHawk.Common
public static string ReadStringAsciiZ(this BinaryReader r) public static string ReadStringAsciiZ(this BinaryReader r)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
for (; ; ) for (;;)
{ {
int b = r.ReadByte(); int b = r.ReadByte();
if (b <= 0) if (b <= 0)
@ -128,7 +128,7 @@ namespace BizHawk.Common
int d = 0; int d = 0;
for (int j = 0; j < 2; j++) for (int j = 0; j < 2; j++)
{ {
var c = char.ToLower(str[i * 2 + j]); var c = char.ToLower(str[(i * 2) + j]);
if (c >= '0' && c <= '9') if (c >= '0' && c <= '9')
{ {
d += c - '0'; d += c - '0';
@ -146,11 +146,11 @@ namespace BizHawk.Common
{ {
d <<= 4; d <<= 4;
} }
} }
ms.WriteByte((byte)d); ms.WriteByte((byte)d);
} }
return ms.ToArray(); return ms.ToArray();
} }
@ -346,11 +346,11 @@ namespace BizHawk.Common
public static string FormatFileSize(long filesize) public static string FormatFileSize(long filesize)
{ {
Decimal size = filesize; decimal size = filesize;
Decimal OneKiloByte = 1024M; const decimal OneKiloByte = 1024M;
Decimal OneMegaByte = OneKiloByte * 1024M; const decimal OneMegaByte = OneKiloByte * 1024M;
Decimal OneGigaByte = OneMegaByte * 1024M; decimal OneGigaByte = OneMegaByte * 1024M;
string suffix; string suffix;
if (size > 1024 * 1024 * 1024) if (size > 1024 * 1024 * 1024)
@ -373,26 +373,45 @@ namespace BizHawk.Common
suffix = " B"; suffix = " B";
} }
var precision = "2"; const string precision = "2";
return String.Format("{0:N" + precision + "}{1}", size, suffix); return string.Format("{0:N" + precision + "}{1}", size, suffix);
} }
// http://stackoverflow.com/questions/3928822/comparing-2-dictionarystring-string-instances // http://stackoverflow.com/questions/3928822/comparing-2-dictionarystring-string-instances
public static bool DictionaryEqual<TKey, TValue>( public static bool DictionaryEqual<TKey, TValue>(
IDictionary<TKey, TValue> first, IDictionary<TKey, TValue> second) IDictionary<TKey, TValue> first, IDictionary<TKey, TValue> second)
{ {
if (first == second) return true; if (first == second)
if ((first == null) || (second == null)) return false; {
if (first.Count != second.Count) return false; return true;
}
if ((first == null) || (second == null))
{
return false;
}
if (first.Count != second.Count)
{
return false;
}
var comparer = EqualityComparer<TValue>.Default; var comparer = EqualityComparer<TValue>.Default;
foreach (KeyValuePair<TKey, TValue> kvp in first) foreach (var kvp in first)
{ {
TValue secondValue; TValue secondValue;
if (!second.TryGetValue(kvp.Key, out secondValue)) return false; if (!second.TryGetValue(kvp.Key, out secondValue))
if (!comparer.Equals(kvp.Value, secondValue)) return false; {
return false;
}
if (!comparer.Equals(kvp.Value, secondValue))
{
return false;
}
} }
return true; return true;
} }
@ -407,9 +426,11 @@ namespace BizHawk.Common
{ {
TValue ret; TValue ret;
if (!dict.TryGetValue(key, out ret)) if (!dict.TryGetValue(key, out ret))
{
return defaultvalue; return defaultvalue;
else }
return ret;
return ret;
} }
} }
@ -420,6 +441,14 @@ namespace BizHawk.Common
internal class SuperGloballyUniqueID internal class SuperGloballyUniqueID
{ {
private static readonly string StaticPart;
private static int ctr;
static SuperGloballyUniqueID()
{
StaticPart = "bizhawk-" + System.Diagnostics.Process.GetCurrentProcess().Id + "-" + Guid.NewGuid();
}
public static string Next() public static string Next()
{ {
int myctr; int myctr;
@ -430,13 +459,5 @@ namespace BizHawk.Common
return StaticPart + "-" + myctr; return StaticPart + "-" + myctr;
} }
static SuperGloballyUniqueID()
{
StaticPart = "bizhawk-" + System.Diagnostics.Process.GetCurrentProcess().Id + "-" + Guid.NewGuid();
}
private static readonly string StaticPart;
private static int ctr;
} }
} }