Skip to content

Commit

Permalink
Added compression flavor/level selection and improved extration speed
Browse files Browse the repository at this point in the history
New options for compression with addition of flavor/level and store uncompressed.  Extration code has been updated.
  • Loading branch information
THGSCST committed May 16, 2020
1 parent d5b5b07 commit 39257fb
Show file tree
Hide file tree
Showing 10 changed files with 263 additions and 265 deletions.
17 changes: 6 additions & 11 deletions HPIZ Archiver/MainForm.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 10 additions & 6 deletions HPIZ Archiver/MainForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,11 @@ public MainForm()
}
private void MainForm_Load(object sender, EventArgs e)
{
// Disable, Not Implemented yet
compressionLevelComboBox.SelectedIndex = 1;
compressionLevelComboBox.Enabled = false;
compressionLevelComboBox.ComboBox.DataSource = Enum.GetValues(typeof(CompressionFlavor));
compressionLevelComboBox.ComboBox.BindingContext = this.BindingContext;
compressionLevelComboBox.SelectedIndex = 4;
}



private void PopulateList(List<ListViewItem> collection)
{
listViewFiles.Items.Clear();
Expand Down Expand Up @@ -262,6 +260,10 @@ private async void compressCheckedFilesToolStripMenuItem_Click(object sender, Ev
int size = Int32.Parse(item.SubItems[1].Text, NumberStyles.AllowThousands);
chunkTotal += (size / 65536) + (size % 65536 == 0 ? 0 : 1);
}

CompressionFlavor flavor;
Enum.TryParse(compressionLevelComboBox.Text, out flavor);

progressBar.Maximum = chunkTotal + 1;
progressBar.Value = 0;
progressBar.Visible = true;
Expand All @@ -278,16 +280,18 @@ private async void compressCheckedFilesToolStripMenuItem_Click(object sender, Ev
var timer = new Stopwatch();
timer.Start();

await Task.Run(() => HpiFile.CreateFromFileList(fileList, toolStripPathTextBox.Text, dialogSaveHpi.FileName, progress));
await Task.Run(() => HpiFile.CreateFromFileList(fileList, toolStripPathTextBox.Text, dialogSaveHpi.FileName, progress, flavor));

timer.Stop();

firstStatusLabel.Text = String.Format("Done! Elapsed time: {0}h {1}m {2}s {3}ms", timer.Elapsed.Hours, timer.Elapsed.Minutes,
timer.Elapsed.Seconds, timer.Elapsed.Milliseconds);
secondStatusLabel.Text = dialogSaveHpi.FileName;
toolStrip.Enabled = true;
toolStripCompressButton.Enabled = false;
}
}

}

}
175 changes: 82 additions & 93 deletions HPIZ/Chunk.cs
Original file line number Diff line number Diff line change
@@ -1,125 +1,114 @@
using CompressSharper.Zopfli;
using HPIZ.Compression;
using System;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Windows.Forms;

