From a81912fd60e54552174981b684a21aa646f6ae4c Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Wed, 7 Jun 2017 19:55:51 +0100 Subject: [PATCH] Adds support for XZ compressed files. --- .../DiscImageChef.Filters.csproj | 1 + DiscImageChef.Filters/XZ.cs | 223 +++++++++++++++++- README.md | 1 + TODO | 1 - 4 files changed, 221 insertions(+), 5 deletions(-) diff --git a/DiscImageChef.Filters/DiscImageChef.Filters.csproj b/DiscImageChef.Filters/DiscImageChef.Filters.csproj index 242472c1f..40f8a67d6 100644 --- a/DiscImageChef.Filters/DiscImageChef.Filters.csproj +++ b/DiscImageChef.Filters/DiscImageChef.Filters.csproj @@ -49,6 +49,7 @@ + diff --git a/DiscImageChef.Filters/XZ.cs b/DiscImageChef.Filters/XZ.cs index 3e9cf7fe6..fdc447e0e 100644 --- a/DiscImageChef.Filters/XZ.cs +++ b/DiscImageChef.Filters/XZ.cs @@ -5,11 +5,11 @@ // Filename : XZ.cs // Author(s) : Natalia Portillo // -// Component : Component +// Component : Filters. // // --[ Description ] ---------------------------------------------------------- // -// Description +// Allow to open files that are compressed using xz. // // --[ License ] -------------------------------------------------------------- // @@ -29,13 +29,228 @@ // ---------------------------------------------------------------------------- // Copyright © 2011-2017 Natalia Portillo // ****************************************************************************/ + using System; +using System.IO; +using SharpCompress.Compressors.Xz; + namespace DiscImageChef.Filters { - public class XZ + public class XZ : Filter { + Stream dataStream; + string basePath; + DateTime lastWriteTime; + DateTime creationTime; + bool opened; + long decompressedSize; + Stream innerStream; + public XZ() { + Name = "XZ"; + UUID = new Guid("FCCFB0C3-32EF-40D8-9714-2333F6AC72A9"); + } + + public override void Close() + { + if(dataStream != null) + dataStream.Close(); + dataStream = null; + basePath = null; + opened = false; + } + + public override string GetBasePath() + { + return basePath; + } + + public override Stream GetDataForkStream() + { + return innerStream; + } + + public override string GetPath() + { + return basePath; + } + + public override Stream GetResourceForkStream() + { + return null; + } + + public override bool HasResourceFork() + { + return false; + } + + public override bool Identify(byte[] buffer) + { + return buffer[0] == 0xFD && buffer[1] == 0x37 && buffer[2] == 0x7A && buffer[3] == 0x58 && buffer[4] == 0x5A && buffer[5] == 0x00 && buffer[buffer.Length - 2] == 0x59 && buffer[buffer.Length - 1] == 0x5A; + } + + public override bool Identify(Stream stream) + { + byte[] buffer = new byte[6]; + byte[] footer = new byte[2]; + + stream.Seek(0, SeekOrigin.Begin); + stream.Read(buffer, 0, 6); + stream.Seek(-2, SeekOrigin.End); + stream.Read(footer, 0, 2); + stream.Seek(0, SeekOrigin.Begin); + + return buffer[0] == 0xFD && buffer[1] == 0x37 && buffer[2] == 0x7A && buffer[3] == 0x58 && buffer[4] == 0x5A && buffer[5] == 0x00 && footer[0] == 0x59 && footer[1] == 0x5A; + } + + public override bool Identify(string path) + { + if(File.Exists(path)) + { + FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read); + byte[] buffer = new byte[6]; + byte[] footer = new byte[2]; + + stream.Seek(0, SeekOrigin.Begin); + stream.Read(buffer, 0, 6); + stream.Seek(0, SeekOrigin.Begin); + stream.Seek(-2, SeekOrigin.End); + stream.Read(footer, 0, 2); + stream.Seek(0, SeekOrigin.Begin); + + return buffer[0] == 0xFD && buffer[1] == 0x37 && buffer[2] == 0x7A && buffer[3] == 0x58 && buffer[4] == 0x5A && buffer[5] == 0x00 && footer[0] == 0x59 && footer[1] == 0x5A; + } + + return false; + } + + void GuessSize() + { + decompressedSize = 0; + // Seek to footer backwards size field + dataStream.Seek(-8, SeekOrigin.End); + byte[] tmp = new byte[4]; + dataStream.Read(tmp, 0, 4); + uint backwardSize = (BitConverter.ToUInt32(tmp, 0) + 1) * 4; + // Seek to first indexed record + dataStream.Seek(-12 - (backwardSize - 2), SeekOrigin.End); + + // Skip compressed size + tmp = new byte[backwardSize - 2]; + dataStream.Read(tmp, 0, tmp.Length); + ulong number = 0; + int ignore = Decode(tmp, tmp.Length, ref number); + + // Get compressed size + dataStream.Seek(-12 - (backwardSize - 2 - ignore), SeekOrigin.End); + tmp = new byte[backwardSize - 2 - ignore]; + dataStream.Read(tmp, 0, tmp.Length); + Decode(tmp, tmp.Length, ref number); + decompressedSize = (long)number; + + dataStream.Seek(0, SeekOrigin.Begin); + } + + int Decode(byte[] buf, int size_max, ref ulong num) + { + if(size_max == 0) + return 0; + + if(size_max > 9) + size_max = 9; + + num = (ulong)(buf[0] & 0x7F); + int i = 0; + + while((buf[i++] & 0x80) == 0x80) + { + if(i >= size_max || buf[i] == 0x00) + return 0; + + num |= (ulong)(buf[i] & 0x7F) << (i * 7); + } + + return i; + } + + public override void Open(byte[] buffer) + { + dataStream = new MemoryStream(buffer); + basePath = null; + creationTime = DateTime.UtcNow; + lastWriteTime = creationTime; + GuessSize(); + innerStream = new ForcedSeekStream(decompressedSize, dataStream); + opened = true; + } + + public override void Open(Stream stream) + { + dataStream = stream; + basePath = null; + creationTime = DateTime.UtcNow; + lastWriteTime = creationTime; + GuessSize(); + innerStream = new ForcedSeekStream(decompressedSize, dataStream); + opened = true; + } + + public override void Open(string path) + { + dataStream = new FileStream(path, FileMode.Open, FileAccess.Read); + basePath = Path.GetFullPath(path); + + DateTime start = DateTime.UtcNow; + DateTime end = DateTime.UtcNow; + + FileInfo fi = new FileInfo(path); + creationTime = fi.CreationTimeUtc; + lastWriteTime = fi.LastWriteTimeUtc; + GuessSize(); + innerStream = new ForcedSeekStream(decompressedSize, dataStream); + opened = true; + } + + public override DateTime GetCreationTime() + { + return creationTime; + } + + public override long GetDataForkLength() + { + return decompressedSize; + } + + public override DateTime GetLastWriteTime() + { + return lastWriteTime; + } + + public override long GetLength() + { + return decompressedSize; + } + + public override long GetResourceForkLength() + { + return 0; + } + + public override string GetFilename() + { + return basePath != null ? Path.GetFileName(basePath) : null; + } + + public override string GetParentFolder() + { + return Path.GetDirectoryName(basePath); + } + + public override bool IsOpened() + { + return opened; } } -} +} \ No newline at end of file diff --git a/README.md b/README.md index 0168d4942..18fc4fe99 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,7 @@ Supported filters * GZip * LZip * MacBinary I, II, III +* XZ Changelog ========= diff --git a/TODO b/TODO index 726a6d501..f45fe6206 100644 --- a/TODO +++ b/TODO @@ -62,5 +62,4 @@ CHD plugin: Filters: --- Add support for 7Z archives ---- Add support for XZ compressed files --- Add support for ZIP archives \ No newline at end of file