diff --git a/DAT_Image_File.txt b/DAT_Image_File.txt
index 18aee64..b3c3f97 100644
--- a/DAT_Image_File.txt
+++ b/DAT_Image_File.txt
@@ -2,7 +2,7 @@ DAT Image File
Author: Michael Gaisser (mjgaisser@gmail.com)
Site: https://github.com/MikeG621
Released: 2010.02.14
-Updated: 2019.09.21
+Updated: 2022.02.27
=====
The .DAT file used in X-wing Alliance is an archive file that can contain
@@ -20,7 +20,7 @@ NAME LENGTH DESC
---- ------ ----
BYTE 1 unsigned 8-bit
SHORT 2 signed Int16
-INT 4 signed Int32
+INT 4 signed Int32
LONG 8 signed Int64
--
@@ -61,10 +61,15 @@ struct Sub
0x00 SubHeader
0x12 ImageHeader
0x3E Colors[ImageHeader.NumberOfColors]
+#if (Type!=25C)
Row[Height]
- #if (Type==7 || Type==23)
+#else
+ 0x3E BYTE[5] CompressionParameters
+ 0x43 BYTE[] LzmaData
+#endif
+#if (Type==7 || Type==23)
BYTE Reserved (0)
- #endif
+#endif
}
struct SubHeader (size 0x12)
@@ -105,49 +110,49 @@ struct Color (size 0x3)
struct Row
{
- #if (Type==7 || Type==23)
+#if (Type==7 || Type==23)
BYTE NumberOfOperations
OpCode[NumberOfOperations]
- #elseif (Type==24 || Type==25)
+#elseif (Type==24 || Type==25)
Pixel[Width]
- #endif
+#endif
}
struct OpCode
{
BYTE Value
- #if (Type==7 && ~Value & 0x80) (Read)
+#if (Type==7 && ~Value & 0x80) (Read)
BYTE[Value &0x7F] ColorIndexes
- #elseif (Type==23 && Value & 0xC0) (Read with Alpha)
+#elseif (Type==23 && Value & 0xC0) (Read with Alpha)
Pixel[Value & 0x3F]
- #elseif (Type==23 && Value < 0x40) (Read Opaque)
+#elseif (Type==23 && Value < 0x40) (Read Opaque)
BYTE[Value]
- #else (Short Transparent)
- #endif
+#else (Short Transparent)
+#endif
}
struct Pixel
{
- #if (Type==23)
+#if (Type==23)
BYTE Alpha
- #endif
- #if (Type==25)
+#endif
+#if (Type==25)
BYTE Blue
BYTE Green
BYTE Red
- #else
+#else
BYTE ColorIndex
- #endif
- #if (Type==24 || Type==25)
+#endif
+#if (Type==24 || Type==25)
BYTE Alpha
- #endif
+#endif
}
=====
DAT Structure Details
Yet another image format for the X-wing series, although this one is XWA only.
-Four different raw formats are bundled under this one type, the difference is
+Five different raw formats are bundled under this one type, the difference is
the type of bitmaps that are displayed. More on that in a sec.
As mentioned at the beginning, the images are categorized as Groups and Subs.
@@ -207,6 +212,12 @@ type. The Type determines the type (gasp) of image and how it needs to be read.
0x17 Alpha blended bitmap (32bpp), compressed
0x18 Alpha blended bitmap (32bpp), uncompressed
0x19 Full ARGB image (32bpp), uncompressed
+0x19 Full ARGB image (32bpp), LZMA compressed (aka "25C")
+
+Yes, those are both 0x19. The way that compression gets flagged is that the
+ImageHeader.NumberOfColors (couple paragraphs down) is set to 1. There really
+aren't any colors defined so that array is empty, but that's the way that 25C
+is defined.
Width and Height are easy enough, they're one-indexed values. The GroupID and
SubID values should also be self-explanatory. SubHeader.Length could also be
@@ -233,7 +244,8 @@ Okay, here's where most of the magic happens. The only common similarity
between the first three Types are the length of the Rows array itself, and that
they are all indexed color images. Once we get to Type 25 (which again requires
a hook to be installed) then there's little to no processing that needs to be
-done and the size of Row doubles.
+done and the size of Row doubles. For Type 25C, the rows are omitted entirely
+since the data is LZMA compressed and is processed separately.
First off, Type 7; Transparent. Mostly used as the backdrop type, it also
has the tightest compression rate for filesize. The Row starts off with the
@@ -295,4 +307,9 @@ zero (which of course means all Group.NumberOfColors values are zero) and
Sub.ImageHeaderOffset is 0x2C since there's no Color array to skip over.
For the image data itself, it's simple raw pixel data at this point, four bytes
-in the BGRA order with no extra termination data, they go from one to the next.
\ No newline at end of file
+in the BGRA order with no extra termination data, they go from one to the next.
+
+Type "25C"; Full ARGB, LZMA compressed, requires 32-bit hook. The
+data is the same as 25, however it's stored as LZMA compressed data. Per the
+LZMA documentation, the first 5 bytes are the compression parameters that
+defines exactly how it's stored.
\ No newline at end of file
diff --git a/DatFile.cs b/DatFile.cs
index 7134edf..f7f5231 100644
--- a/DatFile.cs
+++ b/DatFile.cs
@@ -1,6 +1,6 @@
/*
* Idmr.ImageFormat.Dat, Allows editing capability of LA *.DAT Image files
- * Copyright (C) 2009-2021 Michael Gaisser (mjgaisser@gmail.com)
+ * Copyright (C) 2009-2022 Michael Gaisser (mjgaisser@gmail.com)
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the Mozilla Public License; either version 2.0 of the
@@ -10,13 +10,17 @@
* library is free of defects, merchantable, fit for a particular purpose or
* non-infringing. See the full license text for more details.
*
- * If a copy of the MPL (MPL.txt) was not distributed with this file,
+ * If a copy of the MPL (License.txt) was not distributed with this file,
* you can obtain one at http://mozilla.org/MPL/2.0/.
*
- * VERSION: 2.3
+ * VERSION: 2.4
*/
/* CHANGE LOG
+ * v2.4, 220227
+ * [NEW] Format 25C capability
+ * [UPD] Forced garbage collection after load. I know that's taboo, shush.
+ * [UPD] during Save, write the raw data from EncodeImage for Full32bpp images since that's no longer
* v2.3, 210606
* [UPD] Ignores empty Groups
* v2.2, 190922
@@ -62,6 +66,7 @@ public DatFile(string file)
fs = File.OpenRead(file);
DecodeFile(new BinaryReader(fs).ReadBytes((int)fs.Length));
fs.Close();
+ GC.Collect(); // for files like Planet2 that are 100% Full32, this'll immediately release half the memory
}
catch (Exception x)
{
@@ -86,7 +91,7 @@ public void Save()
for (int g = 0; g < NumberOfGroups; g++) if (Groups[g].ID < 0) throw new InvalidDataException("Not all Groups have been initialized");
updateGroupHeaders();
for (int g = 0; g < NumberOfGroups; g++)
- for (int s = 0; s < Groups[g].NumberOfSubs; s++) Groups[g].Subs[s]._headerUpdate();
+ for (int s = 0; s < Groups[g].NumberOfSubs; s++) Groups[g].Subs[s].UpdateHeader();
if (File.Exists(_filePath)) File.Copy(_filePath, tempFile); // create backup
File.Delete(_filePath);
fs = File.OpenWrite(_filePath);
@@ -96,10 +101,10 @@ public void Save()
bw.Write((short)1);
bw.Write(NumberOfGroups);
bw.Write(Groups.NumberOfSubs);
- bw.Write(_length);
+ bw.Write(length);
bw.Write(Groups.NumberOfColors);
fs.Position += 8; // long 0
- bw.Write(_dataOffset);
+ bw.Write(dataOffset);
for (int g = 0; g < NumberOfGroups; g++) bw.Write(Groups[g]._header);
// Groups
for (int g = 0; g < NumberOfGroups; g++)
@@ -114,7 +119,12 @@ public void Save()
bw.Write(Groups[g].Subs[s].Colors[c].G);
bw.Write(Groups[g].Subs[s].Colors[c].B);
}
- bw.Write(Groups[g].Subs[s]._rows);
+ if (Groups[g].Subs[s].Type == Sub.ImageType.Full32bppArgb)
+ {
+ // _rows is null to save RAM, so execute the copy to bytes again.
+ bw.Write(Sub.EncodeImage(Groups[g].Subs[s].Image, Sub.ImageType.Full32bppArgb, null, out _, out _));
+ }
+ else bw.Write(Groups[g].Subs[s]._rows);
}
}
fs.SetLength(fs.Position);
@@ -166,8 +176,8 @@ public void DecodeFile(byte[] rawData)
{
for (int j = 0; j < Groups[i].NumberOfSubs; j++)
{
- int length = BitConverter.ToInt32(rawData, offset + 0xE);
- byte[] sub = new byte[length + Sub._subHeaderLength];
+ int dataLength = BitConverter.ToInt32(rawData, offset + 0xE);
+ byte[] sub = new byte[dataLength + Sub._subHeaderLength];
ArrayFunctions.TrimArray(rawData, offset, sub);
Groups[i].Subs[j] = new Sub(sub);
offset += sub.Length;
@@ -226,25 +236,25 @@ void updateGroupHeaders()
{
// GroupID is always up-to-date
ArrayFunctions.WriteToArray(Groups[i].NumberOfSubs, Groups[i]._header, 2);
- ArrayFunctions.WriteToArray(Groups[i]._length, Groups[i]._header, 4);
+ ArrayFunctions.WriteToArray(Groups[i].length, Groups[i]._header, 4);
ArrayFunctions.WriteToArray(Groups[i].Subs.NumberOfColors, Groups[i]._header, 8);
// Reserved(0) is always up-to-date
- if (i == 0) Groups[i]._dataOffset = 0;
- else Groups[i]._dataOffset = Groups[i - 1]._dataOffset + Groups[i - 1]._length;
+ if (i == 0) Groups[i].dataOffset = 0;
+ else Groups[i].dataOffset = Groups[i - 1].dataOffset + Groups[i - 1].length;
}
}
/// Gets sum of Groups.Length values
- int _length
+ int length
{
get
{
int l = 0;
- for (int i = 0; i < Groups.Count; i++) l += Groups[i]._length;
+ for (int i = 0; i < Groups.Count; i++) l += Groups[i].length;
return l;
}
}
- int _dataOffset { get { return NumberOfGroups * Group._headerLength; } }
+ int dataOffset { get { return NumberOfGroups * Group._headerLength; } }
}
}
diff --git a/Group.cs b/Group.cs
index ebd4c4e..5de709c 100644
--- a/Group.cs
+++ b/Group.cs
@@ -1,6 +1,6 @@
/*
* Idmr.ImageFormat.Dat, Allows editing capability of LucasArts *.DAT Image files
- * Copyright (C) 2009-2019 Michael Gaisser (mjgaisser@gmail.com)
+ * Copyright (C) 2009-2022 Michael Gaisser (mjgaisser@gmail.com)
* Licensed under the MPL v2.0 or later
*
* Full notice in DatFile.cs
@@ -112,7 +112,7 @@ public short ID
#endregion public properties
/// Sum of Subs.Length values
- internal int _length
+ internal int length
{
get
{
@@ -122,7 +122,7 @@ internal int _length
}
}
/// ***Must be updated at the Dat level***
- internal int _dataOffset
+ internal int dataOffset
{
get { return BitConverter.ToInt32(_header, 0x14); }
set { ArrayFunctions.WriteToArray(value, _header, 0x14); }
diff --git a/ImageFormat.Dat.csproj b/ImageFormat.Dat.csproj
new file mode 100644
index 0000000..6b6f61c
--- /dev/null
+++ b/ImageFormat.Dat.csproj
@@ -0,0 +1,105 @@
+
+
+
+ Debug
+ AnyCPU
+ 9.0.21022
+ 2.0
+ {CB13D130-495C-4F73-9B23-FE688973EF0D}
+ Library
+ Properties
+ Idmr.ImageFormat
+ Idmr.ImageFormat.Dat
+ v4.0
+ 512
+
+
+
+
+ 3.5
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ bin\Release\Idmr.ImageFormat.Dat.XML
+
+
+
+ False
+ ..\..\..\Libraries\Idmr.Common.dll
+
+
+
+ 3.5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
+
+ copy /y "$(TargetPath)" "C:\Users\Me\Documents\Visual Studio 2008\Libraries\"
+
+
\ No newline at end of file
diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs
index c7b2767..6e2e0f8 100644
--- a/Properties/AssemblyInfo.cs
+++ b/Properties/AssemblyInfo.cs
@@ -1,6 +1,6 @@
/*
* Idmr.ImageFormat.Dat, Allows editing capability of LucasArts *.DAT Image files
- * Copyright (C) 2009-2019 Michael Gaisser (mjgaisser@gmail.com)
+ * Copyright (C) 2009-2022 Michael Gaisser (mjgaisser@gmail.com)
* Licensed under the MPL v2.0 or later
*
* Full notice in DatFile.cs
@@ -13,9 +13,9 @@
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Imperial Department of Military Research")]
[assembly: AssemblyProduct("Idmr.ImageFormat.Dat")]
-[assembly: AssemblyCopyright("Copyright © Michael Gaisser 2009-2019")]
+[assembly: AssemblyCopyright("Copyright © Michael Gaisser 2009-2022")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Major.Minor.Build.Revision
-[assembly: AssemblyVersion("2.3.0.*")]
\ No newline at end of file
+[assembly: AssemblyVersion("2.4.*")]
\ No newline at end of file
diff --git a/Readme.txt b/Readme.txt
index 328fa04..b9d1646 100644
--- a/Readme.txt
+++ b/Readme.txt
@@ -2,14 +2,19 @@ Idmr.ImageFormat.Dat.dll
========================
Author: Michael Gaisser (mjgaisser@gmail.com)
-Version: 2.3
-Date: 2021.06.06
+Version: 2.4
+Date: 2022.02.27
Library for reading LucasArts *.DAT backdrop files
==========
Version History
+v2.4 - 27 Feb 2022
+ - Added Format 25C capability
+ - Image isn't decompressed until first accessed
+ - Format 25 now takes up half the memory after loading
+
v2.3 - 06 Jun 2021
- Empty Groups are now ignored during read
- (GroupCollection) Changed the increment on intial ID creation so it's always negative
@@ -59,9 +64,11 @@ Programmer's reference can be found in help/Idmr.ImageFormat.Dat.chm
==========
Copyright Information
-Copyright (C) Michael Gaisser, 2009-2021
+Copyright (C) Michael Gaisser, 2009-2022
This library file and related files are licensed under the Mozilla Public License
-v2.0 or later. See MPL.txt for further details.
+v2.0 or later. See License.txt for further details.
+
+The LZMA SDK is written and placed in the public domain by Igor Pavlov.
"Star Wars" and related items are trademarks of LucasFilm Ltd and
LucasArts Entertainment Co.
diff --git a/SevenZip/CRC.cs b/SevenZip/CRC.cs
new file mode 100644
index 0000000..4ebb1a3
--- /dev/null
+++ b/SevenZip/CRC.cs
@@ -0,0 +1,25 @@
+// Part of the LZMA SDK by Igor Pavlov
+
+namespace SevenZip
+{
+ class CRC
+ {
+ public static readonly uint[] Table;
+
+ static CRC()
+ {
+ Table = new uint[256];
+ const uint kPoly = 0xEDB88320;
+ for (uint i = 0; i < 256; i++)
+ {
+ uint r = i;
+ for (int j = 0; j < 8; j++)
+ if ((r & 1) != 0)
+ r = (r >> 1) ^ kPoly;
+ else
+ r >>= 1;
+ Table[i] = r;
+ }
+ }
+ }
+}
diff --git a/SevenZip/Compression/LZ/BinTree.cs b/SevenZip/Compression/LZ/BinTree.cs
new file mode 100644
index 0000000..df402d5
--- /dev/null
+++ b/SevenZip/Compression/LZ/BinTree.cs
@@ -0,0 +1,367 @@
+// Part of the LZMA SDK by Igor Pavlov
+
+using System;
+
+namespace SevenZip.Compression.LZ
+{
+ public class BinTree : InWindow, IMatchFinder
+ {
+ uint _cyclicBufferPos;
+ uint _cyclicBufferSize = 0;
+ uint _matchMaxLen;
+
+ uint[] _son;
+ uint[] _hash;
+
+ uint _cutValue = 0xFF;
+ uint _hashMask;
+ uint _hashSizeSum = 0;
+
+#pragma warning disable IDE1006 // Naming Styles
+ bool HASH_ARRAY = true;
+
+ const uint kHash2Size = 1 << 10;
+ const uint kHash3Size = 1 << 16;
+ const uint kBT2HashSize = 1 << 16;
+ const uint kStartMaxLen = 1;
+ const uint kHash3Offset = kHash2Size;
+ const uint kEmptyHashValue = 0;
+ const uint kMaxValForNormalize = ((uint)1 << 31) - 1;
+
+ uint kNumHashDirectBytes = 0;
+ uint kMinMatchCheck = 4;
+ uint kFixHashSize = kHash2Size + kHash3Size;
+#pragma warning restore IDE1006 // Naming Styles
+
+ public void SetType(int numHashBytes)
+ {
+ HASH_ARRAY = (numHashBytes > 2);
+ if (HASH_ARRAY)
+ {
+ kNumHashDirectBytes = 0;
+ kMinMatchCheck = 4;
+ kFixHashSize = kHash2Size + kHash3Size;
+ }
+ else
+ {
+ kNumHashDirectBytes = 2;
+ kMinMatchCheck = 2 + 1;
+ kFixHashSize = 0;
+ }
+ }
+
+ public new void SetStream(System.IO.Stream stream) { base.SetStream(stream); }
+ public new void ReleaseStream() { base.ReleaseStream(); }
+
+ public new void Init()
+ {
+ base.Init();
+ for (uint i = 0; i < _hashSizeSum; i++)
+ _hash[i] = kEmptyHashValue;
+ _cyclicBufferPos = 0;
+ ReduceOffsets(-1);
+ }
+
+ public new void MovePos()
+ {
+ if (++_cyclicBufferPos >= _cyclicBufferSize)
+ _cyclicBufferPos = 0;
+ base.MovePos();
+ if (_pos == kMaxValForNormalize)
+ normalize();
+ }
+
+ public new byte GetIndexByte(int index) { return base.GetIndexByte(index); }
+
+ public new uint GetMatchLen(int index, uint distance, uint limit)
+ { return base.GetMatchLen(index, distance, limit); }
+
+ public new uint GetNumAvailableBytes() { return base.GetNumAvailableBytes(); }
+
+ public void Create(uint historySize, uint keepAddBufferBefore,
+ uint matchMaxLen, uint keepAddBufferAfter)
+ {
+ if (historySize > kMaxValForNormalize - 256)
+ throw new Exception();
+ _cutValue = 16 + (matchMaxLen >> 1);
+
+ uint windowReservSize = (historySize + keepAddBufferBefore +
+ matchMaxLen + keepAddBufferAfter) / 2 + 256;
+
+ base.Create(historySize + keepAddBufferBefore, matchMaxLen + keepAddBufferAfter, windowReservSize);
+
+ _matchMaxLen = matchMaxLen;
+
+ uint cyclicBufferSize = historySize + 1;
+ if (_cyclicBufferSize != cyclicBufferSize)
+ _son = new uint[(_cyclicBufferSize = cyclicBufferSize) * 2];
+
+ uint hs = kBT2HashSize;
+
+ if (HASH_ARRAY)
+ {
+ hs = historySize - 1;
+ hs |= (hs >> 1);
+ hs |= (hs >> 2);
+ hs |= (hs >> 4);
+ hs |= (hs >> 8);
+ hs >>= 1;
+ hs |= 0xFFFF;
+ if (hs > (1 << 24))
+ hs >>= 1;
+ _hashMask = hs;
+ hs++;
+ hs += kFixHashSize;
+ }
+ if (hs != _hashSizeSum)
+ _hash = new uint[_hashSizeSum = hs];
+ }
+
+ public uint GetMatches(uint[] distances)
+ {
+ uint lenLimit;
+ if (_pos + _matchMaxLen <= _streamPos)
+ lenLimit = _matchMaxLen;
+ else
+ {
+ lenLimit = _streamPos - _pos;
+ if (lenLimit < kMinMatchCheck)
+ {
+ MovePos();
+ return 0;
+ }
+ }
+
+ uint offset = 0;
+ uint matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0;
+ uint cur = _bufferOffset + _pos;
+ uint maxLen = kStartMaxLen; // to avoid items for len < hashSize;
+ uint hashValue, hash2Value = 0, hash3Value = 0;
+
+ if (HASH_ARRAY)
+ {
+ uint temp = CRC.Table[_bufferBase[cur]] ^ _bufferBase[cur + 1];
+ hash2Value = temp & (kHash2Size - 1);
+ temp ^= ((uint)(_bufferBase[cur + 2]) << 8);
+ hash3Value = temp & (kHash3Size - 1);
+ hashValue = (temp ^ (CRC.Table[_bufferBase[cur + 3]] << 5)) & _hashMask;
+ }
+ else
+ hashValue = _bufferBase[cur] ^ ((uint)(_bufferBase[cur + 1]) << 8);
+
+ uint curMatch = _hash[kFixHashSize + hashValue];
+ if (HASH_ARRAY)
+ {
+ uint curMatch2 = _hash[hash2Value];
+ uint curMatch3 = _hash[kHash3Offset + hash3Value];
+ _hash[hash2Value] = _pos;
+ _hash[kHash3Offset + hash3Value] = _pos;
+ if (curMatch2 > matchMinPos)
+ if (_bufferBase[_bufferOffset + curMatch2] == _bufferBase[cur])
+ {
+ distances[offset++] = maxLen = 2;
+ distances[offset++] = _pos - curMatch2 - 1;
+ }
+ if (curMatch3 > matchMinPos)
+ if (_bufferBase[_bufferOffset + curMatch3] == _bufferBase[cur])
+ {
+ if (curMatch3 == curMatch2)
+ offset -= 2;
+ distances[offset++] = maxLen = 3;
+ distances[offset++] = _pos - curMatch3 - 1;
+ curMatch2 = curMatch3;
+ }
+ if (offset != 0 && curMatch2 == curMatch)
+ {
+ offset -= 2;
+ maxLen = kStartMaxLen;
+ }
+ }
+
+ _hash[kFixHashSize + hashValue] = _pos;
+
+ uint ptr0 = (_cyclicBufferPos << 1) + 1;
+ uint ptr1 = (_cyclicBufferPos << 1);
+
+ uint len0, len1;
+ len0 = len1 = kNumHashDirectBytes;
+
+ if (kNumHashDirectBytes != 0)
+ {
+ if (curMatch > matchMinPos)
+ {
+ if (_bufferBase[_bufferOffset + curMatch + kNumHashDirectBytes] !=
+ _bufferBase[cur + kNumHashDirectBytes])
+ {
+ distances[offset++] = maxLen = kNumHashDirectBytes;
+ distances[offset++] = _pos - curMatch - 1;
+ }
+ }
+ }
+
+ uint count = _cutValue;
+
+ while(true)
+ {
+ if(curMatch <= matchMinPos || count-- == 0)
+ {
+ _son[ptr0] = _son[ptr1] = kEmptyHashValue;
+ break;
+ }
+ uint delta = _pos - curMatch;
+ uint cyclicPos = ((delta <= _cyclicBufferPos) ?
+ (_cyclicBufferPos - delta) :
+ (_cyclicBufferPos - delta + _cyclicBufferSize)) << 1;
+
+ uint pby1 = _bufferOffset + curMatch;
+ uint len = Math.Min(len0, len1);
+ if (_bufferBase[pby1 + len] == _bufferBase[cur + len])
+ {
+ while(++len != lenLimit)
+ if (_bufferBase[pby1 + len] != _bufferBase[cur + len])
+ break;
+ if (maxLen < len)
+ {
+ distances[offset++] = maxLen = len;
+ distances[offset++] = delta - 1;
+ if (len == lenLimit)
+ {
+ _son[ptr1] = _son[cyclicPos];
+ _son[ptr0] = _son[cyclicPos + 1];
+ break;
+ }
+ }
+ }
+ if (_bufferBase[pby1 + len] < _bufferBase[cur + len])
+ {
+ _son[ptr1] = curMatch;
+ ptr1 = cyclicPos + 1;
+ curMatch = _son[ptr1];
+ len1 = len;
+ }
+ else
+ {
+ _son[ptr0] = curMatch;
+ ptr0 = cyclicPos;
+ curMatch = _son[ptr0];
+ len0 = len;
+ }
+ }
+ MovePos();
+ return offset;
+ }
+
+ public void Skip(uint num)
+ {
+ do
+ {
+ uint lenLimit;
+ if (_pos + _matchMaxLen <= _streamPos)
+ lenLimit = _matchMaxLen;
+ else
+ {
+ lenLimit = _streamPos - _pos;
+ if (lenLimit < kMinMatchCheck)
+ {
+ MovePos();
+ continue;
+ }
+ }
+
+ uint matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0;
+ uint cur = _bufferOffset + _pos;
+
+ uint hashValue;
+
+ if (HASH_ARRAY)
+ {
+ uint temp = CRC.Table[_bufferBase[cur]] ^ _bufferBase[cur + 1];
+ uint hash2Value = temp & (kHash2Size - 1);
+ _hash[hash2Value] = _pos;
+ temp ^= ((uint)(_bufferBase[cur + 2]) << 8);
+ uint hash3Value = temp & (kHash3Size - 1);
+ _hash[kHash3Offset + hash3Value] = _pos;
+ hashValue = (temp ^ (CRC.Table[_bufferBase[cur + 3]] << 5)) & _hashMask;
+ }
+ else
+ hashValue = _bufferBase[cur] ^ ((uint)(_bufferBase[cur + 1]) << 8);
+
+ uint curMatch = _hash[kFixHashSize + hashValue];
+ _hash[kFixHashSize + hashValue] = _pos;
+
+ uint ptr0 = (_cyclicBufferPos << 1) + 1;
+ uint ptr1 = (_cyclicBufferPos << 1);
+
+ uint len0, len1;
+ len0 = len1 = kNumHashDirectBytes;
+
+ uint count = _cutValue;
+ while (true)
+ {
+ if (curMatch <= matchMinPos || count-- == 0)
+ {
+ _son[ptr0] = _son[ptr1] = kEmptyHashValue;
+ break;
+ }
+
+ uint delta = _pos - curMatch;
+ uint cyclicPos = ((delta <= _cyclicBufferPos) ?
+ (_cyclicBufferPos - delta) :
+ (_cyclicBufferPos - delta + _cyclicBufferSize)) << 1;
+
+ uint pby1 = _bufferOffset + curMatch;
+ uint len = Math.Min(len0, len1);
+ if (_bufferBase[pby1 + len] == _bufferBase[cur + len])
+ {
+ while (++len != lenLimit)
+ if (_bufferBase[pby1 + len] != _bufferBase[cur + len])
+ break;
+ if (len == lenLimit)
+ {
+ _son[ptr1] = _son[cyclicPos];
+ _son[ptr0] = _son[cyclicPos + 1];
+ break;
+ }
+ }
+ if (_bufferBase[pby1 + len] < _bufferBase[cur + len])
+ {
+ _son[ptr1] = curMatch;
+ ptr1 = cyclicPos + 1;
+ curMatch = _son[ptr1];
+ len1 = len;
+ }
+ else
+ {
+ _son[ptr0] = curMatch;
+ ptr0 = cyclicPos;
+ curMatch = _son[ptr0];
+ len0 = len;
+ }
+ }
+ MovePos();
+ }
+ while (--num != 0);
+ }
+
+ void normalizeLinks(uint[] items, uint numItems, uint subValue)
+ {
+ for (uint i = 0; i < numItems; i++)
+ {
+ uint value = items[i];
+ if (value <= subValue)
+ value = kEmptyHashValue;
+ else
+ value -= subValue;
+ items[i] = value;
+ }
+ }
+
+ void normalize()
+ {
+ uint subValue = _pos - _cyclicBufferSize;
+ normalizeLinks(_son, _cyclicBufferSize * 2, subValue);
+ normalizeLinks(_hash, _hashSizeSum, subValue);
+ ReduceOffsets((int)subValue);
+ }
+ }
+}
diff --git a/SevenZip/Compression/LZ/IMatchFinder.cs b/SevenZip/Compression/LZ/IMatchFinder.cs
new file mode 100644
index 0000000..19d3234
--- /dev/null
+++ b/SevenZip/Compression/LZ/IMatchFinder.cs
@@ -0,0 +1,21 @@
+// Part of the LZMA SDK by Igor Pavlov
+
+namespace SevenZip.Compression.LZ
+{
+ interface IInWindowStream
+ {
+ void SetStream(System.IO.Stream inStream);
+ void Init();
+ void ReleaseStream();
+ byte GetIndexByte(int index);
+ uint GetMatchLen(int index, uint distance, uint limit);
+ uint GetNumAvailableBytes();
+ }
+
+ interface IMatchFinder : IInWindowStream
+ {
+ void Create(uint historySize, uint keepAddBufferBefore, uint matchMaxLen, uint keepAddBufferAfter);
+ uint GetMatches(uint[] distances);
+ void Skip(uint num);
+ }
+}
diff --git a/SevenZip/Compression/LZ/InWindow.cs b/SevenZip/Compression/LZ/InWindow.cs
new file mode 100644
index 0000000..3a4d35a
--- /dev/null
+++ b/SevenZip/Compression/LZ/InWindow.cs
@@ -0,0 +1,130 @@
+// Part of the LZMA SDK by Igor Pavlov
+
+namespace SevenZip.Compression.LZ
+{
+ public class InWindow
+ {
+ public byte[] _bufferBase = null; // pointer to buffer with data
+ System.IO.Stream _stream;
+ uint _posLimit; // offset (from _buffer) of first byte when new block reading must be done
+ bool _streamEndWasReached; // if (true) then _streamPos shows real end of stream
+
+ uint _pointerToLastSafePosition;
+
+ public uint _bufferOffset;
+
+ public uint _blockSize; // Size of Allocated memory block
+ public uint _pos; // offset (from _buffer) of curent byte
+ uint _keepSizeBefore; // how many BYTEs must be kept in buffer before _pos
+ uint _keepSizeAfter; // how many BYTEs must be kept buffer after _pos
+ public uint _streamPos; // offset (from _buffer) of first not read byte from Stream
+
+ public void MoveBlock()
+ {
+ uint offset = (uint)(_bufferOffset) + _pos - _keepSizeBefore;
+ // we need one additional byte, since MovePos moves on 1 byte.
+ if (offset > 0)
+ offset--;
+
+ uint numBytes = (uint)(_bufferOffset) + _streamPos - offset;
+
+ // check negative offset ????
+ for (uint i = 0; i < numBytes; i++)
+ _bufferBase[i] = _bufferBase[offset + i];
+ _bufferOffset -= offset;
+ }
+
+ public virtual void ReadBlock()
+ {
+ if (_streamEndWasReached)
+ return;
+ while (true)
+ {
+ int size = (int)((0 - _bufferOffset) + _blockSize - _streamPos);
+ if (size == 0)
+ return;
+ int numReadBytes = _stream.Read(_bufferBase, (int)(_bufferOffset + _streamPos), size);
+ if (numReadBytes == 0)
+ {
+ _posLimit = _streamPos;
+ uint pointerToPostion = _bufferOffset + _posLimit;
+ if (pointerToPostion > _pointerToLastSafePosition)
+ _posLimit = (uint)(_pointerToLastSafePosition - _bufferOffset);
+
+ _streamEndWasReached = true;
+ return;
+ }
+ _streamPos += (uint)numReadBytes;
+ if (_streamPos >= _pos + _keepSizeAfter)
+ _posLimit = _streamPos - _keepSizeAfter;
+ }
+ }
+
+ void free() { _bufferBase = null; }
+
+ public void Create(uint keepSizeBefore, uint keepSizeAfter, uint keepSizeReserv)
+ {
+ _keepSizeBefore = keepSizeBefore;
+ _keepSizeAfter = keepSizeAfter;
+ uint blockSize = keepSizeBefore + keepSizeAfter + keepSizeReserv;
+ if (_bufferBase == null || _blockSize != blockSize)
+ {
+ free();
+ _blockSize = blockSize;
+ _bufferBase = new byte[_blockSize];
+ }
+ _pointerToLastSafePosition = _blockSize - keepSizeAfter;
+ }
+
+ public void SetStream(System.IO.Stream stream) { _stream = stream; }
+ public void ReleaseStream() { _stream = null; }
+
+ public void Init()
+ {
+ _bufferOffset = 0;
+ _pos = 0;
+ _streamPos = 0;
+ _streamEndWasReached = false;
+ ReadBlock();
+ }
+
+ public void MovePos()
+ {
+ _pos++;
+ if (_pos > _posLimit)
+ {
+ uint pointerToPostion = _bufferOffset + _pos;
+ if (pointerToPostion > _pointerToLastSafePosition)
+ MoveBlock();
+ ReadBlock();
+ }
+ }
+
+ public byte GetIndexByte(int index) { return _bufferBase[_bufferOffset + _pos + index]; }
+
+ // index + limit have not to exceed _keepSizeAfter;
+ public uint GetMatchLen(int index, uint distance, uint limit)
+ {
+ if (_streamEndWasReached)
+ if ((_pos + index) + limit > _streamPos)
+ limit = _streamPos - (uint)(_pos + index);
+ distance++;
+ // Byte *pby = _buffer + (size_t)_pos + index;
+ uint pby = _bufferOffset + _pos + (uint)index;
+
+ uint i;
+ for (i = 0; i < limit && _bufferBase[pby + i] == _bufferBase[pby + i - distance]; i++);
+ return i;
+ }
+
+ public uint GetNumAvailableBytes() { return _streamPos - _pos; }
+
+ public void ReduceOffsets(int subValue)
+ {
+ _bufferOffset += (uint)subValue;
+ _posLimit -= (uint)subValue;
+ _pos -= (uint)subValue;
+ _streamPos -= (uint)subValue;
+ }
+ }
+}
diff --git a/SevenZip/Compression/LZ/OutWindow.cs b/SevenZip/Compression/LZ/OutWindow.cs
new file mode 100644
index 0000000..25f7df7
--- /dev/null
+++ b/SevenZip/Compression/LZ/OutWindow.cs
@@ -0,0 +1,85 @@
+// Part of the LZMA SDK by Igor Pavlov
+
+namespace SevenZip.Compression.LZ
+{
+ public class OutWindow
+ {
+ byte[] _buffer = null;
+ uint _pos;
+ uint _windowSize = 0;
+ uint _streamPos;
+ System.IO.Stream _stream;
+
+ public uint TrainSize = 0;
+
+ public void Create(uint windowSize)
+ {
+ if (_windowSize != windowSize)
+ {
+ _buffer = new byte[windowSize];
+ }
+ _windowSize = windowSize;
+ _pos = 0;
+ _streamPos = 0;
+ }
+
+ public void Init(System.IO.Stream stream, bool solid)
+ {
+ ReleaseStream();
+ _stream = stream;
+ if (!solid)
+ {
+ _streamPos = 0;
+ _pos = 0;
+ TrainSize = 0;
+ }
+ }
+
+ public void ReleaseStream()
+ {
+ Flush();
+ _stream = null;
+ }
+
+ public void Flush()
+ {
+ uint size = _pos - _streamPos;
+ if (size == 0)
+ return;
+ _stream.Write(_buffer, (int)_streamPos, (int)size);
+ if (_pos >= _windowSize)
+ _pos = 0;
+ _streamPos = _pos;
+ }
+
+ public void CopyBlock(uint distance, uint len)
+ {
+ uint pos = _pos - distance - 1;
+ if (pos >= _windowSize)
+ pos += _windowSize;
+ for (; len > 0; len--)
+ {
+ if (pos >= _windowSize)
+ pos = 0;
+ _buffer[_pos++] = _buffer[pos++];
+ if (_pos >= _windowSize)
+ Flush();
+ }
+ }
+
+ public void PutByte(byte b)
+ {
+ _buffer[_pos++] = b;
+ if (_pos >= _windowSize)
+ Flush();
+ }
+
+ public byte GetByte(uint distance)
+ {
+ uint pos = _pos - distance - 1;
+ if (pos >= _windowSize)
+ pos += _windowSize;
+ return _buffer[pos];
+ }
+ }
+}
diff --git a/SevenZip/Compression/LZMA/Base.cs b/SevenZip/Compression/LZMA/Base.cs
new file mode 100644
index 0000000..42af986
--- /dev/null
+++ b/SevenZip/Compression/LZMA/Base.cs
@@ -0,0 +1,69 @@
+// Part of the LZMA SDK by Igor Pavlov
+
+namespace SevenZip.Compression.LZMA
+{
+ internal abstract class Base
+ {
+ public const uint kNumRepDistances = 4;
+ public const uint kNumStates = 12;
+
+ public struct State
+ {
+ public uint Index;
+ public void Init() { Index = 0; }
+ public void UpdateChar()
+ {
+ if (Index < 4) Index = 0;
+ else if (Index < 10) Index -= 3;
+ else Index -= 6;
+ }
+ public void UpdateMatch() { Index = (uint)(Index < 7 ? 7 : 10); }
+ public void UpdateRep() { Index = (uint)(Index < 7 ? 8 : 11); }
+ public void UpdateShortRep() { Index = (uint)(Index < 7 ? 9 : 11); }
+ public bool IsCharState() { return Index < 7; }
+ }
+
+ public const int kNumPosSlotBits = 6;
+ public const int kDicLogSizeMin = 0;
+
+ public const int kNumLenToPosStatesBits = 2; // it's for speed optimization
+ public const uint kNumLenToPosStates = 1 << kNumLenToPosStatesBits;
+
+ public const uint kMatchMinLen = 2;
+
+ public static uint GetLenToPosState(uint len)
+ {
+ len -= kMatchMinLen;
+ if (len < kNumLenToPosStates)
+ return len;
+ return kNumLenToPosStates - 1;
+ }
+
+ public const int kNumAlignBits = 4;
+ public const uint kAlignTableSize = 1 << kNumAlignBits;
+ public const uint kAlignMask = (kAlignTableSize - 1);
+
+ public const uint kStartPosModelIndex = 4;
+ public const uint kEndPosModelIndex = 14;
+ public const uint kNumPosModels = kEndPosModelIndex - kStartPosModelIndex;
+
+ public const uint kNumFullDistances = 1 << ((int)kEndPosModelIndex / 2);
+
+ public const uint kNumLitPosStatesBitsEncodingMax = 4;
+ public const uint kNumLitContextBitsMax = 8;
+
+ public const int kNumPosStatesBitsMax = 4;
+ public const uint kNumPosStatesMax = (1 << kNumPosStatesBitsMax);
+ public const int kNumPosStatesBitsEncodingMax = 4;
+ public const uint kNumPosStatesEncodingMax = (1 << kNumPosStatesBitsEncodingMax);
+
+ public const int kNumLowLenBits = 3;
+ public const int kNumMidLenBits = 3;
+ public const int kNumHighLenBits = 8;
+ public const uint kNumLowLenSymbols = 1 << kNumLowLenBits;
+ public const uint kNumMidLenSymbols = 1 << kNumMidLenBits;
+ public const uint kNumLenSymbols = kNumLowLenSymbols + kNumMidLenSymbols +
+ (1 << kNumHighLenBits);
+ public const uint kMatchMaxLen = kMatchMinLen + kNumLenSymbols - 1;
+ }
+}
diff --git a/SevenZip/Compression/LZMA/Decoder.LenDecoder.cs b/SevenZip/Compression/LZMA/Decoder.LenDecoder.cs
new file mode 100644
index 0000000..1e833b1
--- /dev/null
+++ b/SevenZip/Compression/LZMA/Decoder.LenDecoder.cs
@@ -0,0 +1,58 @@
+// Part of the LZMA SDK by Igor Pavlov
+
+namespace SevenZip.Compression.LZMA
+{
+ using RangeCoder;
+ public partial class Decoder
+ {
+ class LenDecoder
+ {
+ BitDecoder _choice = new BitDecoder();
+ BitDecoder _choice2 = new BitDecoder();
+ readonly BitTreeDecoder[] _lowCoder = new BitTreeDecoder[Base.kNumPosStatesMax];
+ readonly BitTreeDecoder[] _midCoder = new BitTreeDecoder[Base.kNumPosStatesMax];
+ readonly BitTreeDecoder _highCoder = new BitTreeDecoder(Base.kNumHighLenBits);
+ uint _numPosStates = 0;
+
+ public void Create(uint numPosStates)
+ {
+ for (uint posState = _numPosStates; posState < numPosStates; posState++)
+ {
+ _lowCoder[posState] = new BitTreeDecoder(Base.kNumLowLenBits);
+ _midCoder[posState] = new BitTreeDecoder(Base.kNumMidLenBits);
+ }
+ _numPosStates = numPosStates;
+ }
+
+ public void Init()
+ {
+ _choice.Init();
+ for (uint posState = 0; posState < _numPosStates; posState++)
+ {
+ _lowCoder[posState].Init();
+ _midCoder[posState].Init();
+ }
+ _choice2.Init();
+ _highCoder.Init();
+ }
+
+ public uint Decode(RangeCoder.Decoder rangeDecoder, uint posState)
+ {
+ if (_choice.Decode(rangeDecoder) == 0)
+ return _lowCoder[posState].Decode(rangeDecoder);
+ else
+ {
+ uint symbol = Base.kNumLowLenSymbols;
+ if (_choice2.Decode(rangeDecoder) == 0)
+ symbol += _midCoder[posState].Decode(rangeDecoder);
+ else
+ {
+ symbol += Base.kNumMidLenSymbols;
+ symbol += _highCoder.Decode(rangeDecoder);
+ }
+ return symbol;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SevenZip/Compression/LZMA/Decoder.LiteralDecoder.cs b/SevenZip/Compression/LZMA/Decoder.LiteralDecoder.cs
new file mode 100644
index 0000000..b70faaf
--- /dev/null
+++ b/SevenZip/Compression/LZMA/Decoder.LiteralDecoder.cs
@@ -0,0 +1,79 @@
+// Part of the LZMA SDK by Igor Pavlov
+
+namespace SevenZip.Compression.LZMA
+{
+ using RangeCoder;
+ public partial class Decoder
+ {
+ class LiteralDecoder
+ {
+ struct Decoder2
+ {
+ BitDecoder[] _decoders;
+ public void Create() { _decoders = new BitDecoder[0x300]; }
+ public void Init() { for (int i = 0; i < 0x300; i++) _decoders[i].Init(); }
+
+ public byte DecodeNormal(RangeCoder.Decoder rangeDecoder)
+ {
+ uint symbol = 1;
+ do
+ symbol = (symbol << 1) | _decoders[symbol].Decode(rangeDecoder);
+ while (symbol < 0x100);
+ return (byte)symbol;
+ }
+
+ public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, byte matchByte)
+ {
+ uint symbol = 1;
+ do
+ {
+ uint matchBit = (uint)(matchByte >> 7) & 1;
+ matchByte <<= 1;
+ uint bit = _decoders[((1 + matchBit) << 8) + symbol].Decode(rangeDecoder);
+ symbol = (symbol << 1) | bit;
+ if (matchBit != bit)
+ {
+ while (symbol < 0x100)
+ symbol = (symbol << 1) | _decoders[symbol].Decode(rangeDecoder);
+ break;
+ }
+ }
+ while (symbol < 0x100);
+ return (byte)symbol;
+ }
+ }
+
+ Decoder2[] _coders;
+ int _numPrevBits;
+ int _numPosBits;
+ uint _posMask;
+
+ public void Create(int numPosBits, int numPrevBits)
+ {
+ if (_coders != null && _numPrevBits == numPrevBits &&
+ _numPosBits == numPosBits)
+ return;
+ _numPosBits = numPosBits;
+ _posMask = ((uint)1 << numPosBits) - 1;
+ _numPrevBits = numPrevBits;
+ uint numStates = (uint)1 << (_numPrevBits + _numPosBits);
+ _coders = new Decoder2[numStates];
+ for (uint i = 0; i < numStates; i++)
+ _coders[i].Create();
+ }
+
+ public void Init()
+ {
+ uint numStates = (uint)1 << (_numPrevBits + _numPosBits);
+ for (uint i = 0; i < numStates; i++)
+ _coders[i].Init();
+ }
+
+ uint getState(uint pos, byte prevByte) { return ((pos & _posMask) << _numPrevBits) + (uint)(prevByte >> (8 - _numPrevBits)); }
+
+ public byte DecodeNormal(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte) { return _coders[getState(pos, prevByte)].DecodeNormal(rangeDecoder); }
+
+ public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte, byte matchByte) { return _coders[getState(pos, prevByte)].DecodeWithMatchByte(rangeDecoder, matchByte); }
+ }
+ }
+}
diff --git a/SevenZip/Compression/LZMA/Decoder.cs b/SevenZip/Compression/LZMA/Decoder.cs
new file mode 100644
index 0000000..9f8db4b
--- /dev/null
+++ b/SevenZip/Compression/LZMA/Decoder.cs
@@ -0,0 +1,238 @@
+// Part of the LZMA SDK by Igor Pavlov
+
+using System;
+
+namespace SevenZip.Compression.LZMA
+{
+ using RangeCoder;
+
+ public partial class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Stream
+ {
+ readonly LZ.OutWindow _outWindow = new LZ.OutWindow();
+ readonly RangeCoder.Decoder _rangeDecoder = new RangeCoder.Decoder();
+
+ readonly BitDecoder[] _isMatchDecoders = new BitDecoder[Base.kNumStates << Base.kNumPosStatesBitsMax];
+ readonly BitDecoder[] _isRepDecoders = new BitDecoder[Base.kNumStates];
+ readonly BitDecoder[] _isRepG0Decoders = new BitDecoder[Base.kNumStates];
+ readonly BitDecoder[] _isRepG1Decoders = new BitDecoder[Base.kNumStates];
+ readonly BitDecoder[] _isRepG2Decoders = new BitDecoder[Base.kNumStates];
+ readonly BitDecoder[] _isRep0LongDecoders = new BitDecoder[Base.kNumStates << Base.kNumPosStatesBitsMax];
+
+ readonly BitTreeDecoder[] _posSlotDecoder = new BitTreeDecoder[Base.kNumLenToPosStates];
+ readonly BitDecoder[] _posDecoders = new BitDecoder[Base.kNumFullDistances - Base.kEndPosModelIndex];
+ readonly BitTreeDecoder _posAlignDecoder = new BitTreeDecoder(Base.kNumAlignBits);
+
+ readonly LenDecoder _lenDecoder = new LenDecoder();
+ readonly LenDecoder _repLenDecoder = new LenDecoder();
+ readonly LiteralDecoder _literalDecoder = new LiteralDecoder();
+
+ uint _dictionarySize;
+ uint _dictionarySizeCheck;
+
+ uint _posStateMask;
+
+ public Decoder()
+ {
+ _dictionarySize = 0xFFFFFFFF;
+ for (int i = 0; i < Base.kNumLenToPosStates; i++)
+ _posSlotDecoder[i] = new BitTreeDecoder(Base.kNumPosSlotBits);
+ }
+
+ void setDictionarySize(uint dictionarySize)
+ {
+ if (_dictionarySize != dictionarySize)
+ {
+ _dictionarySize = dictionarySize;
+ _dictionarySizeCheck = Math.Max(_dictionarySize, 1);
+ uint blockSize = Math.Max(_dictionarySizeCheck, (1 << 12));
+ _outWindow.Create(blockSize);
+ }
+ }
+
+ void setLiteralProperties(int lp, int lc)
+ {
+ if (lp > 8)
+ throw new InvalidParamException();
+ if (lc > 8)
+ throw new InvalidParamException();
+ _literalDecoder.Create(lp, lc);
+ }
+
+ void setPosBitsProperties(int pb)
+ {
+ if (pb > Base.kNumPosStatesBitsMax)
+ throw new InvalidParamException();
+ uint numPosStates = (uint)1 << pb;
+ _lenDecoder.Create(numPosStates);
+ _repLenDecoder.Create(numPosStates);
+ _posStateMask = numPosStates - 1;
+ }
+
+ void init(System.IO.Stream inStream, System.IO.Stream outStream)
+ {
+ _rangeDecoder.Init(inStream);
+ _outWindow.Init(outStream, false);
+
+ uint i;
+ for (i = 0; i < Base.kNumStates; i++)
+ {
+ for (uint j = 0; j <= _posStateMask; j++)
+ {
+ uint index = (i << Base.kNumPosStatesBitsMax) + j;
+ _isMatchDecoders[index].Init();
+ _isRep0LongDecoders[index].Init();
+ }
+ _isRepDecoders[i].Init();
+ _isRepG0Decoders[i].Init();
+ _isRepG1Decoders[i].Init();
+ _isRepG2Decoders[i].Init();
+ }
+
+ _literalDecoder.Init();
+ for (i = 0; i < Base.kNumLenToPosStates; i++)
+ _posSlotDecoder[i].Init();
+ for (i = 0; i < Base.kNumFullDistances - Base.kEndPosModelIndex; i++)
+ _posDecoders[i].Init();
+
+ _lenDecoder.Init();
+ _repLenDecoder.Init();
+ _posAlignDecoder.Init();
+ }
+
+ public void Code(System.IO.Stream inStream, System.IO.Stream outStream,
+ long inSize, long outSize, ICodeProgress progress)
+ {
+ init(inStream, outStream);
+
+ Base.State state = new Base.State();
+ state.Init();
+ uint rep0 = 0, rep1 = 0, rep2 = 0, rep3 = 0;
+
+ ulong nowPos64 = 0;
+ ulong outSize64 = (ulong)outSize;
+ if (nowPos64 < outSize64)
+ {
+ if (_isMatchDecoders[state.Index << Base.kNumPosStatesBitsMax].Decode(_rangeDecoder) != 0)
+ throw new DataErrorException();
+ state.UpdateChar();
+ byte b = _literalDecoder.DecodeNormal(_rangeDecoder, 0, 0);
+ _outWindow.PutByte(b);
+ nowPos64++;
+ }
+ while (nowPos64 < outSize64)
+ {
+ // while(nowPos64 < next)
+ {
+ uint posState = (uint)nowPos64 & _posStateMask;
+ if (_isMatchDecoders[(state.Index << Base.kNumPosStatesBitsMax) + posState].Decode(_rangeDecoder) == 0)
+ {
+ byte b;
+ byte prevByte = _outWindow.GetByte(0);
+ if (!state.IsCharState())
+ b = _literalDecoder.DecodeWithMatchByte(_rangeDecoder,
+ (uint)nowPos64, prevByte, _outWindow.GetByte(rep0));
+ else
+ b = _literalDecoder.DecodeNormal(_rangeDecoder, (uint)nowPos64, prevByte);
+ _outWindow.PutByte(b);
+ state.UpdateChar();
+ nowPos64++;
+ }
+ else
+ {
+ uint len;
+ if (_isRepDecoders[state.Index].Decode(_rangeDecoder) == 1)
+ {
+ if (_isRepG0Decoders[state.Index].Decode(_rangeDecoder) == 0)
+ {
+ if (_isRep0LongDecoders[(state.Index << Base.kNumPosStatesBitsMax) + posState].Decode(_rangeDecoder) == 0)
+ {
+ state.UpdateShortRep();
+ _outWindow.PutByte(_outWindow.GetByte(rep0));
+ nowPos64++;
+ continue;
+ }
+ }
+ else
+ {
+ uint distance;
+ if (_isRepG1Decoders[state.Index].Decode(_rangeDecoder) == 0)
+ {
+ distance = rep1;
+ }
+ else
+ {
+ if (_isRepG2Decoders[state.Index].Decode(_rangeDecoder) == 0)
+ distance = rep2;
+ else
+ {
+ distance = rep3;
+ rep3 = rep2;
+ }
+ rep2 = rep1;
+ }
+ rep1 = rep0;
+ rep0 = distance;
+ }
+ len = _repLenDecoder.Decode(_rangeDecoder, posState) + Base.kMatchMinLen;
+ state.UpdateRep();
+ }
+ else
+ {
+ rep3 = rep2;
+ rep2 = rep1;
+ rep1 = rep0;
+ len = Base.kMatchMinLen + _lenDecoder.Decode(_rangeDecoder, posState);
+ state.UpdateMatch();
+ uint posSlot = _posSlotDecoder[Base.GetLenToPosState(len)].Decode(_rangeDecoder);
+ if (posSlot >= Base.kStartPosModelIndex)
+ {
+ int numDirectBits = (int)((posSlot >> 1) - 1);
+ rep0 = ((2 | (posSlot & 1)) << numDirectBits);
+ if (posSlot < Base.kEndPosModelIndex)
+ rep0 += BitTreeDecoder.ReverseDecode(_posDecoders,
+ rep0 - posSlot - 1, _rangeDecoder, numDirectBits);
+ else
+ {
+ rep0 += (_rangeDecoder.DecodeDirectBits(
+ numDirectBits - Base.kNumAlignBits) << Base.kNumAlignBits);
+ rep0 += _posAlignDecoder.ReverseDecode(_rangeDecoder);
+ }
+ }
+ else
+ rep0 = posSlot;
+ }
+ if (rep0 >= _outWindow.TrainSize + nowPos64 || rep0 >= _dictionarySizeCheck)
+ {
+ if (rep0 == 0xFFFFFFFF)
+ break;
+ throw new DataErrorException();
+ }
+ _outWindow.CopyBlock(rep0, len);
+ nowPos64 += len;
+ }
+ }
+ }
+ _outWindow.Flush();
+ _outWindow.ReleaseStream();
+ _rangeDecoder.ReleaseStream();
+ }
+
+ public void SetDecoderProperties(byte[] properties)
+ {
+ if (properties.Length < 5)
+ throw new InvalidParamException();
+ int lc = properties[0] % 9;
+ int remainder = properties[0] / 9;
+ int lp = remainder % 5;
+ int pb = remainder / 5;
+ if (pb > Base.kNumPosStatesBitsMax)
+ throw new InvalidParamException();
+ uint dictionarySize = 0;
+ for (int i = 0; i < 4; i++)
+ dictionarySize += ((uint)(properties[1 + i])) << (i * 8);
+ setDictionarySize(dictionarySize);
+ setLiteralProperties(lp, lc);
+ setPosBitsProperties(pb);
+ }
+ }
+}
diff --git a/SevenZip/Compression/LZMA/Encoder.LenEncoder.cs b/SevenZip/Compression/LZMA/Encoder.LenEncoder.cs
new file mode 100644
index 0000000..3d8a6c6
--- /dev/null
+++ b/SevenZip/Compression/LZMA/Encoder.LenEncoder.cs
@@ -0,0 +1,85 @@
+// Part of the LZMA SDK by Igor Pavlov
+
+namespace SevenZip.Compression.LZMA
+{
+ using RangeCoder;
+ public partial class Encoder
+ {
+ class LenEncoder
+ {
+ BitEncoder _choice = new BitEncoder();
+ BitEncoder _choice2 = new BitEncoder();
+ readonly BitTreeEncoder[] _lowCoder = new BitTreeEncoder[Base.kNumPosStatesEncodingMax];
+ readonly BitTreeEncoder[] _midCoder = new BitTreeEncoder[Base.kNumPosStatesEncodingMax];
+ readonly BitTreeEncoder _highCoder = new BitTreeEncoder(Base.kNumHighLenBits);
+
+ public LenEncoder()
+ {
+ for (uint posState = 0; posState < Base.kNumPosStatesEncodingMax; posState++)
+ {
+ _lowCoder[posState] = new BitTreeEncoder(Base.kNumLowLenBits);
+ _midCoder[posState] = new BitTreeEncoder(Base.kNumMidLenBits);
+ }
+ }
+
+ public void Init(uint numPosStates)
+ {
+ _choice.Init();
+ _choice2.Init();
+ for (uint posState = 0; posState < numPosStates; posState++)
+ {
+ _lowCoder[posState].Init();
+ _midCoder[posState].Init();
+ }
+ _highCoder.Init();
+ }
+
+ public void Encode(RangeCoder.Encoder rangeEncoder, uint symbol, uint posState)
+ {
+ if (symbol < Base.kNumLowLenSymbols)
+ {
+ _choice.Encode(rangeEncoder, 0);
+ _lowCoder[posState].Encode(rangeEncoder, symbol);
+ }
+ else
+ {
+ symbol -= Base.kNumLowLenSymbols;
+ _choice.Encode(rangeEncoder, 1);
+ if (symbol < Base.kNumMidLenSymbols)
+ {
+ _choice2.Encode(rangeEncoder, 0);
+ _midCoder[posState].Encode(rangeEncoder, symbol);
+ }
+ else
+ {
+ _choice2.Encode(rangeEncoder, 1);
+ _highCoder.Encode(rangeEncoder, symbol - Base.kNumMidLenSymbols);
+ }
+ }
+ }
+
+ public void SetPrices(uint posState, uint numSymbols, uint[] prices, uint st)
+ {
+ uint a0 = _choice.GetPrice0();
+ uint a1 = _choice.GetPrice1();
+ uint b0 = a1 + _choice2.GetPrice0();
+ uint b1 = a1 + _choice2.GetPrice1();
+ uint i;
+ for (i = 0; i < Base.kNumLowLenSymbols; i++)
+ {
+ if (i >= numSymbols)
+ return;
+ prices[st + i] = a0 + _lowCoder[posState].GetPrice(i);
+ }
+ for (; i < Base.kNumLowLenSymbols + Base.kNumMidLenSymbols; i++)
+ {
+ if (i >= numSymbols)
+ return;
+ prices[st + i] = b0 + _midCoder[posState].GetPrice(i - Base.kNumLowLenSymbols);
+ }
+ for (; i < numSymbols; i++)
+ prices[st + i] = b1 + _highCoder.GetPrice(i - Base.kNumLowLenSymbols - Base.kNumMidLenSymbols);
+ }
+ };
+ }
+}
diff --git a/SevenZip/Compression/LZMA/Encoder.LenPriceTableEncoder.cs b/SevenZip/Compression/LZMA/Encoder.LenPriceTableEncoder.cs
new file mode 100644
index 0000000..bf45794
--- /dev/null
+++ b/SevenZip/Compression/LZMA/Encoder.LenPriceTableEncoder.cs
@@ -0,0 +1,40 @@
+// Part of the LZMA SDK by Igor Pavlov
+
+namespace SevenZip.Compression.LZMA
+{
+ public partial class Encoder
+ {
+ class LenPriceTableEncoder : LenEncoder
+ {
+ readonly uint[] _prices = new uint[Base.kNumLenSymbols << Base.kNumPosStatesBitsEncodingMax];
+ uint _tableSize;
+ readonly uint[] _counters = new uint[Base.kNumPosStatesEncodingMax];
+
+ public void SetTableSize(uint tableSize) { _tableSize = tableSize; }
+
+ public uint GetPrice(uint symbol, uint posState)
+ {
+ return _prices[posState * Base.kNumLenSymbols + symbol];
+ }
+
+ void updateTable(uint posState)
+ {
+ SetPrices(posState, _tableSize, _prices, posState * Base.kNumLenSymbols);
+ _counters[posState] = _tableSize;
+ }
+
+ public void UpdateTables(uint numPosStates)
+ {
+ for (uint posState = 0; posState < numPosStates; posState++)
+ updateTable(posState);
+ }
+
+ public new void Encode(RangeCoder.Encoder rangeEncoder, uint symbol, uint posState)
+ {
+ base.Encode(rangeEncoder, symbol, posState);
+ if (--_counters[posState] == 0)
+ updateTable(posState);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SevenZip/Compression/LZMA/Encoder.LiteralEncoder.cs b/SevenZip/Compression/LZMA/Encoder.LiteralEncoder.cs
new file mode 100644
index 0000000..d906e95
--- /dev/null
+++ b/SevenZip/Compression/LZMA/Encoder.LiteralEncoder.cs
@@ -0,0 +1,107 @@
+// Part of the LZMA SDK by Igor Pavlov
+
+namespace SevenZip.Compression.LZMA
+{
+ using RangeCoder;
+ public partial class Encoder
+ {
+ class LiteralEncoder
+ {
+ public struct Encoder2
+ {
+ BitEncoder[] _encoders;
+
+ public void Create() { _encoders = new BitEncoder[0x300]; }
+
+ public void Init() { for (int i = 0; i < 0x300; i++) _encoders[i].Init(); }
+
+ public void Encode(RangeCoder.Encoder rangeEncoder, byte symbol)
+ {
+ uint context = 1;
+ for (int i = 7; i >= 0; i--)
+ {
+ uint bit = (uint)((symbol >> i) & 1);
+ _encoders[context].Encode(rangeEncoder, bit);
+ context = (context << 1) | bit;
+ }
+ }
+
+ public void EncodeMatched(RangeCoder.Encoder rangeEncoder, byte matchByte, byte symbol)
+ {
+ uint context = 1;
+ bool same = true;
+ for (int i = 7; i >= 0; i--)
+ {
+ uint bit = (uint)((symbol >> i) & 1);
+ uint state = context;
+ if (same)
+ {
+ uint matchBit = (uint)((matchByte >> i) & 1);
+ state += ((1 + matchBit) << 8);
+ same = (matchBit == bit);
+ }
+ _encoders[state].Encode(rangeEncoder, bit);
+ context = (context << 1) | bit;
+ }
+ }
+
+ public uint GetPrice(bool matchMode, byte matchByte, byte symbol)
+ {
+ uint price = 0;
+ uint context = 1;
+ int i = 7;
+ if (matchMode)
+ {
+ for (; i >= 0; i--)
+ {
+ uint matchBit = (uint)(matchByte >> i) & 1;
+ uint bit = (uint)(symbol >> i) & 1;
+ price += _encoders[((1 + matchBit) << 8) + context].GetPrice(bit);
+ context = (context << 1) | bit;
+ if (matchBit != bit)
+ {
+ i--;
+ break;
+ }
+ }
+ }
+ for (; i >= 0; i--)
+ {
+ uint bit = (uint)(symbol >> i) & 1;
+ price += _encoders[context].GetPrice(bit);
+ context = (context << 1) | bit;
+ }
+ return price;
+ }
+ }
+
+ Encoder2[] _coders;
+ int _numPrevBits;
+ int _numPosBits;
+ uint _posMask;
+
+ public void Create(int numPosBits, int numPrevBits)
+ {
+ if (_coders != null && _numPrevBits == numPrevBits && _numPosBits == numPosBits)
+ return;
+ _numPosBits = numPosBits;
+ _posMask = ((uint)1 << numPosBits) - 1;
+ _numPrevBits = numPrevBits;
+ uint numStates = (uint)1 << (_numPrevBits + _numPosBits);
+ _coders = new Encoder2[numStates];
+ for (uint i = 0; i < numStates; i++)
+ _coders[i].Create();
+ }
+
+ public void Init()
+ {
+ uint numStates = (uint)1 << (_numPrevBits + _numPosBits);
+ for (uint i = 0; i < numStates; i++)
+ _coders[i].Init();
+ }
+
+ public Encoder2 GetSubCoder(uint pos, byte prevByte)
+ { return _coders[((pos & _posMask) << _numPrevBits) + (uint)(prevByte >> (8 - _numPrevBits))]; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SevenZip/Compression/LZMA/Encoder.Optimal.cs b/SevenZip/Compression/LZMA/Encoder.Optimal.cs
new file mode 100644
index 0000000..8d01d9a
--- /dev/null
+++ b/SevenZip/Compression/LZMA/Encoder.Optimal.cs
@@ -0,0 +1,31 @@
+// Part of the LZMA SDK by Igor Pavlov
+
+namespace SevenZip.Compression.LZMA
+{
+ public partial class Encoder
+ {
+ class Optimal
+ {
+ public Base.State State;
+
+ public bool Prev1IsChar;
+ public bool Prev2;
+
+ public uint PosPrev2;
+ public uint BackPrev2;
+
+ public uint Price;
+ public uint PosPrev;
+ public uint BackPrev;
+
+ public uint Backs0;
+ public uint Backs1;
+ public uint Backs2;
+ public uint Backs3;
+
+ public void MakeAsChar() { BackPrev = 0xFFFFFFFF; Prev1IsChar = false; }
+ public void MakeAsShortRep() { BackPrev = 0; ; Prev1IsChar = false; }
+ public bool IsShortRep() { return (BackPrev == 0); }
+ };
+ }
+}
\ No newline at end of file
diff --git a/SevenZip/Compression/LZMA/Encoder.cs b/SevenZip/Compression/LZMA/Encoder.cs
new file mode 100644
index 0000000..2a24a3e
--- /dev/null
+++ b/SevenZip/Compression/LZMA/Encoder.cs
@@ -0,0 +1,1207 @@
+// Part of the LZMA SDK by Igor Pavlov
+
+using System;
+
+namespace SevenZip.Compression.LZMA
+{
+ using RangeCoder;
+
+ public partial class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties
+ {
+ enum EMatchFinderType
+ {
+ BT2,
+ BT4,
+ };
+
+#pragma warning disable IDE1006 // Naming Styles
+ const uint kIfinityPrice = 0xFFFFFFF;
+ const int kDefaultDictionaryLogSize = 22;
+ const uint kNumFastBytesDefault = 0x20;
+ const uint kNumOpts = 1 << 12;
+ const int kPropSize = 5;
+ static readonly string[] kMatchFinderIDs =
+ {
+ "BT2",
+ "BT4",
+ };
+#pragma warning restore IDE1006 // Naming Styles
+
+ static readonly byte[] _fastPos = new byte[1 << 11];
+
+ static Encoder()
+ {
+ const byte kFastSlots = 22;
+ int c = 2;
+ _fastPos[0] = 0;
+ _fastPos[1] = 1;
+ for (byte slotFast = 2; slotFast < kFastSlots; slotFast++)
+ {
+ uint k = ((uint)1 << ((slotFast >> 1) - 1));
+ for (uint j = 0; j < k; j++, c++)
+ _fastPos[c] = slotFast;
+ }
+ }
+
+ static uint getPosSlot(uint pos)
+ {
+ if (pos < (1 << 11))
+ return _fastPos[pos];
+ if (pos < (1 << 21))
+ return (uint)(_fastPos[pos >> 10] + 20);
+ return (uint)(_fastPos[pos >> 20] + 40);
+ }
+
+ static uint getPosSlot2(uint pos)
+ {
+ if (pos < (1 << 17))
+ return (uint)(_fastPos[pos >> 6] + 12);
+ if (pos < (1 << 27))
+ return (uint)(_fastPos[pos >> 16] + 32);
+ return (uint)(_fastPos[pos >> 26] + 52);
+ }
+
+ Base.State _state = new Base.State();
+ byte _previousByte;
+ readonly uint[] _repDistances = new uint[Base.kNumRepDistances];
+
+ void baseInit()
+ {
+ _state.Init();
+ _previousByte = 0;
+ for (uint i = 0; i < Base.kNumRepDistances; i++)
+ _repDistances[i] = 0;
+ }
+
+ readonly Optimal[] _optimum = new Optimal[kNumOpts];
+ LZ.IMatchFinder _matchFinder = null;
+ readonly RangeCoder.Encoder _rangeEncoder = new RangeCoder.Encoder();
+ readonly BitEncoder[] _isMatch = new BitEncoder[Base.kNumStates << Base.kNumPosStatesBitsMax];
+ readonly BitEncoder[] _isRep = new BitEncoder[Base.kNumStates];
+ readonly BitEncoder[] _isRepG0 = new BitEncoder[Base.kNumStates];
+ readonly BitEncoder[] _isRepG1 = new BitEncoder[Base.kNumStates];
+ readonly BitEncoder[] _isRepG2 = new BitEncoder[Base.kNumStates];
+ readonly BitEncoder[] _isRep0Long = new BitEncoder[Base.kNumStates << Base.kNumPosStatesBitsMax];
+ readonly BitTreeEncoder[] _posSlotEncoder = new BitTreeEncoder[Base.kNumLenToPosStates];
+ readonly BitEncoder[] _posEncoders = new BitEncoder[Base.kNumFullDistances - Base.kEndPosModelIndex];
+ readonly BitTreeEncoder _posAlignEncoder = new BitTreeEncoder(Base.kNumAlignBits);
+ readonly LenPriceTableEncoder _lenEncoder = new LenPriceTableEncoder();
+ readonly LenPriceTableEncoder _repMatchLenEncoder = new LenPriceTableEncoder();
+ readonly LiteralEncoder _literalEncoder = new LiteralEncoder();
+ readonly uint[] _matchDistances = new uint[Base.kMatchMaxLen * 2 + 2];
+
+ uint _numFastBytes = kNumFastBytesDefault;
+ uint _longestMatchLength;
+ uint _numDistancePairs;
+
+ uint _additionalOffset;
+
+ uint _optimumEndIndex;
+ uint _optimumCurrentIndex;
+
+ bool _longestMatchWasFound;
+ readonly uint[] _posSlotPrices = new uint[1 << (Base.kNumPosSlotBits + Base.kNumLenToPosStatesBits)];
+ readonly uint[] _distancesPrices = new uint[Base.kNumFullDistances << Base.kNumLenToPosStatesBits];
+ readonly uint[] _alignPrices = new uint[Base.kAlignTableSize];
+ uint _alignPriceCount;
+
+ uint _distTableSize = (kDefaultDictionaryLogSize * 2);
+
+ int _posStateBits = 2;
+ uint _posStateMask = (4 - 1);
+ int _numLiteralPosStateBits = 0;
+ int _numLiteralContextBits = 3;
+
+ uint _dictionarySize = (1 << kDefaultDictionaryLogSize);
+ uint _dictionarySizePrev = 0xFFFFFFFF;
+ uint _numFastBytesPrev = 0xFFFFFFFF;
+
+ long _nowPos64;
+ bool _finished;
+ System.IO.Stream _inStream;
+
+ EMatchFinderType _matchFinderType = EMatchFinderType.BT4;
+ bool _writeEndMark = false;
+
+ bool _needReleaseMFStream;
+
+ void create()
+ {
+ if (_matchFinder == null)
+ {
+ LZ.BinTree bt = new LZ.BinTree();
+ int numHashBytes = 4;
+ if (_matchFinderType == EMatchFinderType.BT2)
+ numHashBytes = 2;
+ bt.SetType(numHashBytes);
+ _matchFinder = bt;
+ }
+ _literalEncoder.Create(_numLiteralPosStateBits, _numLiteralContextBits);
+
+ if (_dictionarySize == _dictionarySizePrev && _numFastBytesPrev == _numFastBytes)
+ return;
+ _matchFinder.Create(_dictionarySize, kNumOpts, _numFastBytes, Base.kMatchMaxLen + 1);
+ _dictionarySizePrev = _dictionarySize;
+ _numFastBytesPrev = _numFastBytes;
+ }
+
+ public Encoder()
+ {
+ for (int i = 0; i < kNumOpts; i++)
+ _optimum[i] = new Optimal();
+ for (int i = 0; i < Base.kNumLenToPosStates; i++)
+ _posSlotEncoder[i] = new BitTreeEncoder(Base.kNumPosSlotBits);
+ }
+
+ void setWriteEndMarkerMode(bool writeEndMarker)
+ {
+ _writeEndMark = writeEndMarker;
+ }
+
+ void init()
+ {
+ baseInit();
+ _rangeEncoder.Init();
+
+ uint i;
+ for (i = 0; i < Base.kNumStates; i++)
+ {
+ for (uint j = 0; j <= _posStateMask; j++)
+ {
+ uint complexState = (i << Base.kNumPosStatesBitsMax) + j;
+ _isMatch[complexState].Init();
+ _isRep0Long[complexState].Init();
+ }
+ _isRep[i].Init();
+ _isRepG0[i].Init();
+ _isRepG1[i].Init();
+ _isRepG2[i].Init();
+ }
+ _literalEncoder.Init();
+ for (i = 0; i < Base.kNumLenToPosStates; i++)
+ _posSlotEncoder[i].Init();
+ for (i = 0; i < Base.kNumFullDistances - Base.kEndPosModelIndex; i++)
+ _posEncoders[i].Init();
+
+ _lenEncoder.Init((uint)1 << _posStateBits);
+ _repMatchLenEncoder.Init((uint)1 << _posStateBits);
+
+ _posAlignEncoder.Init();
+
+ _longestMatchWasFound = false;
+ _optimumEndIndex = 0;
+ _optimumCurrentIndex = 0;
+ _additionalOffset = 0;
+ }
+
+ void readMatchDistances(out uint lenRes, out uint numDistancePairs)
+ {
+ lenRes = 0;
+ numDistancePairs = _matchFinder.GetMatches(_matchDistances);
+ if (numDistancePairs > 0)
+ {
+ lenRes = _matchDistances[numDistancePairs - 2];
+ if (lenRes == _numFastBytes)
+ lenRes += _matchFinder.GetMatchLen((int)lenRes - 1, _matchDistances[numDistancePairs - 1],
+ Base.kMatchMaxLen - lenRes);
+ }
+ _additionalOffset++;
+ }
+
+
+ void movePos(uint num)
+ {
+ if (num > 0)
+ {
+ _matchFinder.Skip(num);
+ _additionalOffset += num;
+ }
+ }
+
+ uint getRepLen1Price(Base.State state, uint posState)
+ {
+ return _isRepG0[state.Index].GetPrice0() +
+ _isRep0Long[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice0();
+ }
+
+ uint getPureRepPrice(uint repIndex, Base.State state, uint posState)
+ {
+ uint price;
+ if (repIndex == 0)
+ {
+ price = _isRepG0[state.Index].GetPrice0();
+ price += _isRep0Long[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice1();
+ }
+ else
+ {
+ price = _isRepG0[state.Index].GetPrice1();
+ if (repIndex == 1)
+ price += _isRepG1[state.Index].GetPrice0();
+ else
+ {
+ price += _isRepG1[state.Index].GetPrice1();
+ price += _isRepG2[state.Index].GetPrice(repIndex - 2);
+ }
+ }
+ return price;
+ }
+
+ uint getRepPrice(uint repIndex, uint len, Base.State state, uint posState)
+ {
+ uint price = _repMatchLenEncoder.GetPrice(len - Base.kMatchMinLen, posState);
+ return price + getPureRepPrice(repIndex, state, posState);
+ }
+
+ uint getPosLenPrice(uint pos, uint len, uint posState)
+ {
+ uint price;
+ uint lenToPosState = Base.GetLenToPosState(len);
+ if (pos < Base.kNumFullDistances)
+ price = _distancesPrices[(lenToPosState * Base.kNumFullDistances) + pos];
+ else
+ price = _posSlotPrices[(lenToPosState << Base.kNumPosSlotBits) + getPosSlot2(pos)] +
+ _alignPrices[pos & Base.kAlignMask];
+ return price + _lenEncoder.GetPrice(len - Base.kMatchMinLen, posState);
+ }
+
+ uint backward(out uint backRes, uint cur)
+ {
+ _optimumEndIndex = cur;
+ uint posMem = _optimum[cur].PosPrev;
+ uint backMem = _optimum[cur].BackPrev;
+ do
+ {
+ if (_optimum[cur].Prev1IsChar)
+ {
+ _optimum[posMem].MakeAsChar();
+ _optimum[posMem].PosPrev = posMem - 1;
+ if (_optimum[cur].Prev2)
+ {
+ _optimum[posMem - 1].Prev1IsChar = false;
+ _optimum[posMem - 1].PosPrev = _optimum[cur].PosPrev2;
+ _optimum[posMem - 1].BackPrev = _optimum[cur].BackPrev2;
+ }
+ }
+ uint posPrev = posMem;
+ uint backCur = backMem;
+
+ backMem = _optimum[posPrev].BackPrev;
+ posMem = _optimum[posPrev].PosPrev;
+
+ _optimum[posPrev].BackPrev = backCur;
+ _optimum[posPrev].PosPrev = cur;
+ cur = posPrev;
+ }
+ while (cur > 0);
+ backRes = _optimum[0].BackPrev;
+ _optimumCurrentIndex = _optimum[0].PosPrev;
+ return _optimumCurrentIndex;
+ }
+
+ readonly uint[] _reps = new uint[Base.kNumRepDistances];
+ readonly uint[] _repLens = new uint[Base.kNumRepDistances];
+
+ uint getOptimum(uint position, out uint backRes)
+ {
+ if (_optimumEndIndex != _optimumCurrentIndex)
+ {
+ uint lenRes = _optimum[_optimumCurrentIndex].PosPrev - _optimumCurrentIndex;
+ backRes = _optimum[_optimumCurrentIndex].BackPrev;
+ _optimumCurrentIndex = _optimum[_optimumCurrentIndex].PosPrev;
+ return lenRes;
+ }
+ _optimumCurrentIndex = _optimumEndIndex = 0;
+
+ uint lenMain, numDistancePairs;
+ if (!_longestMatchWasFound)
+ {
+ readMatchDistances(out lenMain, out numDistancePairs);
+ }
+ else
+ {
+ lenMain = _longestMatchLength;
+ numDistancePairs = _numDistancePairs;
+ _longestMatchWasFound = false;
+ }
+
+ uint numAvailableBytes = _matchFinder.GetNumAvailableBytes() + 1;
+ if (numAvailableBytes < 2)
+ {
+ backRes = 0xFFFFFFFF;
+ return 1;
+ }
+
+ uint repMaxIndex = 0;
+ uint i;
+ for (i = 0; i < Base.kNumRepDistances; i++)
+ {
+ _reps[i] = _repDistances[i];
+ _repLens[i] = _matchFinder.GetMatchLen(0 - 1, _reps[i], Base.kMatchMaxLen);
+ if (_repLens[i] > _repLens[repMaxIndex])
+ repMaxIndex = i;
+ }
+ if (_repLens[repMaxIndex] >= _numFastBytes)
+ {
+ backRes = repMaxIndex;
+ uint lenRes = _repLens[repMaxIndex];
+ movePos(lenRes - 1);
+ return lenRes;
+ }
+
+ if (lenMain >= _numFastBytes)
+ {
+ backRes = _matchDistances[numDistancePairs - 1] + Base.kNumRepDistances;
+ movePos(lenMain - 1);
+ return lenMain;
+ }
+
+ byte currentByte = _matchFinder.GetIndexByte(0 - 1);
+ byte matchByte = _matchFinder.GetIndexByte((int)(0 - _repDistances[0] - 1 - 1));
+
+ if (lenMain < 2 && currentByte != matchByte && _repLens[repMaxIndex] < 2)
+ {
+ backRes = (uint)0xFFFFFFFF;
+ return 1;
+ }
+
+ _optimum[0].State = _state;
+
+ uint posState = (position & _posStateMask);
+
+ _optimum[1].Price = _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice0() +
+ _literalEncoder.GetSubCoder(position, _previousByte).GetPrice(!_state.IsCharState(), matchByte, currentByte);
+ _optimum[1].MakeAsChar();
+
+ uint matchPrice = _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice1();
+ uint repMatchPrice = matchPrice + _isRep[_state.Index].GetPrice1();
+
+ if (matchByte == currentByte)
+ {
+ uint shortRepPrice = repMatchPrice + getRepLen1Price(_state, posState);
+ if (shortRepPrice < _optimum[1].Price)
+ {
+ _optimum[1].Price = shortRepPrice;
+ _optimum[1].MakeAsShortRep();
+ }
+ }
+
+ uint lenEnd = ((lenMain >= _repLens[repMaxIndex]) ? lenMain : _repLens[repMaxIndex]);
+
+ if(lenEnd < 2)
+ {
+ backRes = _optimum[1].BackPrev;
+ return 1;
+ }
+
+ _optimum[1].PosPrev = 0;
+
+ _optimum[0].Backs0 = _reps[0];
+ _optimum[0].Backs1 = _reps[1];
+ _optimum[0].Backs2 = _reps[2];
+ _optimum[0].Backs3 = _reps[3];
+
+ uint len = lenEnd;
+ do
+ _optimum[len--].Price = kIfinityPrice;
+ while (len >= 2);
+
+ for (i = 0; i < Base.kNumRepDistances; i++)
+ {
+ uint repLen = _repLens[i];
+ if (repLen < 2)
+ continue;
+ uint price = repMatchPrice + getPureRepPrice(i, _state, posState);
+ do
+ {
+ uint curAndLenPrice = price + _repMatchLenEncoder.GetPrice(repLen - 2, posState);
+ Optimal optimum = _optimum[repLen];
+ if (curAndLenPrice < optimum.Price)
+ {
+ optimum.Price = curAndLenPrice;
+ optimum.PosPrev = 0;
+ optimum.BackPrev = i;
+ optimum.Prev1IsChar = false;
+ }
+ }
+ while (--repLen >= 2);
+ }
+
+ uint normalMatchPrice = matchPrice + _isRep[_state.Index].GetPrice0();
+
+ len = ((_repLens[0] >= 2) ? _repLens[0] + 1 : 2);
+ if (len <= lenMain)
+ {
+ uint offs = 0;
+ while (len > _matchDistances[offs])
+ offs += 2;
+ for (; ; len++)
+ {
+ uint distance = _matchDistances[offs + 1];
+ uint curAndLenPrice = normalMatchPrice + getPosLenPrice(distance, len, posState);
+ Optimal optimum = _optimum[len];
+ if (curAndLenPrice < optimum.Price)
+ {
+ optimum.Price = curAndLenPrice;
+ optimum.PosPrev = 0;
+ optimum.BackPrev = distance + Base.kNumRepDistances;
+ optimum.Prev1IsChar = false;
+ }
+ if (len == _matchDistances[offs])
+ {
+ offs += 2;
+ if (offs == numDistancePairs)
+ break;
+ }
+ }
+ }
+
+ uint cur = 0;
+
+ while (true)
+ {
+ cur++;
+ if (cur == lenEnd)
+ return backward(out backRes, cur);
+ readMatchDistances(out uint newLen, out numDistancePairs);
+ if (newLen >= _numFastBytes)
+ {
+ _numDistancePairs = numDistancePairs;
+ _longestMatchLength = newLen;
+ _longestMatchWasFound = true;
+ return backward(out backRes, cur);
+ }
+ position++;
+ uint posPrev = _optimum[cur].PosPrev;
+ Base.State state;
+ if (_optimum[cur].Prev1IsChar)
+ {
+ posPrev--;
+ if (_optimum[cur].Prev2)
+ {
+ state = _optimum[_optimum[cur].PosPrev2].State;
+ if (_optimum[cur].BackPrev2 < Base.kNumRepDistances)
+ state.UpdateRep();
+ else
+ state.UpdateMatch();
+ }
+ else
+ state = _optimum[posPrev].State;
+ state.UpdateChar();
+ }
+ else
+ state = _optimum[posPrev].State;
+ if (posPrev == cur - 1)
+ {
+ if (_optimum[cur].IsShortRep())
+ state.UpdateShortRep();
+ else
+ state.UpdateChar();
+ }
+ else
+ {
+ uint pos;
+ if (_optimum[cur].Prev1IsChar && _optimum[cur].Prev2)
+ {
+ posPrev = _optimum[cur].PosPrev2;
+ pos = _optimum[cur].BackPrev2;
+ state.UpdateRep();
+ }
+ else
+ {
+ pos = _optimum[cur].BackPrev;
+ if (pos < Base.kNumRepDistances)
+ state.UpdateRep();
+ else
+ state.UpdateMatch();
+ }
+ Optimal opt = _optimum[posPrev];
+ if (pos < Base.kNumRepDistances)
+ {
+ if (pos == 0)
+ {
+ _reps[0] = opt.Backs0;
+ _reps[1] = opt.Backs1;
+ _reps[2] = opt.Backs2;
+ _reps[3] = opt.Backs3;
+ }
+ else if (pos == 1)
+ {
+ _reps[0] = opt.Backs1;
+ _reps[1] = opt.Backs0;
+ _reps[2] = opt.Backs2;
+ _reps[3] = opt.Backs3;
+ }
+ else if (pos == 2)
+ {
+ _reps[0] = opt.Backs2;
+ _reps[1] = opt.Backs0;
+ _reps[2] = opt.Backs1;
+ _reps[3] = opt.Backs3;
+ }
+ else
+ {
+ _reps[0] = opt.Backs3;
+ _reps[1] = opt.Backs0;
+ _reps[2] = opt.Backs1;
+ _reps[3] = opt.Backs2;
+ }
+ }
+ else
+ {
+ _reps[0] = (pos - Base.kNumRepDistances);
+ _reps[1] = opt.Backs0;
+ _reps[2] = opt.Backs1;
+ _reps[3] = opt.Backs2;
+ }
+ }
+ _optimum[cur].State = state;
+ _optimum[cur].Backs0 = _reps[0];
+ _optimum[cur].Backs1 = _reps[1];
+ _optimum[cur].Backs2 = _reps[2];
+ _optimum[cur].Backs3 = _reps[3];
+ uint curPrice = _optimum[cur].Price;
+
+ currentByte = _matchFinder.GetIndexByte(0 - 1);
+ matchByte = _matchFinder.GetIndexByte((int)(0 - _reps[0] - 1 - 1));
+
+ posState = (position & _posStateMask);
+
+ uint curAnd1Price = curPrice +
+ _isMatch[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice0() +
+ _literalEncoder.GetSubCoder(position, _matchFinder.GetIndexByte(0 - 2)).
+ GetPrice(!state.IsCharState(), matchByte, currentByte);
+
+ Optimal nextOptimum = _optimum[cur + 1];
+
+ bool nextIsChar = false;
+ if (curAnd1Price < nextOptimum.Price)
+ {
+ nextOptimum.Price = curAnd1Price;
+ nextOptimum.PosPrev = cur;
+ nextOptimum.MakeAsChar();
+ nextIsChar = true;
+ }
+
+ matchPrice = curPrice + _isMatch[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice1();
+ repMatchPrice = matchPrice + _isRep[state.Index].GetPrice1();
+
+ if (matchByte == currentByte &&
+ !(nextOptimum.PosPrev < cur && nextOptimum.BackPrev == 0))
+ {
+ uint shortRepPrice = repMatchPrice + getRepLen1Price(state, posState);
+ if (shortRepPrice <= nextOptimum.Price)
+ {
+ nextOptimum.Price = shortRepPrice;
+ nextOptimum.PosPrev = cur;
+ nextOptimum.MakeAsShortRep();
+ nextIsChar = true;
+ }
+ }
+
+ uint numAvailableBytesFull = _matchFinder.GetNumAvailableBytes() + 1;
+ numAvailableBytesFull = Math.Min(kNumOpts - 1 - cur, numAvailableBytesFull);
+ numAvailableBytes = numAvailableBytesFull;
+
+ if (numAvailableBytes < 2)
+ continue;
+ if (numAvailableBytes > _numFastBytes)
+ numAvailableBytes = _numFastBytes;
+ if (!nextIsChar && matchByte != currentByte)
+ {
+ // try Literal + rep0
+ uint t = Math.Min(numAvailableBytesFull - 1, _numFastBytes);
+ uint lenTest2 = _matchFinder.GetMatchLen(0, _reps[0], t);
+ if (lenTest2 >= 2)
+ {
+ Base.State state2 = state;
+ state2.UpdateChar();
+ uint posStateNext = (position + 1) & _posStateMask;
+ uint nextRepMatchPrice = curAnd1Price +
+ _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice1() +
+ _isRep[state2.Index].GetPrice1();
+ {
+ uint offset = cur + 1 + lenTest2;
+ while (lenEnd < offset)
+ _optimum[++lenEnd].Price = kIfinityPrice;
+ uint curAndLenPrice = nextRepMatchPrice + getRepPrice(
+ 0, lenTest2, state2, posStateNext);
+ Optimal optimum = _optimum[offset];
+ if (curAndLenPrice < optimum.Price)
+ {
+ optimum.Price = curAndLenPrice;
+ optimum.PosPrev = cur + 1;
+ optimum.BackPrev = 0;
+ optimum.Prev1IsChar = true;
+ optimum.Prev2 = false;
+ }
+ }
+ }
+ }
+
+ uint startLen = 2; // speed optimization
+
+ for (uint repIndex = 0; repIndex < Base.kNumRepDistances; repIndex++)
+ {
+ uint lenTest = _matchFinder.GetMatchLen(0 - 1, _reps[repIndex], numAvailableBytes);
+ if (lenTest < 2)
+ continue;
+ uint lenTestTemp = lenTest;
+ do
+ {
+ while (lenEnd < cur + lenTest)
+ _optimum[++lenEnd].Price = kIfinityPrice;
+ uint curAndLenPrice = repMatchPrice + getRepPrice(repIndex, lenTest, state, posState);
+ Optimal optimum = _optimum[cur + lenTest];
+ if (curAndLenPrice < optimum.Price)
+ {
+ optimum.Price = curAndLenPrice;
+ optimum.PosPrev = cur;
+ optimum.BackPrev = repIndex;
+ optimum.Prev1IsChar = false;
+ }
+ }
+ while(--lenTest >= 2);
+ lenTest = lenTestTemp;
+
+ if (repIndex == 0)
+ startLen = lenTest + 1;
+
+ // if (_maxMode)
+ if (lenTest < numAvailableBytesFull)
+ {
+ uint t = Math.Min(numAvailableBytesFull - 1 - lenTest, _numFastBytes);
+ uint lenTest2 = _matchFinder.GetMatchLen((int)lenTest, _reps[repIndex], t);
+ if (lenTest2 >= 2)
+ {
+ Base.State state2 = state;
+ state2.UpdateRep();
+ uint posStateNext = (position + lenTest) & _posStateMask;
+ uint curAndLenCharPrice =
+ repMatchPrice + getRepPrice(repIndex, lenTest, state, posState) +
+ _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice0() +
+ _literalEncoder.GetSubCoder(position + lenTest,
+ _matchFinder.GetIndexByte((int)lenTest - 1 - 1)).GetPrice(true,
+ _matchFinder.GetIndexByte((int)((int)lenTest - 1 - (int)(_reps[repIndex] + 1))),
+ _matchFinder.GetIndexByte((int)lenTest - 1));
+ state2.UpdateChar();
+ posStateNext = (position + lenTest + 1) & _posStateMask;
+ uint nextMatchPrice = curAndLenCharPrice + _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice1();
+ uint nextRepMatchPrice = nextMatchPrice + _isRep[state2.Index].GetPrice1();
+
+ // for(; lenTest2 >= 2; lenTest2--)
+ {
+ uint offset = lenTest + 1 + lenTest2;
+ while(lenEnd < cur + offset)
+ _optimum[++lenEnd].Price = kIfinityPrice;
+ uint curAndLenPrice = nextRepMatchPrice + getRepPrice(0, lenTest2, state2, posStateNext);
+ Optimal optimum = _optimum[cur + offset];
+ if (curAndLenPrice < optimum.Price)
+ {
+ optimum.Price = curAndLenPrice;
+ optimum.PosPrev = cur + lenTest + 1;
+ optimum.BackPrev = 0;
+ optimum.Prev1IsChar = true;
+ optimum.Prev2 = true;
+ optimum.PosPrev2 = cur;
+ optimum.BackPrev2 = repIndex;
+ }
+ }
+ }
+ }
+ }
+
+ if (newLen > numAvailableBytes)
+ {
+ newLen = numAvailableBytes;
+ for (numDistancePairs = 0; newLen > _matchDistances[numDistancePairs]; numDistancePairs += 2) ;
+ _matchDistances[numDistancePairs] = newLen;
+ numDistancePairs += 2;
+ }
+ if (newLen >= startLen)
+ {
+ normalMatchPrice = matchPrice + _isRep[state.Index].GetPrice0();
+ while (lenEnd < cur + newLen)
+ _optimum[++lenEnd].Price = kIfinityPrice;
+
+ uint offs = 0;
+ while (startLen > _matchDistances[offs])
+ offs += 2;
+
+ for (uint lenTest = startLen; ; lenTest++)
+ {
+ uint curBack = _matchDistances[offs + 1];
+ uint curAndLenPrice = normalMatchPrice + getPosLenPrice(curBack, lenTest, posState);
+ Optimal optimum = _optimum[cur + lenTest];
+ if (curAndLenPrice < optimum.Price)
+ {
+ optimum.Price = curAndLenPrice;
+ optimum.PosPrev = cur;
+ optimum.BackPrev = curBack + Base.kNumRepDistances;
+ optimum.Prev1IsChar = false;
+ }
+
+ if (lenTest == _matchDistances[offs])
+ {
+ if (lenTest < numAvailableBytesFull)
+ {
+ uint t = Math.Min(numAvailableBytesFull - 1 - lenTest, _numFastBytes);
+ uint lenTest2 = _matchFinder.GetMatchLen((int)lenTest, curBack, t);
+ if (lenTest2 >= 2)
+ {
+ Base.State state2 = state;
+ state2.UpdateMatch();
+ uint posStateNext = (position + lenTest) & _posStateMask;
+ uint curAndLenCharPrice = curAndLenPrice +
+ _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice0() +
+ _literalEncoder.GetSubCoder(position + lenTest,
+ _matchFinder.GetIndexByte((int)lenTest - 1 - 1)).
+ GetPrice(true,
+ _matchFinder.GetIndexByte((int)lenTest - (int)(curBack + 1) - 1),
+ _matchFinder.GetIndexByte((int)lenTest - 1));
+ state2.UpdateChar();
+ posStateNext = (position + lenTest + 1) & _posStateMask;
+ uint nextMatchPrice = curAndLenCharPrice + _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice1();
+ uint nextRepMatchPrice = nextMatchPrice + _isRep[state2.Index].GetPrice1();
+
+ uint offset = lenTest + 1 + lenTest2;
+ while (lenEnd < cur + offset)
+ _optimum[++lenEnd].Price = kIfinityPrice;
+ curAndLenPrice = nextRepMatchPrice + getRepPrice(0, lenTest2, state2, posStateNext);
+ optimum = _optimum[cur + offset];
+ if (curAndLenPrice < optimum.Price)
+ {
+ optimum.Price = curAndLenPrice;
+ optimum.PosPrev = cur + lenTest + 1;
+ optimum.BackPrev = 0;
+ optimum.Prev1IsChar = true;
+ optimum.Prev2 = true;
+ optimum.PosPrev2 = cur;
+ optimum.BackPrev2 = curBack + Base.kNumRepDistances;
+ }
+ }
+ }
+ offs += 2;
+ if (offs == numDistancePairs)
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ void writeEndMarker(uint posState)
+ {
+ if (!_writeEndMark)
+ return;
+
+ _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].Encode(_rangeEncoder, 1);
+ _isRep[_state.Index].Encode(_rangeEncoder, 0);
+ _state.UpdateMatch();
+ uint len = Base.kMatchMinLen;
+ _lenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState);
+ uint posSlot = (1 << Base.kNumPosSlotBits) - 1;
+ uint lenToPosState = Base.GetLenToPosState(len);
+ _posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot);
+ int footerBits = 30;
+ uint posReduced = (((uint)1) << footerBits) - 1;
+ _rangeEncoder.EncodeDirectBits(posReduced >> Base.kNumAlignBits, footerBits - Base.kNumAlignBits);
+ _posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.kAlignMask);
+ }
+
+ void flush(uint nowPos)
+ {
+ releaseMFStream();
+ writeEndMarker(nowPos & _posStateMask);
+ _rangeEncoder.FlushData();
+ _rangeEncoder.FlushStream();
+ }
+
+ public void CodeOneBlock(out long inSize, out long outSize, out bool finished)
+ {
+ inSize = 0;
+ outSize = 0;
+ finished = true;
+
+ if (_inStream != null)
+ {
+ _matchFinder.SetStream(_inStream);
+ _matchFinder.Init();
+ _needReleaseMFStream = true;
+ _inStream = null;
+ }
+
+ if (_finished)
+ return;
+ _finished = true;
+
+
+ long progressPosValuePrev = _nowPos64;
+ if (_nowPos64 == 0)
+ {
+ if (_matchFinder.GetNumAvailableBytes() == 0)
+ {
+ flush((uint)_nowPos64);
+ return;
+ }
+ // it's not used
+ readMatchDistances(out _, out _);
+ uint posState = (uint)(_nowPos64) & _posStateMask;
+ _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].Encode(_rangeEncoder, 0);
+ _state.UpdateChar();
+ byte curByte = _matchFinder.GetIndexByte((int)(0 - _additionalOffset));
+ _literalEncoder.GetSubCoder((uint)(_nowPos64), _previousByte).Encode(_rangeEncoder, curByte);
+ _previousByte = curByte;
+ _additionalOffset--;
+ _nowPos64++;
+ }
+ if (_matchFinder.GetNumAvailableBytes() == 0)
+ {
+ flush((uint)_nowPos64);
+ return;
+ }
+ while (true)
+ {
+ uint len = getOptimum((uint)_nowPos64, out uint pos);
+
+ uint posState = ((uint)_nowPos64) & _posStateMask;
+ uint complexState = (_state.Index << Base.kNumPosStatesBitsMax) + posState;
+ if (len == 1 && pos == 0xFFFFFFFF)
+ {
+ _isMatch[complexState].Encode(_rangeEncoder, 0);
+ byte curByte = _matchFinder.GetIndexByte((int)(0 - _additionalOffset));
+ LiteralEncoder.Encoder2 subCoder = _literalEncoder.GetSubCoder((uint)_nowPos64, _previousByte);
+ if (!_state.IsCharState())
+ {
+ byte matchByte = _matchFinder.GetIndexByte((int)(0 - _repDistances[0] - 1 - _additionalOffset));
+ subCoder.EncodeMatched(_rangeEncoder, matchByte, curByte);
+ }
+ else
+ subCoder.Encode(_rangeEncoder, curByte);
+ _previousByte = curByte;
+ _state.UpdateChar();
+ }
+ else
+ {
+ _isMatch[complexState].Encode(_rangeEncoder, 1);
+ if (pos < Base.kNumRepDistances)
+ {
+ _isRep[_state.Index].Encode(_rangeEncoder, 1);
+ if (pos == 0)
+ {
+ _isRepG0[_state.Index].Encode(_rangeEncoder, 0);
+ if (len == 1)
+ _isRep0Long[complexState].Encode(_rangeEncoder, 0);
+ else
+ _isRep0Long[complexState].Encode(_rangeEncoder, 1);
+ }
+ else
+ {
+ _isRepG0[_state.Index].Encode(_rangeEncoder, 1);
+ if (pos == 1)
+ _isRepG1[_state.Index].Encode(_rangeEncoder, 0);
+ else
+ {
+ _isRepG1[_state.Index].Encode(_rangeEncoder, 1);
+ _isRepG2[_state.Index].Encode(_rangeEncoder, pos - 2);
+ }
+ }
+ if (len == 1)
+ _state.UpdateShortRep();
+ else
+ {
+ _repMatchLenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState);
+ _state.UpdateRep();
+ }
+ uint distance = _repDistances[pos];
+ if (pos != 0)
+ {
+ for (uint i = pos; i >= 1; i--)
+ _repDistances[i] = _repDistances[i - 1];
+ _repDistances[0] = distance;
+ }
+ }
+ else
+ {
+ _isRep[_state.Index].Encode(_rangeEncoder, 0);
+ _state.UpdateMatch();
+ _lenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState);
+ pos -= Base.kNumRepDistances;
+ uint posSlot = getPosSlot(pos);
+ uint lenToPosState = Base.GetLenToPosState(len);
+ _posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot);
+
+ if (posSlot >= Base.kStartPosModelIndex)
+ {
+ int footerBits = (int)((posSlot >> 1) - 1);
+ uint baseVal = ((2 | (posSlot & 1)) << footerBits);
+ uint posReduced = pos - baseVal;
+
+ if (posSlot < Base.kEndPosModelIndex)
+ RangeCoder.BitTreeEncoder.ReverseEncode(_posEncoders,
+ baseVal - posSlot - 1, _rangeEncoder, footerBits, posReduced);
+ else
+ {
+ _rangeEncoder.EncodeDirectBits(posReduced >> Base.kNumAlignBits, footerBits - Base.kNumAlignBits);
+ _posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.kAlignMask);
+ _alignPriceCount++;
+ }
+ }
+ uint distance = pos;
+ for (uint i = Base.kNumRepDistances - 1; i >= 1; i--)
+ _repDistances[i] = _repDistances[i - 1];
+ _repDistances[0] = distance;
+ _matchPriceCount++;
+ }
+ _previousByte = _matchFinder.GetIndexByte((int)(len - 1 - _additionalOffset));
+ }
+ _additionalOffset -= len;
+ _nowPos64 += len;
+ if (_additionalOffset == 0)
+ {
+ // if (!_fastMode)
+ if (_matchPriceCount >= (1 << 7))
+ fillDistancesPrices();
+ if (_alignPriceCount >= Base.kAlignTableSize)
+ fillAlignPrices();
+ inSize = _nowPos64;
+ outSize = _rangeEncoder.GetProcessedSizeAdd();
+ if (_matchFinder.GetNumAvailableBytes() == 0)
+ {
+ flush((uint)_nowPos64);
+ return;
+ }
+
+ if (_nowPos64 - progressPosValuePrev >= (1 << 12))
+ {
+ _finished = false;
+ finished = false;
+ return;
+ }
+ }
+ }
+ }
+
+ void releaseMFStream()
+ {
+ if (_matchFinder != null && _needReleaseMFStream)
+ {
+ _matchFinder.ReleaseStream();
+ _needReleaseMFStream = false;
+ }
+ }
+
+ void setOutStream(System.IO.Stream outStream) { _rangeEncoder.SetStream(outStream); }
+ void releaseOutStream() { _rangeEncoder.ReleaseStream(); }
+
+ void releaseStreams()
+ {
+ releaseMFStream();
+ releaseOutStream();
+ }
+
+ void setStreams(System.IO.Stream inStream, System.IO.Stream outStream)
+ {
+ _inStream = inStream;
+ _finished = false;
+ create();
+ setOutStream(outStream);
+ init();
+
+ // if (!_fastMode)
+ {
+ fillDistancesPrices();
+ fillAlignPrices();
+ }
+
+ _lenEncoder.SetTableSize(_numFastBytes + 1 - Base.kMatchMinLen);
+ _lenEncoder.UpdateTables((uint)1 << _posStateBits);
+ _repMatchLenEncoder.SetTableSize(_numFastBytes + 1 - Base.kMatchMinLen);
+ _repMatchLenEncoder.UpdateTables((uint)1 << _posStateBits);
+
+ _nowPos64 = 0;
+ }
+
+
+ public void Code(System.IO.Stream inStream, System.IO.Stream outStream,
+ long inSize, long outSize, ICodeProgress progress)
+ {
+ _needReleaseMFStream = false;
+ try
+ {
+ setStreams(inStream, outStream);
+ while (true)
+ {
+ CodeOneBlock(out long processedInSize, out long processedOutSize, out bool finished);
+ if (finished)
+ return;
+ if (progress != null)
+ {
+ progress.SetProgress(processedInSize, processedOutSize);
+ }
+ }
+ }
+ finally
+ {
+ releaseStreams();
+ }
+ }
+
+ readonly byte[] _properties = new byte[kPropSize];
+
+ public void WriteCoderProperties(System.IO.Stream outStream)
+ {
+ _properties[0] = (byte)((_posStateBits * 5 + _numLiteralPosStateBits) * 9 + _numLiteralContextBits);
+ for (int i = 0; i < 4; i++)
+ _properties[1 + i] = (byte)((_dictionarySize >> (8 * i)) & 0xFF);
+ outStream.Write(_properties, 0, kPropSize);
+ }
+
+ readonly uint[] _tempPrices = new uint[Base.kNumFullDistances];
+ uint _matchPriceCount;
+
+ void fillDistancesPrices()
+ {
+ for (uint i = Base.kStartPosModelIndex; i < Base.kNumFullDistances; i++)
+ {
+ uint posSlot = getPosSlot(i);
+ int footerBits = (int)((posSlot >> 1) - 1);
+ uint baseVal = ((2 | (posSlot & 1)) << footerBits);
+ _tempPrices[i] = BitTreeEncoder.ReverseGetPrice(_posEncoders,
+ baseVal - posSlot - 1, footerBits, i - baseVal);
+ }
+
+ for (uint lenToPosState = 0; lenToPosState < Base.kNumLenToPosStates; lenToPosState++)
+ {
+ uint posSlot;
+ BitTreeEncoder encoder = _posSlotEncoder[lenToPosState];
+
+ uint st = (lenToPosState << Base.kNumPosSlotBits);
+ for (posSlot = 0; posSlot < _distTableSize; posSlot++)
+ _posSlotPrices[st + posSlot] = encoder.GetPrice(posSlot);
+ for (posSlot = Base.kEndPosModelIndex; posSlot < _distTableSize; posSlot++)
+ _posSlotPrices[st + posSlot] += ((((posSlot >> 1) - 1) - Base.kNumAlignBits) << RangeCoder.BitEncoder.kNumBitPriceShiftBits);
+
+ uint st2 = lenToPosState * Base.kNumFullDistances;
+ uint i;
+ for (i = 0; i < Base.kStartPosModelIndex; i++)
+ _distancesPrices[st2 + i] = _posSlotPrices[st + i];
+ for (; i < Base.kNumFullDistances; i++)
+ _distancesPrices[st2 + i] = _posSlotPrices[st + getPosSlot(i)] + _tempPrices[i];
+ }
+ _matchPriceCount = 0;
+ }
+
+ void fillAlignPrices()
+ {
+ for (uint i = 0; i < Base.kAlignTableSize; i++)
+ _alignPrices[i] = _posAlignEncoder.ReverseGetPrice(i);
+ _alignPriceCount = 0;
+ }
+
+ static int findMatchFinder(string s)
+ {
+ for (int m = 0; m < kMatchFinderIDs.Length; m++)
+ if (s == kMatchFinderIDs[m])
+ return m;
+ return -1;
+ }
+
+ public void SetCoderProperties(CoderPropID[] propIDs, object[] properties)
+ {
+ for (uint i = 0; i < properties.Length; i++)
+ {
+ object prop = properties[i];
+ switch (propIDs[i])
+ {
+ case CoderPropID.NumFastBytes:
+ {
+ if (!(prop is int))
+ throw new InvalidParamException();
+ int numFastBytes = (int)prop;
+ if (numFastBytes < 5 || numFastBytes > Base.kMatchMaxLen)
+ throw new InvalidParamException();
+ _numFastBytes = (uint)numFastBytes;
+ break;
+ }
+ case CoderPropID.Algorithm:
+ {
+ break;
+ }
+ case CoderPropID.MatchFinder:
+ {
+ if (!(prop is string))
+ throw new InvalidParamException();
+ EMatchFinderType matchFinderIndexPrev = _matchFinderType;
+ int m = findMatchFinder(((string)prop).ToUpper());
+ if (m < 0)
+ throw new InvalidParamException();
+ _matchFinderType = (EMatchFinderType)m;
+ if (_matchFinder != null && matchFinderIndexPrev != _matchFinderType)
+ {
+ _dictionarySizePrev = 0xFFFFFFFF;
+ _matchFinder = null;
+ }
+ break;
+ }
+ case CoderPropID.DictionarySize:
+ {
+ const int kDicLogSizeMaxCompress = 30;
+ if (!(prop is int))
+ throw new InvalidParamException(); ;
+ int dictionarySize = (int)prop;
+ if (dictionarySize < (uint)(1 << Base.kDicLogSizeMin) ||
+ dictionarySize > (uint)(1 << kDicLogSizeMaxCompress))
+ throw new InvalidParamException();
+ _dictionarySize = (uint)dictionarySize;
+ int dicLogSize;
+ for (dicLogSize = 0; dicLogSize < (uint)kDicLogSizeMaxCompress; dicLogSize++)
+ if (dictionarySize <= ((uint)(1) << dicLogSize))
+ break;
+ _distTableSize = (uint)dicLogSize * 2;
+ break;
+ }
+ case CoderPropID.PosStateBits:
+ {
+ if (!(prop is int))
+ throw new InvalidParamException();
+ int v = (int)prop;
+ if (v < 0 || v > (uint)Base.kNumPosStatesBitsEncodingMax)
+ throw new InvalidParamException();
+ _posStateBits = (int)v;
+ _posStateMask = (((uint)1) << (int)_posStateBits) - 1;
+ break;
+ }
+ case CoderPropID.LitPosBits:
+ {
+ if (!(prop is int))
+ throw new InvalidParamException();
+ int v = (int)prop;
+ if (v < 0 || v > (uint)Base.kNumLitPosStatesBitsEncodingMax)
+ throw new InvalidParamException();
+ _numLiteralPosStateBits = (int)v;
+ break;
+ }
+ case CoderPropID.LitContextBits:
+ {
+ if (!(prop is int))
+ throw new InvalidParamException();
+ int v = (int)prop;
+ if (v < 0 || v > (uint)Base.kNumLitContextBitsMax)
+ throw new InvalidParamException(); ;
+ _numLiteralContextBits = (int)v;
+ break;
+ }
+ case CoderPropID.EndMarker:
+ {
+ if (!(prop is bool))
+ throw new InvalidParamException();
+ setWriteEndMarkerMode((bool)prop);
+ break;
+ }
+ default:
+ throw new InvalidParamException();
+ }
+ }
+ }
+ }
+}
diff --git a/SevenZip/Compression/RangeCoder/BitDecoder.cs b/SevenZip/Compression/RangeCoder/BitDecoder.cs
new file mode 100644
index 0000000..6f39761
--- /dev/null
+++ b/SevenZip/Compression/RangeCoder/BitDecoder.cs
@@ -0,0 +1,45 @@
+// Part of the LZMA SDK by Igor Pavlov
+
+namespace SevenZip.Compression.RangeCoder
+{
+ struct BitDecoder
+ {
+ public const int kNumBitModelTotalBits = 11;
+ public const uint kBitModelTotal = (1 << kNumBitModelTotalBits);
+#pragma warning disable IDE1006 // Naming Styles
+ const int kNumMoveBits = 5;
+#pragma warning restore IDE1006 // Naming Styles
+
+ uint _prob;
+
+ public void Init() { _prob = kBitModelTotal >> 1; }
+
+ public uint Decode(Decoder rangeDecoder)
+ {
+ uint newBound = (rangeDecoder.Range >> kNumBitModelTotalBits) * _prob;
+ if (rangeDecoder.Code < newBound)
+ {
+ rangeDecoder.Range = newBound;
+ _prob += (kBitModelTotal - _prob) >> kNumMoveBits;
+ if (rangeDecoder.Range < Decoder.kTopValue)
+ {
+ rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte();
+ rangeDecoder.Range <<= 8;
+ }
+ return 0;
+ }
+ else
+ {
+ rangeDecoder.Range -= newBound;
+ rangeDecoder.Code -= newBound;
+ _prob -= (_prob) >> kNumMoveBits;
+ if (rangeDecoder.Range < Decoder.kTopValue)
+ {
+ rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte();
+ rangeDecoder.Range <<= 8;
+ }
+ return 1;
+ }
+ }
+ }
+}
diff --git a/SevenZip/Compression/RangeCoder/BitEncoder.cs b/SevenZip/Compression/RangeCoder/BitEncoder.cs
new file mode 100644
index 0000000..ea5d3d6
--- /dev/null
+++ b/SevenZip/Compression/RangeCoder/BitEncoder.cs
@@ -0,0 +1,62 @@
+// Part of the LZMA SDK by Igor Pavlov
+
+namespace SevenZip.Compression.RangeCoder
+{
+ struct BitEncoder
+ {
+ public const int kNumBitModelTotalBits = 11;
+ public const uint kBitModelTotal = (1 << kNumBitModelTotalBits);
+#pragma warning disable IDE1006 // Naming Styles
+ const int kNumMoveBits = 5;
+ const int kNumMoveReducingBits = 2;
+#pragma warning restore IDE1006 // Naming Styles
+ public const int kNumBitPriceShiftBits = 6;
+
+ uint _prob;
+
+ public void Init() { _prob = kBitModelTotal >> 1; }
+
+ public void Encode(Encoder encoder, uint symbol)
+ {
+ uint newBound = (encoder.Range >> kNumBitModelTotalBits) * _prob;
+ if (symbol == 0)
+ {
+ encoder.Range = newBound;
+ _prob += (kBitModelTotal - _prob) >> kNumMoveBits;
+ }
+ else
+ {
+ encoder.Low += newBound;
+ encoder.Range -= newBound;
+ _prob -= (_prob) >> kNumMoveBits;
+ }
+ if (encoder.Range < Encoder.kTopValue)
+ {
+ encoder.Range <<= 8;
+ encoder.ShiftLow();
+ }
+ }
+
+ static readonly uint[] _probPrices = new uint[kBitModelTotal >> kNumMoveReducingBits];
+
+ static BitEncoder()
+ {
+ const int kNumBits = (kNumBitModelTotalBits - kNumMoveReducingBits);
+ for (int i = kNumBits - 1; i >= 0; i--)
+ {
+ uint start = (uint)1 << (kNumBits - i - 1);
+ uint end = (uint)1 << (kNumBits - i);
+ for (uint j = start; j < end; j++)
+ _probPrices[j] = ((uint)i << kNumBitPriceShiftBits) +
+ (((end - j) << kNumBitPriceShiftBits) >> (kNumBits - i - 1));
+ }
+ }
+
+ public uint GetPrice(uint symbol)
+ {
+ return _probPrices[(((_prob - symbol) ^ ((-(int)symbol))) & (kBitModelTotal - 1)) >> kNumMoveReducingBits];
+ }
+ public uint GetPrice0() { return _probPrices[_prob >> kNumMoveReducingBits]; }
+ public uint GetPrice1() { return _probPrices[(kBitModelTotal - _prob) >> kNumMoveReducingBits]; }
+ }
+}
diff --git a/SevenZip/Compression/RangeCoder/BitTreeDecoder.cs b/SevenZip/Compression/RangeCoder/BitTreeDecoder.cs
new file mode 100644
index 0000000..121c910
--- /dev/null
+++ b/SevenZip/Compression/RangeCoder/BitTreeDecoder.cs
@@ -0,0 +1,59 @@
+// Part of the LZMA SDK by Igor Pavlov
+
+namespace SevenZip.Compression.RangeCoder
+{
+ struct BitTreeDecoder
+ {
+ readonly BitDecoder[] _models;
+ readonly int _numBitLevels;
+
+ public BitTreeDecoder(int numBitLevels)
+ {
+ _numBitLevels = numBitLevels;
+ _models = new BitDecoder[1 << numBitLevels];
+ }
+
+ public void Init()
+ {
+ for (uint i = 1; i < (1 << _numBitLevels); i++)
+ _models[i].Init();
+ }
+
+ public uint Decode(Decoder rangeDecoder)
+ {
+ uint m = 1;
+ for (int bitIndex = _numBitLevels; bitIndex > 0; bitIndex--)
+ m = (m << 1) + _models[m].Decode(rangeDecoder);
+ return m - ((uint)1 << _numBitLevels);
+ }
+
+ public uint ReverseDecode(Decoder rangeDecoder)
+ {
+ uint m = 1;
+ uint symbol = 0;
+ for (int bitIndex = 0; bitIndex < _numBitLevels; bitIndex++)
+ {
+ uint bit = _models[m].Decode(rangeDecoder);
+ m <<= 1;
+ m += bit;
+ symbol |= (bit << bitIndex);
+ }
+ return symbol;
+ }
+
+ public static uint ReverseDecode(BitDecoder[] Models, uint startIndex,
+ Decoder rangeDecoder, int NumBitLevels)
+ {
+ uint m = 1;
+ uint symbol = 0;
+ for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++)
+ {
+ uint bit = Models[startIndex + m].Decode(rangeDecoder);
+ m <<= 1;
+ m += bit;
+ symbol |= (bit << bitIndex);
+ }
+ return symbol;
+ }
+ }
+}
diff --git a/SevenZip/Compression/RangeCoder/BitTreeEncoder.cs b/SevenZip/Compression/RangeCoder/BitTreeEncoder.cs
new file mode 100644
index 0000000..e3472ae
--- /dev/null
+++ b/SevenZip/Compression/RangeCoder/BitTreeEncoder.cs
@@ -0,0 +1,102 @@
+// Part of the LZMA SDK by Igor Pavlov
+
+namespace SevenZip.Compression.RangeCoder
+{
+ struct BitTreeEncoder
+ {
+ readonly BitEncoder[] _models;
+ readonly int _numBitLevels;
+
+ public BitTreeEncoder(int numBitLevels)
+ {
+ _numBitLevels = numBitLevels;
+ _models = new BitEncoder[1 << numBitLevels];
+ }
+
+ public void Init()
+ {
+ for (uint i = 1; i < (1 << _numBitLevels); i++)
+ _models[i].Init();
+ }
+
+ public void Encode(Encoder rangeEncoder, uint symbol)
+ {
+ uint m = 1;
+ for (int bitIndex = _numBitLevels; bitIndex > 0; )
+ {
+ bitIndex--;
+ uint bit = (symbol >> bitIndex) & 1;
+ _models[m].Encode(rangeEncoder, bit);
+ m = (m << 1) | bit;
+ }
+ }
+
+ public void ReverseEncode(Encoder rangeEncoder, uint symbol)
+ {
+ uint m = 1;
+ for (uint i = 0; i < _numBitLevels; i++)
+ {
+ uint bit = symbol & 1;
+ _models[m].Encode(rangeEncoder, bit);
+ m = (m << 1) | bit;
+ symbol >>= 1;
+ }
+ }
+
+ public uint GetPrice(uint symbol)
+ {
+ uint price = 0;
+ uint m = 1;
+ for (int bitIndex = _numBitLevels; bitIndex > 0; )
+ {
+ bitIndex--;
+ uint bit = (symbol >> bitIndex) & 1;
+ price += _models[m].GetPrice(bit);
+ m = (m << 1) + bit;
+ }
+ return price;
+ }
+
+ public uint ReverseGetPrice(uint symbol)
+ {
+ uint price = 0;
+ uint m = 1;
+ for (int i = _numBitLevels; i > 0; i--)
+ {
+ uint bit = symbol & 1;
+ symbol >>= 1;
+ price += _models[m].GetPrice(bit);
+ m = (m << 1) | bit;
+ }
+ return price;
+ }
+
+ public static uint ReverseGetPrice(BitEncoder[] Models, uint startIndex,
+ int NumBitLevels, uint symbol)
+ {
+ uint price = 0;
+ uint m = 1;
+ for (int i = NumBitLevels; i > 0; i--)
+ {
+ uint bit = symbol & 1;
+ symbol >>= 1;
+ price += Models[startIndex + m].GetPrice(bit);
+ m = (m << 1) | bit;
+ }
+ return price;
+ }
+
+ public static void ReverseEncode(BitEncoder[] Models, uint startIndex,
+ Encoder rangeEncoder, int NumBitLevels, uint symbol)
+ {
+ uint m = 1;
+ for (int i = 0; i < NumBitLevels; i++)
+ {
+ uint bit = symbol & 1;
+ Models[startIndex + m].Encode(rangeEncoder, bit);
+ m = (m << 1) | bit;
+ symbol >>= 1;
+ }
+ }
+ }
+}
diff --git a/SevenZip/Compression/RangeCoder/Decoder.cs b/SevenZip/Compression/RangeCoder/Decoder.cs
new file mode 100644
index 0000000..4cc4995
--- /dev/null
+++ b/SevenZip/Compression/RangeCoder/Decoder.cs
@@ -0,0 +1,50 @@
+// Part of the LZMA SDK by Igor Pavlov
+
+namespace SevenZip.Compression.RangeCoder
+{
+ class Decoder
+ {
+ public const uint kTopValue = (1 << 24);
+ public uint Range;
+ public uint Code;
+ public System.IO.Stream Stream;
+
+ public void Init(System.IO.Stream stream)
+ {
+ Stream = stream;
+
+ Code = 0;
+ Range = 0xFFFFFFFF;
+ for (int i = 0; i < 5; i++)
+ Code = (Code << 8) | (byte)Stream.ReadByte();
+ }
+
+ public void ReleaseStream()
+ {
+ Stream = null;
+ }
+
+ public uint DecodeDirectBits(int numTotalBits)
+ {
+ uint range = Range;
+ uint code = Code;
+ uint result = 0;
+ for (int i = numTotalBits; i > 0; i--)
+ {
+ range >>= 1;
+ uint t = (code - range) >> 31;
+ code -= range & (t - 1);
+ result = (result << 1) | (1 - t);
+
+ if (range < kTopValue)
+ {
+ code = (code << 8) | (byte)Stream.ReadByte();
+ range <<= 8;
+ }
+ }
+ Range = range;
+ Code = code;
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SevenZip/Compression/RangeCoder/Encoder.cs b/SevenZip/Compression/RangeCoder/Encoder.cs
new file mode 100644
index 0000000..91bc898
--- /dev/null
+++ b/SevenZip/Compression/RangeCoder/Encoder.cs
@@ -0,0 +1,87 @@
+// Part of the LZMA SDK by Igor Pavlov
+
+namespace SevenZip.Compression.RangeCoder
+{
+ class Encoder
+ {
+ public const uint kTopValue = (1 << 24);
+
+ System.IO.Stream _stream;
+
+ public ulong Low;
+ public uint Range;
+ uint _cacheSize;
+ byte _cache;
+
+ long _startPosition;
+
+ public void SetStream(System.IO.Stream stream)
+ {
+ _stream = stream;
+ }
+
+ public void ReleaseStream()
+ {
+ _stream = null;
+ }
+
+ public void Init()
+ {
+ _startPosition = _stream.Position;
+
+ Low = 0;
+ Range = 0xFFFFFFFF;
+ _cacheSize = 1;
+ _cache = 0;
+ }
+
+ public void FlushData()
+ {
+ for (int i = 0; i < 5; i++)
+ ShiftLow();
+ }
+
+ public void FlushStream()
+ {
+ _stream.Flush();
+ }
+
+ public void ShiftLow()
+ {
+ if ((uint)Low < 0xFF000000 || (uint)(Low >> 32) == 1)
+ {
+ byte temp = _cache;
+ do
+ {
+ _stream.WriteByte((byte)(temp + (Low >> 32)));
+ temp = 0xFF;
+ }
+ while (--_cacheSize != 0);
+ _cache = (byte)(((uint)Low) >> 24);
+ }
+ _cacheSize++;
+ Low = ((uint)Low) << 8;
+ }
+
+ public void EncodeDirectBits(uint v, int numTotalBits)
+ {
+ for (int i = numTotalBits - 1; i >= 0; i--)
+ {
+ Range >>= 1;
+ if (((v >> i) & 1) == 1)
+ Low += Range;
+ if (Range < kTopValue)
+ {
+ Range <<= 8;
+ ShiftLow();
+ }
+ }
+ }
+
+ public long GetProcessedSizeAdd()
+ {
+ return _cacheSize +
+ _stream.Position - _startPosition + 4;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SevenZip/ICoder.cs b/SevenZip/ICoder.cs
new file mode 100644
index 0000000..5773dc2
--- /dev/null
+++ b/SevenZip/ICoder.cs
@@ -0,0 +1,90 @@
+// Part of the LZMA SDK by Igor Pavlov
+
+using System;
+
+namespace SevenZip
+{
+ /// The exception that is thrown when an error in input stream occurs during decoding.
+ class DataErrorException : ApplicationException
+ {
+ public DataErrorException(): base("Data Error") { }
+ }
+
+ /// The exception that is thrown when the value of an argument is outside the allowable range.
+ class InvalidParamException : ApplicationException
+ {
+ public InvalidParamException(): base("Invalid Parameter") { }
+ }
+
+ public interface ICodeProgress
+ {
+ /// Callback progress.
+ /// input size. -1 if unknown.
+ /// output size. -1 if unknown.
+ void SetProgress(long inSize, long outSize);
+ };
+
+ public interface ICoder
+ {
+ /// Codes streams.
+ /// input Stream.
+ /// output Stream.
+ /// input Size. -1 if unknown.
+ /// output Size. -1 if unknown.
+ /// callback progress reference.
+ /// if input stream is not valid
+ void Code(System.IO.Stream inStream, System.IO.Stream outStream,
+ long inSize, long outSize, ICodeProgress progress);
+ };
+
+ /// Provides the fields that represent properties idenitifiers for compressing.
+ public enum CoderPropID
+ {
+ /// Specifies default property.
+ DefaultProp = 0,
+ /// Specifies size of dictionary.
+ DictionarySize,
+ /// Specifies size of memory for PPM*.
+ UsedMemorySize,
+ /// Specifies order for PPM methods.
+ Order,
+ /// Specifies Block Size.
+ BlockSize,
+ /// Specifies number of postion state bits for LZMA (0 <= x <= 4).
+ PosStateBits,
+ /// Specifies number of literal context bits for LZMA (0 <= x <= 8).
+ LitContextBits,
+ /// Specifies number of literal position bits for LZMA (0 <= x <= 4).
+ LitPosBits,
+ /// Specifies number of fast bytes for LZ*.
+ NumFastBytes,
+ /// Specifies match finder. LZMA: "BT2", "BT4" or "BT4B".
+ MatchFinder,
+ /// Specifies the number of match finder cyckes.
+ MatchFinderCycles,
+ /// Specifies number of passes.
+ NumPasses,
+ /// Specifies number of algorithm.
+ Algorithm,
+ /// Specifies the number of threads.
+ NumThreads,
+ /// Specifies mode with end marker.
+ EndMarker
+ };
+
+
+ public interface ISetCoderProperties
+ {
+ void SetCoderProperties(CoderPropID[] propIDs, object[] properties);
+ };
+
+ public interface IWriteCoderProperties
+ {
+ void WriteCoderProperties(System.IO.Stream outStream);
+ }
+
+ public interface ISetDecoderProperties
+ {
+ void SetDecoderProperties(byte[] properties);
+ }
+}
diff --git a/Sub.cs b/Sub.cs
index 50ea006..0a607cc 100644
--- a/Sub.cs
+++ b/Sub.cs
@@ -1,13 +1,17 @@
/*
* Idmr.ImageFormat.Dat, Allows editing capability of LucasArts *.DAT Image files
- * Copyright (C) 2009-2019 Michael Gaisser (mjgaisser@gmail.com)
+ * Copyright (C) 2009-2022 Michael Gaisser (mjgaisser@gmail.com)
* Licensed under the GPL v2.0 or later
*
* Full notice in DatFile.cs
- * VERSION: 2.2
+ * VERSION: 2.4
*/
/* CHANGE LOG
+ * v2.4, 220227
+ * [UPD] _rows set to null for Full32bpp images since there's no processing
+ * [NEW] Format "25C", 32bpp LZMA compressed
+ * [UPD] length property replaced with _length field
* v2.2, 190922
* [NEW] Format 25, 32bpp ARGB
* [DEL] max image limits
@@ -24,6 +28,7 @@
using System;
using System.Drawing;
using System.Drawing.Imaging;
+using System.IO;
using Idmr.Common;
namespace Idmr.ImageFormat.Dat
@@ -34,8 +39,12 @@ public class Sub
short _groupID;
short _subID;
Color[] _colors = null;
- Bitmap _image; // if (Type.Transparent) Format8bppIndexed, else Format32bppArgb
+ short _height, _width;
+ /// if (Type.Transparent) Format8bppIndexed, else Format32bppArgb
+ Bitmap _image;
ImageType _type;
+ /// SubHeader.Length = ImageHeader.ImageDataOffset + _rows.Length
+ internal int _length;
/// SubHeader + ImageHeader
internal byte[] _headers;
/// Rows data plus final 0 if required
@@ -54,7 +63,9 @@ public enum ImageType : short
/// 16bpp Indexed Alpha uncompressed
UncompressedBlended,
/// 32bpp ARGB
- Full32bppArgb
+ Full32bppArgb,
+ /// "25C", 32bpp ARGB with LZMA compression
+ Compressed32bppArgb
};
#region constructors
@@ -67,11 +78,12 @@ public Sub(byte[] raw)
ArrayFunctions.TrimArray(raw, 0, _headers);
// Sub.SubHeader
_type = (ImageType)BitConverter.ToInt16(_headers, 0);
- short width = BitConverter.ToInt16(_headers, 2);
- short height = BitConverter.ToInt16(_headers, 4);
+ _width = BitConverter.ToInt16(_headers, 2);
+ _height = BitConverter.ToInt16(_headers, 4);
_groupID = BitConverter.ToInt16(_headers, 0xA);
_subID = BitConverter.ToInt16(_headers, 0xC);
int length = BitConverter.ToInt32(_headers, 0xE);
+ _length = length;
int offset = _subHeaderLength;
// Sub.ImageHeader
if (BitConverter.ToInt32(_headers, offset + 4) != _imageHeaderLength)
@@ -79,8 +91,14 @@ public Sub(byte[] raw)
int imageDataOffset = BitConverter.ToInt32(_headers, offset + 8);
if (BitConverter.ToInt32(_headers, offset + 0x24) != _imageHeaderReserved)
throw new ArgumentException(DatFile._valEx, "raw"); // Reserved
- if (BitConverter.ToInt32(_headers, offset + 0x28) != 0)
- _colors = new Color[BitConverter.ToInt32(_headers, offset + 0x28)]; // NumberOfColors
+ int numberOfColors = BitConverter.ToInt32(_headers, offset + 0x28);
+ if (_type == ImageType.Full32bppArgb && numberOfColors == 1)
+ {
+ _type = ImageType.Compressed32bppArgb;
+ numberOfColors = 0;
+ }
+ if (numberOfColors != 0)
+ _colors = new Color[numberOfColors];
offset += _imageHeaderLength;
// Sub.Colors[]
if (_colors != null)
@@ -89,7 +107,9 @@ public Sub(byte[] raw)
// Sub.Rows[]
_rows = new byte[length - imageDataOffset];
ArrayFunctions.TrimArray(raw, offset, _rows);
- _image = DecodeImage(_rows, width, height, _colors, _type);
+ if (_type != ImageType.Compressed32bppArgb)
+ _image = DecodeImage(_rows, _width, _height, _colors, _type); // don't unzip until it's called
+ if (_type == ImageType.Full32bppArgb) _rows = null;
}
/// Create a new sub from the provided image and IDs
/// Group ID value
@@ -110,7 +130,11 @@ public Sub(short groupID, short subID, Bitmap image)
ArrayFunctions.WriteToArray(_imageHeaderReserved, _headers, _subHeaderLength + 0x24);
_type = ImageType.Transparent;
_rows = EncodeImage(image, _type, (Color[])image.Palette.Entries.Clone(), out _image, out _colors);
- _headerUpdate();
+ _height = (short)_image.Height;
+ _width = (short)_image.Width;
+ _length = _rows.Length + imageDataOffset;
+ UpdateHeader();
+ if (_type == ImageType.Full32bppArgb) _rows = null;
}
/// Create an empty Sub
/// Group ID value
@@ -133,14 +157,15 @@ public Sub(short groupID, short subID)
#region public methods
/// Returns the image given the encoded Rows data and settings
- /// Encoded Rows data
+ /// Encoded image data
/// Image width
/// Image height
/// Defined Color array to be used for the image
/// Encoding protocol used for
/// If is , returns a image.
/// Otherwise the returned image will be .
- /// is ignored and allowed to be null if is .
+ /// is ignored and allowed to be null if is or .
+ /// if is , then must be the LZMA compression data.
public static Bitmap DecodeImage(byte[] rawData, int width, int height, Color[] colors, ImageType type)
{
int offset = 0;
@@ -224,10 +249,26 @@ public static Bitmap DecodeImage(byte[] rawData, int width, int height, Color[]
image.UnlockBits(bdU);
break;
case ImageType.Full32bppArgb:
+ case ImageType.Compressed32bppArgb:
image = new Bitmap(width, height, PixelFormat.Format32bppArgb);
BitmapData bdArgb = GraphicsFunctions.GetBitmapData(image);
- // Don't need to process the rows, it's already raw data
- GraphicsFunctions.CopyBytesToImage(rawData, bdArgb);
+ if (type == ImageType.Full32bppArgb) GraphicsFunctions.CopyBytesToImage(rawData, bdArgb); // already good data
+ else
+ {
+ byte[] lzmaParameters = new byte[5];
+ ArrayFunctions.TrimArray(rawData, 0, lzmaParameters);
+ byte[] pixelsArgb = new byte[bdArgb.Stride * bdArgb.Height];
+ using (var compressedData = new MemoryStream(rawData, 5, rawData.Length - 5))
+ {
+ using (var pixelData = new MemoryStream(pixelsArgb, true))
+ {
+ var decoder = new SevenZip.Compression.LZMA.Decoder();
+ decoder.SetDecoderProperties(lzmaParameters);
+ decoder.Code(compressedData, pixelData, -1, pixelData.Capacity, null);
+ }
+ }
+ GraphicsFunctions.CopyBytesToImage(pixelsArgb, bdArgb);
+ }
image.UnlockBits(bdArgb);
break;
}
@@ -238,14 +279,15 @@ public static Bitmap DecodeImage(byte[] rawData, int width, int height, Color[]
/// The image to encode
/// The encoding protocol to use
/// The color array to use
- /// with unused color indexes removed, or simply the original as if is .
- /// with unused color indexes removed, or null if is .
- /// Unused color indexes are removed from both and , the returned array reflects the trimmed parameters. is ignored and can be null if is .
+ /// with unused color indexes removed, or simply the original as if is or .
+ /// with unused color indexes removed, or null if is or .
+ /// Unused color indexes are removed from both and , the returned array reflects the trimmed parameters. is ignored and can be null if is or .
+ /// If is , then instead of being row data the array is the compressed LZMA data.
/// Encoded byte data of
- /// is null and is not .
+ /// is null and is not or .
public static byte[] EncodeImage(Bitmap image, ImageType type, Color[] colors, out Bitmap trimmedImage, out Color[] trimmedColors)
{
- if (type == ImageType.Full32bppArgb)
+ if (type == ImageType.Full32bppArgb || type == ImageType.Compressed32bppArgb)
{
trimmedImage = new Bitmap(image); // convert to 32bppArgb
BitmapData bdArgb = GraphicsFunctions.GetBitmapData(trimmedImage);
@@ -253,7 +295,19 @@ public static byte[] EncodeImage(Bitmap image, ImageType type, Color[] colors, o
GraphicsFunctions.CopyImageToBytes(bdArgb, pixelsArgb);
trimmedImage.UnlockBits(bdArgb);
trimmedColors = null;
- return pixelsArgb;
+ if (type == ImageType.Full32bppArgb) return pixelsArgb;
+ byte[] rawData;
+ using (var compressedData = new MemoryStream(pixelsArgb.Length + 5))
+ {
+ using (var pixelData = new MemoryStream(pixelsArgb))
+ {
+ var encoder = new SevenZip.Compression.LZMA.Encoder();
+ encoder.WriteCoderProperties(compressedData);
+ encoder.Code(pixelData, compressedData, pixelData.Length, -1, null);
+ }
+ rawData = compressedData.ToArray();
+ }
+ return rawData;
}
byte[] mask = null;
@@ -263,7 +317,7 @@ public static byte[] EncodeImage(Bitmap image, ImageType type, Color[] colors, o
image = GraphicsFunctions.ConvertTo8bpp(image, colors);
else if (image.PixelFormat == PixelFormat.Format32bppArgb) // converting from one Blended to another
{
- mask = _getMaskData(image);
+ mask = getMaskData(image);
image = GraphicsFunctions.ConvertTo8bpp(image, colors);
}
BitmapData bd = GraphicsFunctions.GetBitmapData(image);
@@ -329,7 +383,7 @@ public static byte[] EncodeImage(Bitmap image, ImageType type, Color[] colors, o
int rowOffset = offset++;
byte numOps = 0;
bool transparent = false;
- Color firstColor = Color.Black;
+ //Color firstColor = Color.Black;
for (int x = 0, pos = bd.Stride * y, len = 1; x < bd.Width; )
{
if (len == 1) if (pixels[pos + x] == 0 || mask[pos + x] == 0) transparent = true;
@@ -360,7 +414,7 @@ public static byte[] EncodeImage(Bitmap image, ImageType type, Color[] colors, o
rows[offset++] = Convert.ToByte(len + 0x80);
for (int p = 0; p < len; p++)
{
- Color c = Color.FromArgb(BitConverter.ToInt32(pixels, pos + (x + p) * 4));
+ //Color c = Color.FromArgb(BitConverter.ToInt32(pixels, pos + (x + p) * 4));
rows[offset++] = mask[pos + x + p];
rows[offset++] = pixels[pos + x + p];
}
@@ -389,7 +443,7 @@ public static byte[] EncodeImage(Bitmap image, ImageType type, Color[] colors, o
if (type != ImageType.Transparent)
{
image = new Bitmap(image);
- if (mask != null) image = _applyMaskData(image, mask); // null for Transparent > Blended conversion
+ if (mask != null) image = applyMaskData(image, mask); // null for Transparent > Blended conversion
}
trimmedImage = image;
byte[] temp = new byte[offset];
@@ -407,7 +461,11 @@ public void SetImage(Bitmap image)
if (image.PixelFormat == PixelFormat.Format8bppIndexed) _colors = image.Palette.Entries;
if (_colors == null) _colors = new Bitmap(1, 1, PixelFormat.Format8bppIndexed).Palette.Entries;
_rows = EncodeImage(image, _type, (Color[])_colors.Clone(), out _image, out _colors);
- _headerUpdate();
+ _height = (short)_image.Height;
+ _width = (short)_image.Width;
+ _length = _rows.Length + imageDataOffset;
+ UpdateHeader();
+ if (_type == ImageType.Full32bppArgb) _rows = null;
}
/// Sets the image and transparency of the Sub
/// The image to be used
@@ -442,7 +500,7 @@ public void SetTransparencyMask(Bitmap mask)
byte[] maskData = new byte[bdMask.Stride * bdMask.Height]; // 8 bit
GraphicsFunctions.CopyImageToBytes(bdMask, maskData);
mask.UnlockBits(bdMask); // don't need it anymore
- _image = _applyMaskData(_image, maskData);
+ _image = applyMaskData(_image, maskData);
}
#endregion public methods
@@ -456,21 +514,26 @@ public ImageType Type
get { return _type; }
set
{
- if (value != ImageType.Full32bppArgb && _colors == null)
+ if (value != ImageType.Full32bppArgb && value != ImageType.Compressed32bppArgb && _colors == null)
throw new InvalidOperationException("Cannot change to indexed format prior to assigning color palette.");
- if (_image != null) _rows = EncodeImage(_image, value, (_colors != null ? (Color[])_colors.Clone() : null), out _image, out _colors);
+ if (_image != null)
+ {
+ _rows = EncodeImage(_image, value, (_colors != null ? (Color[])_colors.Clone() : null), out _image, out _colors);
+ _length = _rows.Length + imageDataOffset;
+ }
_type = value;
- _headerUpdate();
+ UpdateHeader();
+ if (_type == ImageType.Full32bppArgb) _rows = null;
}
}
/// Gets the width of the image
/// If is undefined, value is 0
- public short Width { get { return (short)(_image != null ? _image.Width : 0); } }
+ public short Width { get { return _width; } }
/// Gets the height of the image
/// If is undefined, value is 0
- public short Height { get { return (short)(_image != null ? _image.Height : 0); } }
+ public short Height { get { return _height; } }
/// Gets or sets the ID of the parent
public short GroupID
@@ -507,7 +570,7 @@ public Color[] Colors
get { return _colors; }
set
{
- if (value == null && _type != ImageType.Full32bppArgb) throw new ArgumentNullException("Colors", "Colors cannot be null for indexed image types");
+ if (value == null && _type != ImageType.Full32bppArgb && _type != ImageType.Compressed32bppArgb) throw new ArgumentNullException("Colors", "Colors cannot be null for indexed image types");
if (value != null)
{
if (value.Length > 256) throw new ArgumentOutOfRangeException("Colors", "256 colors max");
@@ -519,40 +582,47 @@ public Color[] Colors
for (int i = 0; i < value.Length; i++) pal.Entries[i] = value[i];
_image.Palette = pal;
}
- else if (_type != ImageType.Full32bppArgb)
+ else if (_type != ImageType.Full32bppArgb && _type != ImageType.Compressed32bppArgb)
{
- byte[] maskData = _getMaskData(_image);
+ byte[] maskData = getMaskData(_image);
Bitmap temp8bpp = GraphicsFunctions.ConvertTo8bpp(_image, _colors); //force back to 8bpp
ColorPalette pal = temp8bpp.Palette;
for (int i = 0; i < value.Length; i++) pal.Entries[i] = value[i];
temp8bpp.Palette = pal; // apply new colors
- _image = _applyMaskData(temp8bpp, maskData);
+ _image = applyMaskData(temp8bpp, maskData);
}
else
{
- // existing image is Full32bppARGB
+ // existing image is 32bppARGB
// chances are, if it's ARGB and the user is assigning colors it's the first step towards getting it ready to down-convert
// for that reason, I don't think there's anything we need to do here. Set the pallet, change types later
}
}
}
_colors = value;
- _headerUpdate();
+ UpdateHeader();
}
}
/// Gets the image of the Sub
- public Bitmap Image { get { return _image; } }
+ public Bitmap Image
+ {
+ get
+ {
+ if (_image == null && _rows != null) _image = DecodeImage(_rows, _width, _height, _colors, _type);
+ return _image;
+ }
+ }
#endregion public properties
#region private methods
- internal void _headerUpdate()
+ internal void UpdateHeader()
{
if (_image == null) throw new ArgumentException("Image is undefined");
- byte[] width = BitConverter.GetBytes((short)_image.Width);
- byte[] height = BitConverter.GetBytes((short)_image.Height);
+ byte[] width = BitConverter.GetBytes(_width);
+ byte[] height = BitConverter.GetBytes(_height);
byte[] length = BitConverter.GetBytes(_length);
- byte[] type = BitConverter.GetBytes((short)_type);
+ byte[] type = BitConverter.GetBytes((short)(_type == ImageType.Compressed32bppArgb ? ImageType.Full32bppArgb : _type));
ArrayFunctions.WriteToArray(type, _headers, 0);
ArrayFunctions.WriteToArray(width, _headers, 2);
ArrayFunctions.WriteToArray(height, _headers, 4);
@@ -562,7 +632,7 @@ internal void _headerUpdate()
ArrayFunctions.WriteToArray(length, _headers, 0xE);
ArrayFunctions.WriteToArray(length, _headers, _subHeaderLength);
// imageHeaderLength
- ArrayFunctions.WriteToArray(_imageDataOffset, _headers, _subHeaderLength + 8);
+ ArrayFunctions.WriteToArray(imageDataOffset, _headers, _subHeaderLength + 8);
ArrayFunctions.WriteToArray(length, _headers, _subHeaderLength + 0xC);
ArrayFunctions.WriteToArray(width, _headers, _subHeaderLength + 0x10);
// short 0
@@ -575,10 +645,10 @@ internal void _headerUpdate()
if (_colors != null)
ArrayFunctions.WriteToArray(_colors.Length, _headers, _subHeaderLength + 0x28);
else
- ArrayFunctions.WriteToArray(0, _headers, _subHeaderLength + 0x28);
+ ArrayFunctions.WriteToArray((_type == ImageType.Compressed32bppArgb ? 1 : 0), _headers, _subHeaderLength + 0x28);
}
- static Bitmap _applyMaskData(Bitmap image, byte[] maskData)
+ static Bitmap applyMaskData(Bitmap image, byte[] maskData)
{
int maskStride = image.Width + (image.Width % 4 == 0 ? 0 : 4 - image.Width % 4);
if (maskData.Length != maskStride * image.Height)
@@ -594,7 +664,7 @@ static Bitmap _applyMaskData(Bitmap image, byte[] maskData)
return image;
}
- static byte[] _getMaskData(Bitmap image)
+ static byte[] getMaskData(Bitmap image)
{
BitmapData bdImage = GraphicsFunctions.GetBitmapData(image);
byte[] pixels = new byte[bdImage.Stride * bdImage.Height];
@@ -610,10 +680,7 @@ static byte[] _getMaskData(Bitmap image)
}
#endregion private methods
- /// SubHeader.Length = ImageHeader.ImageDataOffset + _rows.Length
- internal int _length { get { return _imageDataOffset + (_rows != null ? _rows.Length : 0); } }
-
/// ImageHeader.ImageDataOffset = ImageHeaderLength + NumberOfColors * 3
- internal int _imageDataOffset { get { return _imageHeaderLength + (_colors != null ? _colors.Length * 3 : 0); } }
+ internal int imageDataOffset { get { return _imageHeaderLength + (_colors != null ? _colors.Length * 3 : 0); } }
}
}
diff --git a/help/Idmr.ImageFormat.Dat.chm b/help/Idmr.ImageFormat.Dat.chm
index 7049bc3..473021f 100644
Binary files a/help/Idmr.ImageFormat.Dat.chm and b/help/Idmr.ImageFormat.Dat.chm differ