diff --git a/README.MD b/README.MD
index d36a013..4849174 100644
--- a/README.MD
+++ b/README.MD
@@ -8,6 +8,7 @@ Find the link to the Nuget package [here](https://www.nuget.org/packages/SabreTo
| Compression Name | Decompress | Compress |
| --- | --- | --- |
+| Blast | Yes | No |
| LZ | Yes | No |
| MSZIP | Yes* | No |
diff --git a/SabreTools.Compression/Blast/Blast.cs b/SabreTools.Compression/Blast/Blast.cs
new file mode 100644
index 0000000..1c12080
--- /dev/null
+++ b/SabreTools.Compression/Blast/Blast.cs
@@ -0,0 +1,270 @@
+/* blast.c
+ * Copyright (C) 2003, 2012, 2013 Mark Adler
+ * For conditions of distribution and use, see copyright notice in blast.h
+ * version 1.3, 24 Aug 2013
+ *
+ * blast.c decompresses data compressed by the PKWare Compression Library.
+ * This function provides functionality similar to the explode() function of
+ * the PKWare library, hence the name "blast".
+ *
+ * This decompressor is based on the excellent format description provided by
+ * Ben Rudiak-Gould in comp.compression on August 13, 2001. Interestingly, the
+ * example Ben provided in the post is incorrect. The distance 110001 should
+ * instead be 111000. When corrected, the example byte stream becomes:
+ *
+ * 00 04 82 24 25 8f 80 7f
+ *
+ * which decompresses to "AIAIAIAIAIAIA" (without the quotes).
+ */
+
+/*
+ * Change history:
+ *
+ * 1.0 12 Feb 2003 - First version
+ * 1.1 16 Feb 2003 - Fixed distance check for > 4 GB uncompressed data
+ * 1.2 24 Oct 2012 - Add note about using binary mode in stdio
+ * - Fix comparisons of differently signed integers
+ * 1.3 24 Aug 2013 - Return unused input from blast()
+ * - Fix test code to correctly report unused input
+ * - Enable the provision of initial input to blast()
+ */
+
+using System;
+using System.Collections.Generic;
+using static SabreTools.Compression.Blast.Constants;
+
+namespace SabreTools.Compression.Blast
+{
+ public unsafe static class BlastDecoder
+ {
+ #region Huffman Encoding
+
+ ///
+ /// Literal code
+ ///
+ private static readonly Huffman litcode = new(MAXBITS + 1, 256);
+
+ ///
+ /// Length code
+ ///
+ private static readonly Huffman lencode = new(MAXBITS + 1, 16);
+
+ ///
+ /// Distance code
+ ///
+ private static readonly Huffman distcode = new(MAXBITS + 1, 64);
+
+ ///
+ /// Base for length codes
+ ///
+ private static readonly short[] baseLength =
+ [
+ 3, 2, 4, 5, 6, 7, 8, 9, 10, 12, 16, 24, 40, 72, 136, 264
+ ];
+
+ ///
+ /// Extra bits for length codes
+ ///
+ private static readonly byte[] extra =
+ [
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8
+ ];
+
+ #endregion
+
+ ///
+ /// Static constructor
+ ///
+ static BlastDecoder()
+ {
+ // Repeated code lengths of literal codes
+ byte[] litlen =
+ [
+ 11, 124, 8, 7, 28, 7, 188, 13, 76, 4, 10, 8, 12, 10, 12, 10, 8, 23, 8,
+ 9, 7, 6, 7, 8, 7, 6, 55, 8, 23, 24, 12, 11, 7, 9, 11, 12, 6, 7, 22, 5,
+ 7, 24, 6, 11, 9, 6, 7, 22, 7, 11, 38, 7, 9, 8, 25, 11, 8, 11, 9, 12,
+ 8, 12, 5, 38, 5, 38, 5, 11, 7, 5, 6, 21, 6, 10, 53, 8, 7, 24, 10, 27,
+ 44, 253, 253, 253, 252, 252, 252, 13, 12, 45, 12, 45, 12, 61, 12, 45,
+ 44, 173
+ ];
+ litcode.Initialize(litlen);
+
+ // Repeated code lengths of length codes 0..15
+ byte[] lenlen =
+ [
+ 2, 35, 36, 53, 38, 23
+ ];
+ lencode.Initialize(lenlen);
+
+ // Repeated code lengths of distance codes 0..63
+ byte[] distlen =
+ [
+ 2, 20, 53, 230, 247, 151, 248
+ ];
+ distcode.Initialize(distlen);
+ }
+
+ ///
+ /// blast() decompresses the PKWare Data Compression Library (DCL) compressed
+ /// format. It provides the same functionality as the explode() function in
+ /// that library. (Note: PKWare overused the "implode" verb, and the format
+ /// used by their library implode() function is completely different and
+ /// incompatible with the implode compression method supported by PKZIP.)
+ ///
+ /// The binary mode for stdio functions should be used to assure that the
+ /// compressed data is not corrupted when read or written. For example:
+ /// fopen(..., "rb") and fopen(..., "wb").
+ ///
+ public static int Blast(byte[] inhow, List outhow)
+ {
+ // Input/output state
+ var state = new State(inhow, outhow);
+
+ // Attempt to decompress using the above state
+ int err;
+ try
+ {
+ err = Decomp(state);
+ }
+ catch (IndexOutOfRangeException)
+ {
+ // This was originally a jump, which is bad form for C#
+ err = 2;
+ }
+
+ // Write any leftover output and update the error code if needed
+ if (err != 1 && state.Next != 0 && !state.ProcessOutput() && err == 0)
+ err = 1;
+
+ return err;
+ }
+
+ ///
+ /// Decode PKWare Compression Library stream.
+ ///
+ ///
+ /// First byte is 0 if literals are uncoded or 1 if they are coded. Second
+ /// byte is 4, 5, or 6 for the number of extra bits in the distance code.
+ /// This is the base-2 logarithm of the dictionary size minus six.
+ ///
+ /// Compressed data is a combination of literals and length/distance pairs
+ /// terminated by an end code. Literals are either Huffman coded or
+ /// uncoded bytes. A length/distance pair is a coded length followed by a
+ /// coded distance to represent a string that occurs earlier in the
+ /// uncompressed data that occurs again at the current location.
+ ///
+ /// A bit preceding a literal or length/distance pair indicates which comes
+ /// next, 0 for literals, 1 for length/distance.
+ ///
+ /// If literals are uncoded, then the next eight bits are the literal, in the
+ /// normal bit order in the stream, i.e. no bit-reversal is needed. Similarly,
+ /// no bit reversal is needed for either the length extra bits or the distance
+ /// extra bits.
+ ///
+ /// Literal bytes are simply written to the output. A length/distance pair is
+ /// an instruction to copy previously uncompressed bytes to the output. The
+ /// copy is from distance bytes back in the output stream, copying for length
+ /// bytes.
+ ///
+ /// Distances pointing before the beginning of the output data are not
+ /// permitted.
+ ///
+ /// Overlapped copies, where the length is greater than the distance, are
+ /// allowed and common. For example, a distance of one and a length of 518
+ /// simply copies the last byte 518 times. A distance of four and a length of
+ /// twelve copies the last four bytes three times. A simple forward copy
+ /// ignoring whether the length is greater than the distance or not implements
+ /// this correctly.
+ ///
+ private static int Decomp(State state)
+ {
+ int symbol; // decoded symbol, extra bits for distance
+ int len; // length for copy
+ uint dist; // distance for copy
+ int copy; // copy counter
+ int from, to; // copy pointers
+
+ // Read header
+ int lit = state.Bits(8); // true if literals are coded
+ if (lit > 1)
+ return -1;
+
+ int dict = state.Bits(8); // log2(dictionary size) - 6
+ if (dict < 4 || dict > 6)
+ return -2;
+
+ // Decode literals and length/distance pairs
+ do
+ {
+ if (state.Bits(1) != 0)
+ {
+ // Get length
+ symbol = lencode.Decode(state);
+ len = baseLength[symbol] + state.Bits(extra[symbol]);
+ if (len == 519)
+ break; // end code
+
+ // Get distance
+ symbol = len == 2 ? 2 : dict;
+ dist = (uint)(distcode.Decode(state) << symbol);
+ dist += (uint)state.Bits(symbol);
+ dist++;
+ if (state.First && dist > state.Next)
+ return -3; //distance too far back
+
+ // Copy length bytes from distance bytes back
+ do
+ {
+ to = (int)(state.OutputPtr + state.Next);
+ from = (int)(to - dist);
+ copy = MAXWIN;
+ if (state.Next < dist)
+ {
+ from += copy;
+ copy = (int)dist;
+ }
+
+ copy -= (int)state.Next;
+ if (copy > len)
+ copy = len;
+
+ len -= copy;
+ state.Next += (uint)copy;
+ do
+ {
+ state.Output[to++] = state.Output[from++];
+ }
+ while (--copy != 0);
+
+ if (state.Next == MAXWIN)
+ {
+ if (!state.ProcessOutput())
+ return 1;
+
+ state.Next = 0;
+ state.First = false;
+ }
+ }
+ while (len != 0);
+ }
+ else
+ {
+ // Get literal and write it
+ symbol = lit != 0 ? litcode.Decode(state) : state.Bits(8);
+ state.Output[state.Next++] = (byte)symbol;
+ if (state.Next == MAXWIN)
+ {
+ if (!state.ProcessOutput())
+ return 1;
+
+ state.Next = 0;
+ state.First = false;
+ }
+ }
+ }
+ while (true);
+
+ return 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SabreTools.Compression/Blast/Constants.cs b/SabreTools.Compression/Blast/Constants.cs
new file mode 100644
index 0000000..2364153
--- /dev/null
+++ b/SabreTools.Compression/Blast/Constants.cs
@@ -0,0 +1,15 @@
+namespace SabreTools.Compression.Blast
+{
+ public static class Constants
+ {
+ ///
+ /// Maximum code length
+ ///
+ public const int MAXBITS = 13;
+
+ ///
+ /// Maximum window size
+ ///
+ public const int MAXWIN = 4096;
+ }
+}
\ No newline at end of file
diff --git a/SabreTools.Compression/Blast/Huffman.cs b/SabreTools.Compression/Blast/Huffman.cs
new file mode 100644
index 0000000..70dd3a6
--- /dev/null
+++ b/SabreTools.Compression/Blast/Huffman.cs
@@ -0,0 +1,207 @@
+using System;
+using static SabreTools.Compression.Blast.Constants;
+
+namespace SabreTools.Compression.Blast
+{
+ ///
+ /// Huffman code decoding tables. count[1..MAXBITS] is the number of symbols of
+ /// each length, which for a canonical code are stepped through in order.
+ /// symbol[] are the symbol values in canonical order, where the number of
+ /// entries is the sum of the counts in count[]. The decoding process can be
+ /// seen in the function decode() below.
+ ///
+ public class Huffman
+ {
+ ///
+ /// Number of symbols of each length
+ ///
+ public short[] Count { get; set; }
+
+ ///
+ /// Pointer to number of symbols of each length
+ ///
+ public int CountPtr { get; set; }
+
+ ///
+ /// Canonically ordered symbols
+ ///
+ public short[] Symbol { get; set; }
+
+ ///
+ /// Constructor
+ ///
+ /// Length of the Count array
+ /// Length of the Symbol array
+ public Huffman(int countLength, int symbolLength)
+ {
+ Count = new short[countLength];
+ Symbol = new short[symbolLength];
+ }
+
+ ///
+ /// Given a list of repeated code lengths rep[0..n-1], where each byte is a
+ /// count (high four bits + 1) and a code length (low four bits), generate the
+ /// list of code lengths. This compaction reduces the size of the object code.
+ /// Then given the list of code lengths length[0..n-1] representing a canonical
+ /// Huffman code for n symbols, construct the tables required to decode those
+ /// codes. Those tables are the number of codes of each length, and the symbols
+ /// sorted by length, retaining their original order within each length. The
+ /// return value is zero for a complete code set, negative for an over-
+ /// subscribed code set, and positive for an incomplete code set. The tables
+ /// can be used if the return value is zero or positive, but they cannot be used
+ /// if the return value is negative. If the return value is zero, it is not
+ /// possible for decode() using that table to return an error--any stream of
+ /// enough bits will resolve to a symbol. If the return value is positive, then
+ /// it is possible for decode() using that table to return an error for received
+ /// codes past the end of the incomplete lengths.
+ ///
+ /// Repeated code length array
+ public int Initialize(byte[] rep)
+ {
+ int n = rep.Length; // Length of the bit length array
+ short symbol = 0; // Current symbol when stepping through length[]
+ short len; // Current length when stepping through h.Count[]
+ int left; // Number of possible codes left of current length
+ short[] offs = new short[MAXBITS + 1]; // offsets in symbol table for each length
+ short[] length = new short[256]; // Code lengths
+
+ // Convert compact repeat counts into symbol bit length list
+ int repPtr = 0;
+ do
+ {
+ len = rep[repPtr++];
+ left = (len >> 4) + 1;
+ len &= 15;
+ do
+ {
+ length[symbol++] = len;
+ }
+ while (--left != 0);
+ }
+ while (--n != 0);
+
+ n = symbol;
+
+ // Count number of codes of each length
+ for (len = 0; len <= MAXBITS; len++)
+ {
+ Count[len] = 0;
+ }
+
+ // Assumes lengths are within bounds
+ for (symbol = 0; symbol < n; symbol++)
+ {
+ (Count[length[symbol]])++;
+ }
+
+ // No codes! Complete, but decode() will fail
+ if (Count[0] == n)
+ return 0;
+
+ // Check for an over-subscribed or incomplete set of lengths
+ left = 1; // One possible code of zero length
+ for (len = 1; len <= MAXBITS; len++)
+ {
+ left <<= 1; // One more bit, double codes left
+ left -= Count[len]; // Deduct count from possible codes
+ if (left < 0)
+ return left; // over-subscribed--return negative
+ }
+
+ // Generate offsets into symbol table for each length for sorting
+ offs[1] = 0;
+ for (len = 1; len < MAXBITS; len++)
+ {
+ offs[len + 1] = (short)(offs[len] + Count[len]);
+ }
+
+ // Put symbols in table sorted by length, by symbol order within each length
+ for (symbol = 0; symbol < n; symbol++)
+ {
+ if (length[symbol] != 0)
+ Symbol[offs[length[symbol]]++] = symbol;
+ }
+
+ // Return zero for complete set, positive for incomplete set
+ return left;
+ }
+
+ ///
+ /// Decode a code from the stream s using huffman table h. Return the symbol or
+ /// a negative value if there is an error. If all of the lengths are zero, i.e.
+ /// an empty code, or if the code is incomplete and an invalid code is received,
+ /// then -9 is returned after reading MAXBITS bits.
+ ///
+ /// Current input/output state to process
+ ///
+ /// The codes as stored in the compressed data are bit-reversed relative to
+ /// a simple integer ordering of codes of the same lengths. Hence below the
+ /// bits are pulled from the compressed data one at a time and used to
+ /// build the code value reversed from what is in the stream in order to
+ /// permit simple integer comparisons for decoding.
+ ///
+ /// The first code for the shortest length is all ones. Subsequent codes of
+ /// the same length are simply integer decrements of the previous code. When
+ /// moving up a length, a one bit is appended to the code. For a complete
+ /// code, the last code of the longest length will be all zeros. To support
+ /// this ordering, the bits pulled during decoding are inverted to apply the
+ /// more "natural" ordering starting with all zeros and incrementing.
+ ///
+ public int Decode(State state)
+ {
+ int len = 1; // Current number of bits in code
+ int code = 0; // len bits being decoded
+ int first = 0; // First code of length len
+ int count; // Number of codes of length len
+ int index = 0; // Index of first code of length len in symbol table
+ int bitbuf = state.BitBuf; // Bits from stream
+ int left = state.BitCnt; // Bits left in next or left to process
+ int nextPtr = CountPtr + 1; // Next number of codes
+
+ while (true)
+ {
+ while (left-- != 0)
+ {
+ // Invert code
+ code |= (bitbuf & 1) ^ 1;
+ bitbuf >>= 1;
+ count = Count[nextPtr++];
+
+ // If length len, return symbol
+ if (code < first + count)
+ {
+ state.BitBuf = bitbuf;
+ state.BitCnt = (state.BitCnt - len) & 7;
+ return Symbol[index + (code - first)];
+ }
+
+ // Else update for next length
+ index += count;
+ first += count;
+ first <<= 1;
+ code <<= 1;
+ len++;
+ }
+
+ left = (MAXBITS + 1) - len;
+ if (left == 0)
+ break;
+
+ if (state.Left == 0)
+ {
+ state.Left = state.ProcessInput();
+ if (state.Left == 0)
+ throw new IndexOutOfRangeException();
+ }
+
+ bitbuf = state.Input[state.InputPtr++];
+ state.Left--;
+ if (left > 8)
+ left = 8;
+ }
+
+ // Ran out of codes
+ return -9;
+ }
+ };
+}
\ No newline at end of file
diff --git a/SabreTools.Compression/Blast/State.cs b/SabreTools.Compression/Blast/State.cs
new file mode 100644
index 0000000..b946c7a
--- /dev/null
+++ b/SabreTools.Compression/Blast/State.cs
@@ -0,0 +1,160 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using static SabreTools.Compression.Blast.Constants;
+
+namespace SabreTools.Compression.Blast
+{
+ ///
+ /// Input and output state
+ ///
+ public class State
+ {
+ #region Input State
+
+ ///
+ /// Opaque information passed to InputFunction()
+ ///
+ public byte[] InHow { get; set; }
+
+ ///
+ /// Next input location
+ ///
+ public List Input { get; set; }
+
+ ///
+ /// Pointer to the next input location
+ ///
+ public int InputPtr { get; set; }
+
+ ///
+ /// Available input at in
+ ///
+ public uint Left { get; set; }
+
+ ///
+ /// Bit buffer
+ ///
+ public int BitBuf { get; set; }
+
+ ///
+ /// Number of bits in bit buffer
+ ///
+ public int BitCnt { get; set; }
+
+ #endregion
+
+ #region Output State
+
+ ///
+ /// Opaque information passed to OutputFunction()
+ ///
+ public List OutHow { get; set; }
+
+ ///
+ /// Index of next write location in out[]
+ ///
+ public uint Next { get; set; }
+
+ ///
+ /// True to check distances (for first 4K)
+ ///
+ public bool First { get; set; }
+
+ ///
+ /// Output buffer and sliding window
+ ///
+ public byte[] Output { get; set; } = new byte[MAXWIN];
+
+ ///
+ /// Pointer to the next output location
+ ///
+ public int OutputPtr { get; set; }
+
+ #endregion
+
+ ///
+ /// Constructor
+ ///
+ /// Input byte array
+ /// Output byte list
+ public State(byte[] inhow, List outhow)
+ {
+ InHow = inhow;
+ Input = new List();
+ InputPtr = 0;
+ Left = 0;
+ BitBuf = 0;
+ BitCnt = 0;
+
+ OutHow = outhow;
+ Next = 0;
+ First = true;
+ }
+
+ ///
+ /// Return need bits from the input stream. This always leaves less than
+ /// eight bits in the buffer. bits() works properly for need == 0.
+ ///
+ /// Number of bits to read
+ ///
+ /// Bits are stored in bytes from the least significant bit to the most
+ /// significant bit. Therefore bits are dropped from the bottom of the bit
+ /// buffer, using shift right, and new bytes are appended to the top of the
+ /// bit buffer, using shift left.
+ ///
+ public int Bits(int need)
+ {
+ // Load at least need bits into val
+ int val = BitBuf;
+ while (BitCnt < need)
+ {
+ if (Left == 0)
+ {
+ Left = ProcessInput();
+ if (Left == 0)
+ throw new IndexOutOfRangeException();
+ }
+
+ // Load eight bits
+ val |= (int)(Input[InputPtr++]) << BitCnt;
+ Left--;
+ BitCnt += 8;
+ }
+
+ // Drop need bits and update buffer, always zero to seven bits left
+ BitBuf = val >> need;
+ BitCnt -= need;
+
+ // Return need bits, zeroing the bits above that
+ return val & ((1 << need) - 1);
+ }
+
+ ///
+ /// Process input for the current state
+ ///
+ /// Amount of data in Input
+ public uint ProcessInput()
+ {
+ Input = new List(InHow);
+ return (uint)Input.Count;
+ }
+
+ ///
+ /// Process output for the current state
+ ///
+ /// True if the output could be added, false otherwise
+ public bool ProcessOutput()
+ {
+ try
+ {
+ OutHow.AddRange(Output.Take((int)Next));
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+ }
+}
\ No newline at end of file