namespace HPIZ
{
public class Chunk
internal static class Chunk
{
private const int Header = 0x48535153; //SQSH (SQUASH)
private const byte DefaultVersion = 2; //Always 2?
public const int SizeOfChunk = 19;

public CompressionMethod FlagCompression;
public bool IsObfuscated;
public int CompressedSize; // the length of the compressed data
public int DecompressedSize; // the length of the decompressed data
public byte[] Data;
public Chunk(byte[] chunkData)
{
BinaryReader hr = new BinaryReader(new MemoryStream(chunkData));
int headerMark = hr.ReadInt32();
if (headerMark != Chunk.Header) throw new InvalidDataException("Invalid Chunk Header");

int version = hr.ReadByte();
if (version != DefaultVersion) throw new NotImplementedException("Unsuported Chunk Version");
public const int MinSize = 19;
public const int MaxSize = 65536;
private const byte NoObfuscation = 0;

FlagCompression = (CompressionMethod)hr.ReadByte();
if (FlagCompression != CompressionMethod.LZ77 && FlagCompression != CompressionMethod.ZLib)
throw new Exception("Unknown compression method in Chunk header");
public static int ObtainDecompressedSize(byte[] chunk)
{
return BitConverter.ToInt32(chunk, 11);
}

IsObfuscated = hr.ReadBoolean();
CompressedSize = hr.ReadInt32();
DecompressedSize = hr.ReadInt32();
internal static byte[] Compress(byte[] bytesToCompress, CompressionFlavor flavor)
{
if (bytesToCompress == null)
throw new InvalidDataException("Cannot compress null array");

if (FlagCompression == CompressionMethod.None && CompressedSize != DecompressedSize)
throw new Exception("Chunk size inconsistent with decompressed and compressed sizes");
if (flavor == CompressionFlavor.StoreUncompressed)
throw new InvalidOperationException("Chunk format cannot be used for uncompressed data");

int checksum = hr.ReadInt32();
MemoryStream output = new MemoryStream(bytesToCompress.Length);
BinaryWriter writer = new BinaryWriter(output);

Data = new byte[CompressedSize];
hr.Read(Data, 0, CompressedSize);
writer.Write(Chunk.Header);
writer.Write(Chunk.DefaultVersion);
writer.Write((byte)CompressionMethod.ZLib);
writer.Write(NoObfuscation);

using (MemoryStream compressedStream = new MemoryStream(bytesToCompress.Length))
{
compressedStream.WriteByte(0x78); //ZLib header first byte
compressedStream.WriteByte(0xDA); //ZLib header second byte

if (ComputeChecksum() != checksum) throw new InvalidDataException("Bad Chunk Checksum");
switch (flavor)
{
case CompressionFlavor.ZLibDeflate:
using (DeflateStream deflateStream = new DeflateStream(compressedStream, CompressionMode.Compress, true))
deflateStream.Write(bytesToCompress, 0, bytesToCompress.Length);
break;
case CompressionFlavor.i5ZopfliDeflate:
case CompressionFlavor.i10ZopfliDeflate:
case CompressionFlavor.i15ZopfliDeflate:
ZopfliDeflater zstream = new ZopfliDeflater(compressedStream);
zstream.NumberOfIterations = (int)flavor;
zstream.MasterBlockSize = 0;
zstream.Deflate(bytesToCompress, true);
break;
default:
throw new InvalidOperationException("Unknow compression flavor");
}
var compressedDataArray = compressedStream.ToArray(); //Change to stream
int checksum = ComputeChecksum(compressedDataArray); //Change to stream

if (IsObfuscated)
for (int j = 0; j < CompressedSize; ++j)
Data[j] = (byte)((Data[j] - j) ^ j);
writer.Write(compressedDataArray.Length);
writer.Write(bytesToCompress.Length);
writer.Write(checksum);
writer.Write(compressedDataArray);
}
return output.ToArray();
}

public Chunk(byte[] data, bool toREMOVE)
internal static byte[] Decompress(MemoryStream bytesToDecompress)
{
Data = data;
FlagCompression = CompressionMethod.None;
IsObfuscated = false;
CompressedSize = data.Length;
DecompressedSize = data.Length;
}
BinaryReader reader = new BinaryReader(bytesToDecompress);
int headerMark = reader.ReadInt32();
if (headerMark != Chunk.Header) throw new InvalidDataException("Invalid Chunk Header");

private int ComputeChecksum()
{
int sum = 0;
for (int i = 0; i < Data.Length; ++i)
sum += Data[i];
return sum;
}
int version = reader.ReadByte();
if (version != DefaultVersion) throw new NotImplementedException("Unsuported Chunk Version");

public void WriteBytes(BinaryWriter writer)
{
writer.Write(Chunk.Header);
writer.Write(Chunk.DefaultVersion);
writer.Write((byte)FlagCompression);
writer.Write(IsObfuscated);
writer.Write(CompressedSize);
writer.Write(DecompressedSize);
writer.Write(ComputeChecksum());
writer.Write(Data);
}
CompressionMethod FlagCompression = (CompressionMethod)reader.ReadByte();
if (FlagCompression != CompressionMethod.LZ77 && FlagCompression != CompressionMethod.ZLib)
throw new InvalidOperationException("Unknown compression method in Chunk header");

public void Compress(bool useZopfli)
{
bool IsObfuscated = reader.ReadBoolean();
int CompressedSize = reader.ReadInt32();
int DecompressedSize = reader.ReadInt32();
int checksum = reader.ReadInt32();

using (MemoryStream ms = new MemoryStream(Data.Length))
{
ms.WriteByte(0x78); //ZLib header first byte
ms.WriteByte(0xDA); //ZLib header second byte
if (useZopfli)
{
ZopfliDeflater zstream = new ZopfliDeflater(ms);
byte[] compressedData = reader.ReadBytes(CompressedSize);

zstream.NumberOfIterations = 10;
zstream.MasterBlockSize = 0;
zstream.Deflate(Data, true);
}
else
using (DeflateStream deflateStream = new DeflateStream(ms, CompressionMode.Compress, true))
deflateStream.Write(Data, 0, Data.Length);

Data = ms.ToArray();
CompressedSize = Data.Length;
FlagCompression = CompressionMethod.ZLib;
}
}
if (ComputeChecksum(compressedData) != checksum) throw new InvalidDataException("Bad Chunk Checksum");

public void Decompress()
{
var outputBuffer = new byte[DecompressedSize];
if (IsObfuscated)
for (int j = 0; j < CompressedSize; ++j)
compressedData[j] = (byte)((compressedData[j] - j) ^ j);

byte[] outputBuffer = new byte[DecompressedSize];
if (FlagCompression == CompressionMethod.LZ77)
{
LZ77.Decompress(Data, outputBuffer);
Data = outputBuffer;
}
LZ77.Decompress(compressedData, outputBuffer);

if (FlagCompression == CompressionMethod.ZLib)
{
ZLibDeflater.Decompress(Data, outputBuffer);
Data = outputBuffer;
}
FlagCompression = CompressionMethod.None;
ZLibDeflater.Decompress(compressedData, outputBuffer);

return outputBuffer;
}

private static int ComputeChecksum(byte[] data)
{
int sum = 0;
for (int i = 0; i < data.Length; ++i)
sum += data[i];
return sum;
}

}
Expand Down
16 changes: 16 additions & 0 deletions HPIZ/Compression/CompressionFlavor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace HPIZ
{
public enum CompressionFlavor
{
StoreUncompressed = 0,
ZLibDeflate = 1,
i5ZopfliDeflate = 5,
i10ZopfliDeflate = 10,
i15ZopfliDeflate = 15

}
}
2 changes: 1 addition & 1 deletion HPIZ/Compression/LZ77.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using System.IO;
using System.Text;

namespace HPIZ.Compression
namespace HPIZ
{
public static class LZ77
{
Expand Down
12 changes: 10 additions & 2 deletions HPIZ/FileEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ public class FileEntry
public CompressionMethod FlagCompression;
public int[] ChunkSizes;


public FileEntry(BinaryReader reader)
{
OffsetOfCompressedData = reader.ReadInt32();
Expand All @@ -22,10 +21,19 @@ public FileEntry()
{
}

public FileEntry(int uncompressedSize, CompressionMethod flagCompression, int[] chunkSizes)
{
UncompressedSize = uncompressedSize;
FlagCompression = flagCompression;
ChunkSizes = chunkSizes;
}

public int CompressedSizeCount()
{
return ChunkSizes.Sum();
if (FlagCompression == CompressionMethod.None)
return UncompressedSize;
else
return ChunkSizes.Sum() + ChunkSizes.Length * 4 + Chunk.MinSize;
}

public float Ratio()
Expand Down
1 change: 1 addition & 0 deletions HPIZ/HPIZ.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)Chunk.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Compression\CompressionFlavor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Compression\CompressionMethod.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Compression\ZLibDeflater.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Compression\LZ77.cs" />
Expand Down
Loading

0 comments on commit 39257fb

Please sign in to comment.