re-engineer HawkFile to be aware of archives. its a little more complex to use now (not only do you have to open it, you have to call one of the Bind() methods on it to choose an interior file), but its more powerful.
This commit is contained in:
parent
3ec1ed128d
commit
341ee44509
|
@ -1,12 +1,11 @@
|
|||
//http://nesdev.parodius.com/bbs/viewtopic.php?p=4571&sid=db4c7e35316cc5d734606dd02f11dccb
|
||||
|
||||
using System;
|
||||
using System.Xml;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using BizHawk.Emulation.CPUs.M6502;
|
||||
|
||||
|
||||
namespace BizHawk.Emulation.Consoles.Nintendo
|
||||
{
|
||||
|
|
|
@ -3,92 +3,230 @@ using System.IO;
|
|||
|
||||
namespace BizHawk.MultiClient
|
||||
{
|
||||
//todo:
|
||||
//split into "bind" and "open (the bound thing)"
|
||||
//scan archive to flatten interior directories down to a path (maintain our own archive item list)
|
||||
|
||||
public class HawkFile : IDisposable
|
||||
{
|
||||
private bool zipped;
|
||||
public bool Zipped { get { return zipped; } }
|
||||
/// <summary>
|
||||
/// returns whether a bound file exists. if there is no bound file, it can't exist
|
||||
/// </summary>
|
||||
public bool Exists { get { if (!rootExists) return false; return boundStream != null; } }
|
||||
|
||||
private bool exists;
|
||||
public bool Exists { get { return exists; } }
|
||||
/// <summary>
|
||||
/// returns whether the root exists (the actual physical file)
|
||||
/// </summary>
|
||||
public bool RootExists { get { return rootExists; } }
|
||||
|
||||
private string extension;
|
||||
public string Extension { get { return extension; } }
|
||||
|
||||
public string Directory { get { return Path.GetDirectoryName(rawFileName); } }
|
||||
|
||||
private string rawFileName;
|
||||
private string name;
|
||||
public string Name { get { return name; } }
|
||||
public string FullName { get { return name + "." + extension; } }
|
||||
|
||||
private IDisposable thingToDispose;
|
||||
private Stream zippedStream;
|
||||
|
||||
public HawkFile(string path) : this(path,"SMS","PCE","SGX","GG","SG","BIN","SMD","GB","IPS") {}
|
||||
|
||||
public HawkFile(string path, params string[] recognizedExtensions)
|
||||
{
|
||||
var file = new FileInfo(path);
|
||||
|
||||
exists = file.Exists;
|
||||
if (file.Exists == false)
|
||||
return;
|
||||
|
||||
if (file.Extension.ToLower().In(".zip",".rar",".7z"))
|
||||
{
|
||||
LoadZipFile(path, recognizedExtensions);
|
||||
return;
|
||||
}
|
||||
|
||||
zipped = false;
|
||||
extension = file.Extension.Substring(1).ToUpperInvariant();
|
||||
rawFileName = path;
|
||||
name = Path.GetFileNameWithoutExtension(path);
|
||||
}
|
||||
|
||||
private void LoadZipFile(string path, string[] recognizedExtensions)
|
||||
{
|
||||
zipped = true;
|
||||
rawFileName = path;
|
||||
|
||||
using (var extractor = new SevenZip.SevenZipExtractor(path))
|
||||
{
|
||||
thingToDispose = extractor;
|
||||
foreach (var e in extractor.ArchiveFileData)
|
||||
{
|
||||
extension = Path.GetExtension(e.FileName).Substring(1).ToUpperInvariant();
|
||||
|
||||
if (extension.In(recognizedExtensions))
|
||||
{
|
||||
// we found our match.
|
||||
name = Path.GetFileNameWithoutExtension(e.FileName);
|
||||
zippedStream = new MemoryStream();
|
||||
//e.Extract(zippedStream);
|
||||
extractor.ExtractFile(e.Index,zippedStream);
|
||||
thingToDispose = zippedStream;
|
||||
return;
|
||||
}
|
||||
}
|
||||
exists = false;
|
||||
}
|
||||
}
|
||||
/// <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 (zipped == false)
|
||||
{
|
||||
var stream = new FileStream(rawFileName, FileMode.Open, FileAccess.Read);
|
||||
thingToDispose = stream;
|
||||
return stream;
|
||||
if (boundStream == null)
|
||||
throw new InvalidOperationException("HawkFil: Can't call GetStream() before youve successfully bound something!");
|
||||
return boundStream;
|
||||
}
|
||||
|
||||
return zippedStream;
|
||||
/// <summary>
|
||||
/// indicates whether this instance is bound
|
||||
/// </summary>
|
||||
public bool IsBound { get { return boundStream != null; } }
|
||||
|
||||
/// <summary>
|
||||
/// returns the complete canonical name ("archive|member") of the bound file
|
||||
/// </summary>
|
||||
public string CanonicalName { get { return MakeCanonicalName(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); } }
|
||||
|
||||
//---
|
||||
bool rootExists;
|
||||
string rootPath;
|
||||
string memberPath;
|
||||
Stream rootStream, boundStream;
|
||||
SevenZip.SevenZipExtractor extractor;
|
||||
|
||||
public static bool PathExists(string path)
|
||||
{
|
||||
using (var hf = new HawkFile(path))
|
||||
return hf.Exists;
|
||||
}
|
||||
|
||||
public HawkFile(string path)
|
||||
{
|
||||
string autobind = null;
|
||||
if (IsCanonicalArchivePath(path))
|
||||
{
|
||||
string[] parts = path.Split('|');
|
||||
path = parts[0];
|
||||
autobind = parts[1];
|
||||
}
|
||||
|
||||
var fi = new FileInfo(path);
|
||||
|
||||
rootExists = fi.Exists;
|
||||
if (fi.Exists == false)
|
||||
return;
|
||||
|
||||
rootPath = path;
|
||||
|
||||
AnalyzeArchive(path);
|
||||
if (extractor == null)
|
||||
{
|
||||
rootStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
}
|
||||
|
||||
if (autobind != null)
|
||||
{
|
||||
autobind = autobind.ToUpperInvariant();
|
||||
for (int i = 0; i < extractor.ArchiveFileData.Count; i++)
|
||||
{
|
||||
if (extractor.ArchiveFileNames[i].ToUpperInvariant() == autobind)
|
||||
{
|
||||
BindArchiveMember(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// is the supplied path a canonical name including an archive?
|
||||
/// </summary>
|
||||
bool IsCanonicalArchivePath(string path)
|
||||
{
|
||||
return (path.IndexOf('|') != -1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// converts a canonical name to a bound name (the bound part, whether or not it is an archive)
|
||||
/// </summary>
|
||||
string GetBoundNameFromCanonical(string canonical)
|
||||
{
|
||||
string[] parts = canonical.Split('|');
|
||||
return parts[parts.Length - 1];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// makes a canonical name from two parts
|
||||
/// </summary>
|
||||
string MakeCanonicalName(string root, string member)
|
||||
{
|
||||
if (member == null) return root;
|
||||
else return string.Format("{0}|{1}", root, member);
|
||||
}
|
||||
|
||||
void BindArchiveMember(int index)
|
||||
{
|
||||
boundStream = new MemoryStream();
|
||||
extractor.ExtractFile(index, boundStream);
|
||||
boundStream.Position = 0;
|
||||
memberPath = extractor.ArchiveFileNames[index];
|
||||
Console.WriteLine("bound " + CanonicalName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes any existing binding
|
||||
/// </summary>
|
||||
public void Unbind()
|
||||
{
|
||||
if (boundStream != null && boundStream != rootStream) boundStream.Close();
|
||||
boundStream = null;
|
||||
memberPath = null;
|
||||
}
|
||||
|
||||
void BindRoot()
|
||||
{
|
||||
boundStream = rootStream;
|
||||
Console.WriteLine("bound " + CanonicalName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds the first item in the archive (or the file itself). Supposing that there is anything in the archive.
|
||||
/// </summary>
|
||||
public HawkFile BindFirst()
|
||||
{
|
||||
BindFirstOf();
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds the first item in the archive (or the file itself) if the extension matches one of the supplied templates
|
||||
/// </summary>
|
||||
public HawkFile BindFirstOf(params string[] extensions)
|
||||
{
|
||||
if (!rootExists) return this;
|
||||
if (boundStream != null) throw new InvalidOperationException("stream already bound!");
|
||||
|
||||
if (extractor == null)
|
||||
{
|
||||
//open uncompressed file
|
||||
string extension = Path.GetExtension(rootPath).Substring(1).ToUpperInvariant();
|
||||
if (extensions.Length==0 || extension.In(extensions))
|
||||
{
|
||||
BindRoot();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
for(int i=0;i<extractor.ArchiveFileData.Count;i++)
|
||||
{
|
||||
var e = extractor.ArchiveFileData[i];
|
||||
var extension = Path.GetExtension(e.FileName).Substring(1).ToUpperInvariant();
|
||||
if (extensions.Length == 0 || extension.In(extensions))
|
||||
{
|
||||
BindArchiveMember(i);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
private void AnalyzeArchive(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
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..
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
//must not be an archive. is there a better way to determine this? the exceptions are as annoying as hell
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (thingToDispose != null)
|
||||
thingToDispose.Dispose();
|
||||
Unbind();
|
||||
|
||||
if (extractor != null) extractor.Dispose();
|
||||
if (rootStream != null) rootStream.Dispose();
|
||||
|
||||
extractor = null;
|
||||
rootStream = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -46,6 +46,11 @@ namespace BizHawk.MultiClient
|
|||
|
||||
public MainForm(string[] args)
|
||||
{
|
||||
//using (HawkFile NesCartFile = new HawkFile("NesCarts.7z").BindFirst())
|
||||
//{
|
||||
// var NesCartXmlBytes = Util.ReadAllBytes(NesCartFile.GetStream());
|
||||
//}
|
||||
|
||||
Global.MainForm = this;
|
||||
Global.Config = ConfigService.Load<Config>("config.ini");
|
||||
|
||||
|
@ -386,12 +391,13 @@ namespace BizHawk.MultiClient
|
|||
|
||||
private bool LoadRom(string path)
|
||||
{
|
||||
var file = new FileInfo(path);
|
||||
if (file.Exists == false) return false;
|
||||
using (var file = new HawkFile(path))
|
||||
{
|
||||
if (!file.RootExists) return false;
|
||||
|
||||
CloseGame();
|
||||
|
||||
var game = new RomGame(path);
|
||||
var game = new RomGame(file);
|
||||
Global.Game = game;
|
||||
|
||||
switch (game.System)
|
||||
|
@ -443,7 +449,7 @@ namespace BizHawk.MultiClient
|
|||
Global.Emulator.LoadGame(game);
|
||||
Text = DisplayNameForSystem(game.System) + " - " + game.Name;
|
||||
ResetRewindBuffer();
|
||||
Global.Config.RecentRoms.Add(file.FullName);
|
||||
Global.Config.RecentRoms.Add(file.CanonicalName);
|
||||
if (File.Exists(game.SaveRamPath))
|
||||
LoadSaveRam();
|
||||
|
||||
|
@ -474,6 +480,7 @@ namespace BizHawk.MultiClient
|
|||
CurrentlyOpenRom = path;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadSaveRam()
|
||||
{
|
||||
|
|
|
@ -14,12 +14,12 @@ namespace BizHawk.MultiClient
|
|||
private List<string> options;
|
||||
private const int BankSize = 4096;
|
||||
|
||||
public RomGame(string path) : this(path, null){}
|
||||
public RomGame(HawkFile file) : this(file, null){}
|
||||
|
||||
public RomGame(string path, string patch)
|
||||
{
|
||||
using (var file = new HawkFile(path))
|
||||
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.");
|
||||
|
||||
|
@ -37,18 +37,19 @@ namespace BizHawk.MultiClient
|
|||
if (file.Extension == "SMD")
|
||||
RomData = DeInterleaveSMD(RomData);
|
||||
|
||||
var info = Database.GetGameInfo(RomData, file.FullName);
|
||||
var info = Database.GetGameInfo(RomData, file.Name);
|
||||
name = info.Name;
|
||||
System = info.System;
|
||||
options = new List<string>(info.GetOptions());
|
||||
CheckForPatchOptions();
|
||||
}
|
||||
|
||||
if (patch != null)
|
||||
{
|
||||
using (var stream = new HawkFile(patch).GetStream())
|
||||
using (var patchFile = new HawkFile(patch))
|
||||
{
|
||||
IPS.Patch(RomData, stream);
|
||||
patchFile.BindFirstOf("IPS");
|
||||
if(patchFile.Exists)
|
||||
IPS.Patch(RomData, patchFile.GetStream());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,8 +24,10 @@ namespace SevenZip
|
|||
/// The signature checker class. Original code by Siddharth Uppal, adapted by Markhor.
|
||||
/// </summary>
|
||||
/// <remarks>Based on the code at http://blog.somecreativity.com/2008/04/08/how-to-check-if-a-file-is-compressed-in-c/#</remarks>
|
||||
internal static class FileChecker
|
||||
public static class FileChecker
|
||||
{
|
||||
public static bool ThrowExceptions = true;
|
||||
|
||||
private const int SIGNATURE_SIZE = 16;
|
||||
private const int SFX_SCAN_LENGTH = 256 * 1024;
|
||||
|
||||
|
@ -69,13 +71,19 @@ namespace SevenZip
|
|||
public static InArchiveFormat CheckSignature(Stream stream, out int offset, out bool isExecutable)
|
||||
{
|
||||
offset = 0;
|
||||
isExecutable = false;
|
||||
|
||||
if (!stream.CanRead)
|
||||
{
|
||||
if (ThrowExceptions)
|
||||
throw new ArgumentException("The stream must be readable.");
|
||||
else return InArchiveFormat.None;
|
||||
}
|
||||
if (stream.Length < SIGNATURE_SIZE)
|
||||
{
|
||||
if (ThrowExceptions)
|
||||
throw new ArgumentException("The stream is invalid.");
|
||||
else return InArchiveFormat.None;
|
||||
}
|
||||
|
||||
#region Get file signature
|
||||
|
@ -208,7 +216,9 @@ namespace SevenZip
|
|||
}
|
||||
#endregion
|
||||
|
||||
if (ThrowExceptions)
|
||||
throw new ArgumentException("The stream is invalid or no corresponding signature was found.");
|
||||
else return InArchiveFormat.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -225,16 +235,18 @@ namespace SevenZip
|
|||
{
|
||||
try
|
||||
{
|
||||
return CheckSignature(fs, out offset, out isExecutable);
|
||||
InArchiveFormat format = CheckSignature(fs, out offset, out isExecutable);
|
||||
if (format != InArchiveFormat.None) return format;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
}
|
||||
|
||||
offset = 0;
|
||||
isExecutable = false;
|
||||
return Formats.FormatByFileName(fileName, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -186,7 +186,11 @@ namespace SevenZip
|
|||
/// Microsoft virtual hard disk file format.
|
||||
/// </summary>
|
||||
/// <remarks><a href="http://en.wikipedia.org/wiki/VHD_%28file_format%29">Wikipedia information</a></remarks>
|
||||
Vhd
|
||||
Vhd,
|
||||
/// <summary>
|
||||
/// Not an archive
|
||||
/// </summary>
|
||||
None
|
||||
}
|
||||
|
||||
#if COMPRESS
|
||||
|
@ -519,8 +523,10 @@ namespace SevenZip
|
|||
string extension = Path.GetExtension(fileName).Substring(1);
|
||||
if (!InExtensionFormats.ContainsKey(extension) && reportErrors)
|
||||
{
|
||||
throw new ArgumentException("Extension \"" + extension +
|
||||
"\" is not a supported archive file name extension.");
|
||||
if (FileChecker.ThrowExceptions)
|
||||
throw new ArgumentException("Extension \"" + extension + "\" is not a supported archive file name extension.");
|
||||
else return InArchiveFormat.None;
|
||||
|
||||
}
|
||||
return InExtensionFormats[extension];
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue