using System; using System.Collections.Generic; using System.IO; using ICSharpCode.SharpZipLib.Zip; //using Ionic.Zip; namespace BizHawk.Client.Common { public enum BinaryStateLump { Versiontag, Corestate, Framebuffer, Input, CorestateText, // Only for movies they probably shoudln't be leaching this stuff Movieheader, Comments, Subtitles, SyncSettings, // TasMovie LagLog, Greenzone, GreenzoneSettings, Markers, ClientSettings } public static class BinaryStateFileNames { private static readonly Dictionary ReadNames; private static readonly Dictionary WriteNames; static void AddLumpName(BinaryStateLump token, string name) { ReadNames[token] = Path.GetFileNameWithoutExtension(name); WriteNames[token] = name; } static BinaryStateFileNames() { ReadNames = new Dictionary(); WriteNames = new Dictionary(); AddLumpName(BinaryStateLump.Versiontag, "BizState 1.0"); AddLumpName(BinaryStateLump.Corestate, "Core"); AddLumpName(BinaryStateLump.Framebuffer, "Framebuffer"); AddLumpName(BinaryStateLump.Input, "Input Log.txt"); AddLumpName(BinaryStateLump.CorestateText, "CoreText.txt"); AddLumpName(BinaryStateLump.Movieheader, "Header.txt"); // Only for movies they probably shoudln't be leaching this stuff AddLumpName(BinaryStateLump.Comments, "Comments.txt"); AddLumpName(BinaryStateLump.Subtitles, "Subtitles.txt"); AddLumpName(BinaryStateLump.SyncSettings, "SyncSettings.json"); // TasMovie AddLumpName(BinaryStateLump.LagLog, "LagLog"); AddLumpName(BinaryStateLump.Greenzone, "GreenZone"); AddLumpName(BinaryStateLump.GreenzoneSettings, "GreenZoneSettings.txt"); AddLumpName(BinaryStateLump.Markers, "Markers.txt"); AddLumpName(BinaryStateLump.ClientSettings, "ClientSettings.json"); } public static string GetReadName(BinaryStateLump lump) { return ReadNames[lump]; } public static string GetWriteName(BinaryStateLump lump) { return WriteNames[lump]; } } /// /// more accurately should be called ZipStateLoader, as it supports both text and binary core data /// public class BinaryStateLoader : IDisposable { private ZipFile _zip; private Version _ver; private bool _isDisposed; private Dictionary _entriesbyname; private BinaryStateLoader() { } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_isDisposed) { _isDisposed = true; if (disposing) { _zip.Close(); } } } private void ReadVersion(Stream s, long length) { // the "BizState 1.0" tag contains an integer in it describing the sub version. if (length == 0) { _ver = new Version(1, 0, 0); // except for the first release, which doesn't } else { var sr = new StreamReader(s); _ver = new Version(1, 0, int.Parse(sr.ReadLine())); } Console.WriteLine("Read a zipstate of version {0}", _ver); } private void PopulateEntries() { _entriesbyname = new Dictionary(); foreach (ZipEntry z in _zip) { _entriesbyname.Add(Path.GetFileNameWithoutExtension(z.Name), z); } } public static BinaryStateLoader LoadAndDetect(string filename, bool isMovieLoad = false) { var ret = new BinaryStateLoader(); // PORTABLE TODO - SKIP THIS.. FOR NOW // check whether its an archive before we try opening it bool isArchive; using (var archiveChecker = new SevenZipSharpArchiveHandler()) { int offset; bool isExecutable; isArchive = archiveChecker.CheckSignature(filename, out offset, out isExecutable); } if (!isArchive) { return null; } try { ret._zip = new ZipFile(filename); ret.PopulateEntries(); if (!isMovieLoad && !ret.GetLump(BinaryStateLump.Versiontag, false, ret.ReadVersion)) { ret._zip.Close(); return null; } return ret; } catch (ZipException) { return null; } } public bool HasLump(BinaryStateLump lump) { string name = BinaryStateFileNames.GetReadName(lump); ZipEntry e; return _entriesbyname.TryGetValue(name, out e); } /// /// Gets a lump /// /// lump to retriever /// true to throw exception on failure /// function to call with the desired stream /// true if callback was called and stream was loaded public bool GetLump(BinaryStateLump lump, bool abort, Action callback) { string name = BinaryStateFileNames.GetReadName(lump); ZipEntry e; if (_entriesbyname.TryGetValue(name, out e)) { using (var zs = _zip.GetInputStream(e)) { callback(zs, e.Size); } return true; } if (abort) { throw new Exception("Essential zip section not found: " + name); } return false; } public bool GetLump(BinaryStateLump lump, bool abort, Action callback) { return GetLump(lump, abort, delegate(Stream s, long unused) { var br = new BinaryReader(s); callback(br); }); } public bool GetLump(BinaryStateLump lump, bool abort, Action callback) { return GetLump(lump, abort, delegate(Stream s, long length) { var br = new BinaryReader(s); callback(br, length); }); } public bool GetLump(BinaryStateLump lump, bool abort, Action callback) { return GetLump(lump, abort, delegate(Stream s, long unused) { var tr = new StreamReader(s); callback(tr); }); } public void GetCoreState(Action callbackBinary, Action callbackText) { if (!GetLump(BinaryStateLump.Corestate, false, callbackBinary) && !GetLump(BinaryStateLump.CorestateText, false, callbackText)) { throw new Exception("Couldn't find Binary or Text savestate"); } } public void GetCoreState(Action callbackBinary, Action callbackText) { if (!GetLump(BinaryStateLump.Corestate, false, callbackBinary) && !GetLump(BinaryStateLump.CorestateText, false, callbackText)) { throw new Exception("Couldn't find Binary or Text savestate"); } } } public class BinaryStateSaver : IDisposable { private readonly IZipWriter _zip; private bool _isDisposed; private static void WriteVersion(Stream s) { var sw = new StreamWriter(s); sw.WriteLine("1"); // version 1.0.1 sw.Flush(); } public BinaryStateSaver(string path, bool stateVersionTag = true) // stateVersionTag is a hack for reusing this for movie code { _zip = new IonicZipWriter(path, Global.Config.SaveStateCompressionLevelNormal); //_zip = new SharpZipWriter(path, Global.Config.SaveStateCompressionLevelNormal); //_zip = new SevenZipWriter(path, Global.Config.SaveStateCompressionLevelNormal); if (stateVersionTag) { PutLump(BinaryStateLump.Versiontag, WriteVersion); } } public void PutLump(BinaryStateLump lump, Action callback) { var name = BinaryStateFileNames.GetWriteName(lump); _zip.WriteItem(name, callback); } public void PutLump(BinaryStateLump lump, Action callback) { PutLump(lump, delegate(Stream s) { var bw = new BinaryWriter(s); callback(bw); bw.Flush(); }); } public void PutLump(BinaryStateLump lump, Action callback) { PutLump(lump, delegate(Stream s) { TextWriter tw = new StreamWriter(s); callback(tw); tw.Flush(); }); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_isDisposed) { _isDisposed = true; if (disposing) { _zip.Dispose(); } } } } }