diff --git a/.idea/.idea.DiscImageChef/.idea/contentModel.xml b/.idea/.idea.DiscImageChef/.idea/contentModel.xml
index 916f19d25..994d95339 100644
--- a/.idea/.idea.DiscImageChef/.idea/contentModel.xml
+++ b/.idea/.idea.DiscImageChef/.idea/contentModel.xml
@@ -84,6 +84,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/DiscImageChef.Compression/DiscImageChef.Compression.csproj b/DiscImageChef.Compression/DiscImageChef.Compression.csproj
new file mode 100644
index 000000000..a47608021
--- /dev/null
+++ b/DiscImageChef.Compression/DiscImageChef.Compression.csproj
@@ -0,0 +1,57 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {858398D1-7321-4763-8BAB-56BBFEC74E29}
+ Library
+ Properties
+ DiscImageChef.Compression
+ DiscImageChef.Compression
+ v4.0
+ 512
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+ LICENSE.LGPL
+
+
+
+
+
\ No newline at end of file
diff --git a/DiscImageChef.Compression/Properties/AssemblyInfo.cs b/DiscImageChef.Compression/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..0290dd1d4
--- /dev/null
+++ b/DiscImageChef.Compression/Properties/AssemblyInfo.cs
@@ -0,0 +1,66 @@
+// /***************************************************************************
+// The Disc Image Chef
+// ----------------------------------------------------------------------------
+//
+// Filename : AssemblyInfo.cs
+// Author(s) : Natalia Portillo
+//
+// Component : Compression algorithms.
+//
+// --[ Description ] ----------------------------------------------------------
+//
+// C# assembly definitions.
+//
+// --[ License ] --------------------------------------------------------------
+//
+// This library is free software; you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 2.1 of the
+// License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see .
+//
+// ----------------------------------------------------------------------------
+// Copyright © 2011-2018 Natalia Portillo
+// ****************************************************************************/
+
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("DiscImageChef.Compression")]
+[assembly: AssemblyDescription("The Disc Image Chef")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Claunia.com")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("Copyright © 2011-2018 Natalia Portillo")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("858398D1-7321-4763-8BAB-56BBFEC74E29")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("4.0.99.0")]
diff --git a/DiscImageChef.Compression/TeleDiskLzh.cs b/DiscImageChef.Compression/TeleDiskLzh.cs
new file mode 100644
index 000000000..c77bc3e40
--- /dev/null
+++ b/DiscImageChef.Compression/TeleDiskLzh.cs
@@ -0,0 +1,419 @@
+// /***************************************************************************
+// The Disc Image Chef
+// ----------------------------------------------------------------------------
+//
+// Filename : TeleDiskLzh.cs
+// Author(s) : Natalia Portillo
+//
+// Component : Compression algorithms.
+//
+// --[ Description ] ----------------------------------------------------------
+//
+// Decompress TeleDisk variant of LZH.
+//
+// --[ License ] --------------------------------------------------------------
+//
+// Redistribution and use in source and binary forms, with or without modification, are permitted provided that
+// the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
+// following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
+// following disclaimer in the documentation and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
+// products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+// ----------------------------------------------------------------------------
+// Copyright © 2011-2018 Natalia Portillo
+// Copyright © 2017 Miodrag Milanovic
+// Copyright © 1988 Haruhiko OKUMURA
+// Copyright © 1988 Haruyasu YOSHIZAKI
+// Copyright © 1988 Kenji RIKITAKE
+// ****************************************************************************/
+
+using System;
+using System.IO;
+
+namespace DiscImageChef.Compression
+{
+ /*
+ * Based on Japanese version 29-NOV-1988
+ * LZSS coded by Haruhiko OKUMURA
+ * Adaptive Huffman Coding coded by Haruyasu YOSHIZAKI
+ * Edited and translated to English by Kenji RIKITAKE
+ */
+ public class TeleDiskLzh
+ {
+ const int BUFSZ = 512;
+
+ /* LZSS Parameters */
+
+ const int N = 4096; /* Size of string buffer */
+ const int F = 60; /* Size of look-ahead buffer */
+ const int THRESHOLD = 2;
+ const int NIL = N; /* End of tree's node */
+
+ /* Huffman coding parameters */
+
+ const int N_CHAR = 256 - THRESHOLD + F;
+ /* character code (= 0..N_CHAR-1) */
+ const int T = N_CHAR * 2 - 1; /* Size of table */
+ const int ROOT = T - 1; /* root position */
+ const int MAX_FREQ = 0x8000;
+
+ /*
+ * Tables for encoding/decoding upper 6 bits of
+ * sliding dictionary pointer
+ */
+
+ /* decoder table */
+ readonly byte[] d_code =
+ {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F, 0x10, 0x10,
+ 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14,
+ 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x19, 0x19, 0x1A, 0x1A,
+ 0x1B, 0x1B, 0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, 0x1F, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23,
+ 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B, 0x2C, 0x2C,
+ 0x2D, 0x2D, 0x2E, 0x2E, 0x2F, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,
+ 0x3C, 0x3D, 0x3E, 0x3F
+ };
+
+ readonly byte[] d_len =
+ {
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08
+ };
+
+ readonly Stream inStream;
+ ushort[] freq = new ushort[T + 1]; /* cumulative freq table */
+
+ ushort getbuf;
+ byte getlen;
+
+ /*
+ * pointing parent nodes.
+ * area [T..(T + N_CHAR - 1)] are pointers for leaves
+ */
+ short[] prnt = new short[T + N_CHAR];
+
+ /* pointing children nodes (son[], son[] + 1)*/
+ short[] son = new short[T];
+
+ Tdlzhuf tdctl;
+ byte[] text_buf = new byte[N + F - 1];
+
+ public TeleDiskLzh(Stream dataStream)
+ {
+ int i;
+ getbuf = 0;
+ getlen = 0;
+ tdctl = new Tdlzhuf();
+ tdctl.Ibufcnt = tdctl.Ibufndx = 0; // input buffer is empty
+ tdctl.Bufcnt = 0;
+ StartHuff();
+ for(i = 0; i < N - F; i++) text_buf[i] = 0x20;
+
+ tdctl.R = N - F;
+ inStream = dataStream;
+ }
+
+ /* DeCompression
+
+ split out initialization code to init_Decode()
+
+ */
+
+ public int Decode(out byte[] buf, int len) /* Decoding/Uncompressing */
+ {
+ short c;
+ buf = new byte[len];
+ int count; // was an unsigned long, seems unnecessary
+ for(count = 0; count < len;)
+ if(tdctl.Bufcnt == 0)
+ {
+ if((c = DecodeChar()) < 0) return count; // fatal error
+
+ if(c < 256)
+ {
+ buf[count] = (byte)c;
+ text_buf[tdctl.R++] = (byte)c;
+ tdctl.R &= N - 1;
+ count++;
+ }
+ else
+ {
+ short pos;
+ if((pos = DecodePosition()) < 0) return count; // fatal error
+
+ tdctl.Bufpos = (ushort)((tdctl.R - pos - 1) & (N - 1));
+ tdctl.Bufcnt = (ushort)(c - 255 + THRESHOLD);
+ tdctl.Bufndx = 0;
+ }
+ }
+ else
+ {
+ // still chars from last string
+ while(tdctl.Bufndx < tdctl.Bufcnt && count < len)
+ {
+ c = text_buf[(tdctl.Bufpos + tdctl.Bufndx) & (N - 1)];
+ buf[count] = (byte)c;
+ tdctl.Bufndx++;
+ text_buf[tdctl.R++] = (byte)c;
+ tdctl.R &= N - 1;
+ count++;
+ }
+
+ // reset bufcnt after copy string from text_buf[]
+ if(tdctl.Bufndx >= tdctl.Bufcnt) tdctl.Bufndx = tdctl.Bufcnt = 0;
+ }
+
+ return count; // count == len, success
+ }
+
+ long DataRead(out byte[] buf, long size)
+ {
+ if(size > inStream.Length - inStream.Position) size = inStream.Length - inStream.Position;
+
+ buf = new byte[size];
+ inStream.Read(buf, 0, (int)size);
+ return size;
+ }
+
+ int NextWord()
+ {
+ if(tdctl.Ibufndx >= tdctl.Ibufcnt)
+ {
+ tdctl.Ibufndx = 0;
+ tdctl.Ibufcnt = (ushort)DataRead(out tdctl.Inbuf, BUFSZ);
+ if(tdctl.Ibufcnt <= 0) return -1;
+ }
+
+ while(getlen <= 8)
+ {
+ // typically reads a word at a time
+ getbuf |= (ushort)(tdctl.Inbuf[tdctl.Ibufndx++] << (8 - getlen));
+ getlen += 8;
+ }
+
+ return 0;
+ }
+
+ int GetBit() /* get one bit */
+ {
+ if(NextWord() < 0) return -1;
+
+ short i = (short)getbuf;
+ getbuf <<= 1;
+ getlen--;
+ return i < 0 ? 1 : 0;
+ }
+
+ int GetByte() /* get a byte */
+ {
+ if(NextWord() != 0) return -1;
+
+ ushort i = getbuf;
+ getbuf <<= 8;
+ getlen -= 8;
+ i = (ushort)(i >> 8);
+ return i;
+ }
+
+ /* initialize freq tree */
+
+ void StartHuff()
+ {
+ int i;
+
+ for(i = 0; i < N_CHAR; i++)
+ {
+ freq[i] = 1;
+ son[i] = (short)(i + T);
+ prnt[i + T] = (short)i;
+ }
+
+ i = 0;
+ int j = N_CHAR;
+ while(j <= ROOT)
+ {
+ freq[j] = (ushort)(freq[i] + freq[i + 1]);
+ son[j] = (short)i;
+ prnt[i] = prnt[i + 1] = (short)j;
+ i += 2;
+ j++;
+ }
+
+ freq[T] = 0xffff;
+ prnt[ROOT] = 0;
+ }
+
+ /* reconstruct freq tree */
+
+ void Reconst()
+ {
+ short i, k;
+
+ /* halven cumulative freq for leaf nodes */
+ short j = 0;
+ for(i = 0; i < T; i++)
+ if(son[i] >= T)
+ {
+ freq[j] = (ushort)((freq[i] + 1) / 2);
+ son[j] = son[i];
+ j++;
+ }
+
+ /* make a tree : first, connect children nodes */
+ for(i = 0, j = N_CHAR; j < T; i += 2, j++)
+ {
+ k = (short)(i + 1);
+ ushort f = freq[j] = (ushort)(freq[i] + freq[k]);
+ for(k = (short)(j - 1); f < freq[k]; k--) { }
+
+ k++;
+ ushort l = (ushort)((j - k) * 2);
+
+ Array.ConstrainedCopy(freq, k, freq, k + 1, l);
+ freq[k] = f;
+ Array.ConstrainedCopy(son, k, son, k + 1, l);
+ son[k] = i;
+ }
+
+ /* connect parent nodes */
+ for(i = 0; i < T; i++)
+ if((k = son[i]) >= T)
+ prnt[k] = i;
+ else
+ prnt[k] = prnt[k + 1] = i;
+ }
+
+ /* update freq tree */
+
+ void Update(int c)
+ {
+ if(freq[ROOT] == MAX_FREQ) Reconst();
+ c = prnt[c + T];
+ do
+ {
+ int k = ++freq[c];
+
+ /* swap nodes to keep the tree freq-ordered */
+ int l;
+ if(k <= freq[l = c + 1]) continue;
+
+ while(k > freq[++l]) { }
+
+ l--;
+ freq[c] = freq[l];
+ freq[l] = (ushort)k;
+
+ int i = son[c];
+ prnt[i] = (short)l;
+ if(i < T) prnt[i + 1] = (short)l;
+
+ int j = son[l];
+ son[l] = (short)i;
+
+ prnt[j] = (short)c;
+ if(j < T) prnt[j + 1] = (short)c;
+ son[c] = (short)j;
+
+ c = l;
+ }
+ while((c = prnt[c]) != 0); /* do it until reaching the root */
+ }
+
+ short DecodeChar()
+ {
+ ushort c = (ushort)son[ROOT];
+
+ /*
+ * start searching tree from the root to leaves.
+ * choose node #(son[]) if input bit == 0
+ * else choose #(son[]+1) (input bit == 1)
+ */
+ while(c < T)
+ {
+ int ret;
+ if((ret = GetBit()) < 0) return -1;
+
+ c += (ushort)ret;
+ c = (ushort)son[c];
+ }
+
+ c -= T;
+ Update(c);
+ return (short)c;
+ }
+
+ short DecodePosition()
+ {
+ short bit;
+ ushort j;
+
+ /* decode upper 6 bits from given table */
+ if((bit = (short)GetByte()) < 0) return -1;
+
+ ushort i = (ushort)bit;
+ ushort c = (ushort)(d_code[i] << 6);
+ j = d_len[i];
+
+ /* input lower 6 bits directly */
+ j -= 2;
+ while(j-- > 0)
+ {
+ if((bit = (short)GetBit()) < 0) return -1;
+
+ i = (ushort)((i << 1) + bit);
+ }
+
+ return (short)(c | (i & 0x3f));
+ }
+ /* update when cumulative frequency */
+ /* reaches to this value */
+
+ struct Tdlzhuf
+ {
+ public ushort R,
+ Bufcnt,
+ Bufndx,
+ Bufpos, // string buffer
+ // the following to allow block reads from input in next_word()
+ Ibufcnt,
+ Ibufndx; // input buffer counters
+ public byte[] Inbuf; // input buffer
+ }
+ }
+}
\ No newline at end of file
diff --git a/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj b/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj
index 9ac2cd2f1..7f7d70fd2 100644
--- a/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj
+++ b/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj
@@ -121,6 +121,10 @@
{CC48B324-A532-4A45-87A6-6F91F7141E8D}
DiscImageChef.Checksums
+
+ {858398d1-7321-4763-8bab-56bbfec74e29}
+ DiscImageChef.Compression
+
{F8BDF57B-1571-4CD0-84B3-B422088D359A}
DiscImageChef.Helpers
diff --git a/DiscImageChef.DiscImages/TeleDisk.cs b/DiscImageChef.DiscImages/TeleDisk.cs
index ffd1f5126..7289edcb6 100644
--- a/DiscImageChef.DiscImages/TeleDisk.cs
+++ b/DiscImageChef.DiscImages/TeleDisk.cs
@@ -28,11 +28,6 @@
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2018 Natalia Portillo
-// For Advanced Compression support (aka LZH):
-// Copyright © 2017 Miodrag Milanovic
-// Copyright © 1988 Haruhiko OKUMURA
-// Copyright © 1988 Haruyasu YOSHIZAKI
-// Copyright © 1988 Kenji RIKITAKE
// ****************************************************************************/
using System;
@@ -40,6 +35,7 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
using DiscImageChef.CommonTypes;
+using DiscImageChef.Compression;
using DiscImageChef.Console;
using DiscImageChef.Filters;
@@ -122,6 +118,8 @@ namespace DiscImageChef.DiscImages
// LBA, data
uint totalDiskSize;
+ const int BUFSZ = 512;
+
public TeleDisk()
{
imageInfo = new ImageInfo
@@ -291,8 +289,8 @@ namespace DiscImageChef.DiscImages
int rd;
inStream.Seek(12, SeekOrigin.Begin);
stream.Seek(12, SeekOrigin.Begin);
- init_Decode();
- do if((rd = Decode(out byte[] obuf, BUFSZ)) > 0) stream.Write(obuf, 0, rd);
+ TeleDiskLzh lzh = new TeleDiskLzh(inStream);
+ do if((rd = lzh.Decode(out byte[] obuf, BUFSZ)) > 0) stream.Write(obuf, 0, rd);
while(rd == BUFSZ);
}
else
@@ -1139,393 +1137,5 @@ namespace DiscImageChef.DiscImages
/// Encoding used for data block
public byte DataEncoding;
}
-
- #region LZH decompression from MAME
- /* This region is under following license:
- * Copyright © 2017 Miodrag Milanovic
- * Adapted to C#, Copyright © 2017 Natalia Portillo
- *
- * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
- /*
- * Based on Japanese version 29-NOV-1988
- * LZSS coded by Haruhiko OKUMURA
- * Adaptive Huffman Coding coded by Haruyasu YOSHIZAKI
- * Edited and translated to English by Kenji RIKITAKE
- */
-
- const int BUFSZ = 512;
-
- /* LZSS Parameters */
-
- const int N = 4096; /* Size of string buffer */
- const int F = 60; /* Size of look-ahead buffer */
- const int THRESHOLD = 2;
- const int NIL = N; /* End of tree's node */
-
- /* Huffman coding parameters */
-
- const int N_CHAR = 256 - THRESHOLD + F;
- /* character code (= 0..N_CHAR-1) */
- const int T = N_CHAR * 2 - 1; /* Size of table */
- const int ROOT = T - 1; /* root position */
- const int MAX_FREQ = 0x8000;
- /* update when cumulative frequency */
- /* reaches to this value */
-
- struct Tdlzhuf
- {
- public ushort R,
- Bufcnt,
- Bufndx,
- Bufpos, // string buffer
- // the following to allow block reads from input in next_word()
- Ibufcnt,
- Ibufndx; // input buffer counters
- public byte[] Inbuf; // input buffer
- }
-
- Tdlzhuf tdctl;
- byte[] text_buf = new byte[N + F - 1];
- ushort[] freq = new ushort[T + 1]; /* cumulative freq table */
-
- /*
- * pointing parent nodes.
- * area [T..(T + N_CHAR - 1)] are pointers for leaves
- */
- short[] prnt = new short[T + N_CHAR];
-
- /* pointing children nodes (son[], son[] + 1)*/
- short[] son = new short[T];
-
- ushort getbuf;
- byte getlen;
-
- long data_read(out byte[] buf, long size)
- {
- if(size > inStream.Length - inStream.Position) size = inStream.Length - inStream.Position;
-
- buf = new byte[size];
- inStream.Read(buf, 0, (int)size);
- return size;
- }
-
- /*
- * Tables for encoding/decoding upper 6 bits of
- * sliding dictionary pointer
- */
-
- /* decoder table */
- readonly byte[] d_code =
- {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
- 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F, 0x10, 0x10,
- 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14,
- 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x19, 0x19, 0x1A, 0x1A,
- 0x1B, 0x1B, 0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, 0x1F, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23,
- 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B, 0x2C, 0x2C,
- 0x2D, 0x2D, 0x2E, 0x2E, 0x2F, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,
- 0x3C, 0x3D, 0x3E, 0x3F
- };
-
- readonly byte[] d_len =
- {
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08
- };
-
- int next_word()
- {
- if(tdctl.Ibufndx >= tdctl.Ibufcnt)
- {
- tdctl.Ibufndx = 0;
- tdctl.Ibufcnt = (ushort)data_read(out tdctl.Inbuf, BUFSZ);
- if(tdctl.Ibufcnt <= 0) return -1;
- }
-
- while(getlen <= 8)
- {
- // typically reads a word at a time
- getbuf |= (ushort)(tdctl.Inbuf[tdctl.Ibufndx++] << (8 - getlen));
- getlen += 8;
- }
-
- return 0;
- }
-
- int GetBit() /* get one bit */
- {
- short i;
- if(next_word() < 0) return -1;
-
- i = (short)getbuf;
- getbuf <<= 1;
- getlen--;
- return i < 0 ? 1 : 0;
- }
-
- int GetByte() /* get a byte */
- {
- ushort i;
- if(next_word() != 0) return -1;
-
- i = getbuf;
- getbuf <<= 8;
- getlen -= 8;
- i = (ushort)(i >> 8);
- return i;
- }
-
- /* initialize freq tree */
-
- void StartHuff()
- {
- int i, j;
-
- for(i = 0; i < N_CHAR; i++)
- {
- freq[i] = 1;
- son[i] = (short)(i + T);
- prnt[i + T] = (short)i;
- }
-
- i = 0;
- j = N_CHAR;
- while(j <= ROOT)
- {
- freq[j] = (ushort)(freq[i] + freq[i + 1]);
- son[j] = (short)i;
- prnt[i] = prnt[i + 1] = (short)j;
- i += 2;
- j++;
- }
-
- freq[T] = 0xffff;
- prnt[ROOT] = 0;
- }
-
- /* reconstruct freq tree */
-
- void Reconst()
- {
- short i, j, k;
-
- /* halven cumulative freq for leaf nodes */
- j = 0;
- for(i = 0; i < T; i++)
- if(son[i] >= T)
- {
- freq[j] = (ushort)((freq[i] + 1) / 2);
- son[j] = son[i];
- j++;
- }
- /* make a tree : first, connect children nodes */
- for(i = 0, j = N_CHAR; j < T; i += 2, j++)
- {
- k = (short)(i + 1);
- ushort f = freq[j] = (ushort)(freq[i] + freq[k]);
- for(k = (short)(j - 1); f < freq[k]; k--) { }
-
- k++;
- ushort l = (ushort)((j - k) * 2);
-
- Array.ConstrainedCopy(freq, k, freq, k + 1, l);
- freq[k] = f;
- Array.ConstrainedCopy(son, k, son, k + 1, l);
- son[k] = i;
- }
- /* connect parent nodes */
- for(i = 0; i < T; i++)
- if((k = son[i]) >= T) prnt[k] = i;
- else prnt[k] = prnt[k + 1] = i;
- }
-
- /* update freq tree */
-
- void Update(int c)
- {
- if(freq[ROOT] == MAX_FREQ) Reconst();
- c = prnt[c + T];
- do
- {
- int k = ++freq[c];
-
- /* swap nodes to keep the tree freq-ordered */
- int l;
- if(k <= freq[l = c + 1]) continue;
-
- while(k > freq[++l]) { }
-
- l--;
- freq[c] = freq[l];
- freq[l] = (ushort)k;
-
- int i = son[c];
- prnt[i] = (short)l;
- if(i < T) prnt[i + 1] = (short)l;
-
- int j = son[l];
- son[l] = (short)i;
-
- prnt[j] = (short)c;
- if(j < T) prnt[j + 1] = (short)c;
- son[c] = (short)j;
-
- c = l;
- }
- while((c = prnt[c]) != 0); /* do it until reaching the root */
- }
-
- short DecodeChar()
- {
- ushort c;
-
- c = (ushort)son[ROOT];
-
- /*
- * start searching tree from the root to leaves.
- * choose node #(son[]) if input bit == 0
- * else choose #(son[]+1) (input bit == 1)
- */
- while(c < T)
- {
- int ret;
- if((ret = GetBit()) < 0) return -1;
-
- c += (ushort)ret;
- c = (ushort)son[c];
- }
-
- c -= T;
- Update(c);
- return (short)c;
- }
-
- short DecodePosition()
- {
- short bit;
- ushort i, j, c;
-
- /* decode upper 6 bits from given table */
- if((bit = (short)GetByte()) < 0) return -1;
-
- i = (ushort)bit;
- c = (ushort)(d_code[i] << 6);
- j = d_len[i];
-
- /* input lower 6 bits directly */
- j -= 2;
- while(j-- > 0)
- {
- if((bit = (short)GetBit()) < 0) return -1;
-
- i = (ushort)((i << 1) + bit);
- }
-
- return (short)(c | (i & 0x3f));
- }
-
- /* DeCompression
-
- split out initialization code to init_Decode()
-
- */
-
- void init_Decode()
- {
- int i;
- getbuf = 0;
- getlen = 0;
- tdctl = new Tdlzhuf();
- tdctl.Ibufcnt = tdctl.Ibufndx = 0; // input buffer is empty
- tdctl.Bufcnt = 0;
- StartHuff();
- for(i = 0; i < N - F; i++) text_buf[i] = 0x20;
-
- tdctl.R = N - F;
- }
-
- int Decode(out byte[] buf, int len) /* Decoding/Uncompressing */
- {
- short c;
- buf = new byte[len];
- int count; // was an unsigned long, seems unnecessary
- for(count = 0; count < len;)
- if(tdctl.Bufcnt == 0)
- {
- if((c = DecodeChar()) < 0) return count; // fatal error
-
- if(c < 256)
- {
- buf[count] = (byte)c;
- text_buf[tdctl.R++] = (byte)c;
- tdctl.R &= N - 1;
- count++;
- }
- else
- {
- short pos;
- if((pos = DecodePosition()) < 0) return count; // fatal error
-
- tdctl.Bufpos = (ushort)((tdctl.R - pos - 1) & (N - 1));
- tdctl.Bufcnt = (ushort)(c - 255 + THRESHOLD);
- tdctl.Bufndx = 0;
- }
- }
- else
- {
- // still chars from last string
- while(tdctl.Bufndx < tdctl.Bufcnt && count < len)
- {
- c = text_buf[(tdctl.Bufpos + tdctl.Bufndx) & (N - 1)];
- buf[count] = (byte)c;
- tdctl.Bufndx++;
- text_buf[tdctl.R++] = (byte)c;
- tdctl.R &= N - 1;
- count++;
- }
- // reset bufcnt after copy string from text_buf[]
- if(tdctl.Bufndx >= tdctl.Bufcnt) tdctl.Bufndx = tdctl.Bufcnt = 0;
- }
-
- return count; // count == len, success
- }
- #endregion LZH decompression from MAME
}
}
\ No newline at end of file
diff --git a/DiscImageChef.sln b/DiscImageChef.sln
index cfc6d9e43..abd01fc7e 100644
--- a/DiscImageChef.sln
+++ b/DiscImageChef.sln
@@ -39,6 +39,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DiscImageChef.Tests", "Disc
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DiscImageChef.Tests.Devices", "DiscImageChef.Tests.Devices\DiscImageChef.Tests.Devices.csproj", "{A40662EB-D202-46A4-AB41-9C32ADE6D6B5}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DiscImageChef.Compression", "DiscImageChef.Compression\DiscImageChef.Compression.csproj", "{858398D1-7321-4763-8BAB-56BBFEC74E29}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
@@ -117,6 +119,10 @@ Global
{A40662EB-D202-46A4-AB41-9C32ADE6D6B5}.Debug|x86.Build.0 = Debug|x86
{A40662EB-D202-46A4-AB41-9C32ADE6D6B5}.Release|x86.ActiveCfg = Release|x86
{A40662EB-D202-46A4-AB41-9C32ADE6D6B5}.Release|x86.Build.0 = Release|x86
+ {858398D1-7321-4763-8BAB-56BBFEC74E29}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {858398D1-7321-4763-8BAB-56BBFEC74E29}.Debug|x86.Build.0 = Debug|Any CPU
+ {858398D1-7321-4763-8BAB-56BBFEC74E29}.Release|x86.ActiveCfg = Release|Any CPU
+ {858398D1-7321-4763-8BAB-56BBFEC74E29}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE