diff --git a/DiscImageChef.Filters/DiscImageChef.Filters.csproj b/DiscImageChef.Filters/DiscImageChef.Filters.csproj
index 242472c1..40f8a67d 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 3e9cf7fe..fdc447e0 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 0168d494..18fc4fe9 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 726a6d50..f45fe620 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