This commit is contained in:
2020-07-04 14:41:25 +08:00
parent 70c346d2c1
commit a8f02e4da5
3748 changed files with 587372 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8fd58bd1583bf46c7bb9e5786ce14a7a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a7bf495be56564ebcbdcbc1e2c12c255
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6620816fa3c2740ccbe19425289a6bb8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,77 @@
using System;
using System.IO;
namespace ICSharpCode.SharpZipLib.BZip2
{
/// <summary>
/// An example class to demonstrate compression and decompression of BZip2 streams.
/// </summary>
public static class BZip2
{
/// <summary>
/// Decompress the <paramref name="inStream">input</paramref> writing
/// uncompressed data to the <paramref name="outStream">output stream</paramref>
/// </summary>
/// <param name="inStream">The readable stream containing data to decompress.</param>
/// <param name="outStream">The output stream to receive the decompressed data.</param>
/// <param name="isStreamOwner">Both streams are closed on completion if true.</param>
public static void Decompress(Stream inStream, Stream outStream, bool isStreamOwner)
{
if (inStream == null || outStream == null)
{
throw new Exception("Null Stream");
}
try
{
using (BZip2InputStream bzipInput = new BZip2InputStream(inStream))
{
bzipInput.IsStreamOwner = isStreamOwner;
Core.StreamUtils.Copy(bzipInput, outStream, new byte[4096]);
}
}
finally
{
if (isStreamOwner)
{
// inStream is closed by the BZip2InputStream if stream owner
outStream.Dispose();
}
}
}
/// <summary>
/// Compress the <paramref name="inStream">input stream</paramref> sending
/// result data to <paramref name="outStream">output stream</paramref>
/// </summary>
/// <param name="inStream">The readable stream to compress.</param>
/// <param name="outStream">The output stream to receive the compressed data.</param>
/// <param name="isStreamOwner">Both streams are closed on completion if true.</param>
/// <param name="level">Block size acts as compression level (1 to 9) with 1 giving
/// the lowest compression and 9 the highest.</param>
public static void Compress(Stream inStream, Stream outStream, bool isStreamOwner, int level)
{
if (inStream == null || outStream == null)
{
throw new Exception("Null Stream");
}
try
{
using (BZip2OutputStream bzipOutput = new BZip2OutputStream(outStream, level))
{
bzipOutput.IsStreamOwner = isStreamOwner;
Core.StreamUtils.Copy(inStream, bzipOutput, new byte[4096]);
}
}
finally
{
if (isStreamOwner)
{
// outStream is closed by the BZip2OutputStream if stream owner
inStream.Dispose();
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 36bf1efd201c149f880b8e2f526472ed
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,121 @@
namespace ICSharpCode.SharpZipLib.BZip2
{
/// <summary>
/// Defines internal values for both compression and decompression
/// </summary>
internal sealed class BZip2Constants
{
/// <summary>
/// Random numbers used to randomise repetitive blocks
/// </summary>
public readonly static int[] RandomNumbers = {
619, 720, 127, 481, 931, 816, 813, 233, 566, 247,
985, 724, 205, 454, 863, 491, 741, 242, 949, 214,
733, 859, 335, 708, 621, 574, 73, 654, 730, 472,
419, 436, 278, 496, 867, 210, 399, 680, 480, 51,
878, 465, 811, 169, 869, 675, 611, 697, 867, 561,
862, 687, 507, 283, 482, 129, 807, 591, 733, 623,
150, 238, 59, 379, 684, 877, 625, 169, 643, 105,
170, 607, 520, 932, 727, 476, 693, 425, 174, 647,
73, 122, 335, 530, 442, 853, 695, 249, 445, 515,
909, 545, 703, 919, 874, 474, 882, 500, 594, 612,
641, 801, 220, 162, 819, 984, 589, 513, 495, 799,
161, 604, 958, 533, 221, 400, 386, 867, 600, 782,
382, 596, 414, 171, 516, 375, 682, 485, 911, 276,
98, 553, 163, 354, 666, 933, 424, 341, 533, 870,
227, 730, 475, 186, 263, 647, 537, 686, 600, 224,
469, 68, 770, 919, 190, 373, 294, 822, 808, 206,
184, 943, 795, 384, 383, 461, 404, 758, 839, 887,
715, 67, 618, 276, 204, 918, 873, 777, 604, 560,
951, 160, 578, 722, 79, 804, 96, 409, 713, 940,
652, 934, 970, 447, 318, 353, 859, 672, 112, 785,
645, 863, 803, 350, 139, 93, 354, 99, 820, 908,
609, 772, 154, 274, 580, 184, 79, 626, 630, 742,
653, 282, 762, 623, 680, 81, 927, 626, 789, 125,
411, 521, 938, 300, 821, 78, 343, 175, 128, 250,
170, 774, 972, 275, 999, 639, 495, 78, 352, 126,
857, 956, 358, 619, 580, 124, 737, 594, 701, 612,
669, 112, 134, 694, 363, 992, 809, 743, 168, 974,
944, 375, 748, 52, 600, 747, 642, 182, 862, 81,
344, 805, 988, 739, 511, 655, 814, 334, 249, 515,
897, 955, 664, 981, 649, 113, 974, 459, 893, 228,
433, 837, 553, 268, 926, 240, 102, 654, 459, 51,
686, 754, 806, 760, 493, 403, 415, 394, 687, 700,
946, 670, 656, 610, 738, 392, 760, 799, 887, 653,
978, 321, 576, 617, 626, 502, 894, 679, 243, 440,
680, 879, 194, 572, 640, 724, 926, 56, 204, 700,
707, 151, 457, 449, 797, 195, 791, 558, 945, 679,
297, 59, 87, 824, 713, 663, 412, 693, 342, 606,
134, 108, 571, 364, 631, 212, 174, 643, 304, 329,
343, 97, 430, 751, 497, 314, 983, 374, 822, 928,
140, 206, 73, 263, 980, 736, 876, 478, 430, 305,
170, 514, 364, 692, 829, 82, 855, 953, 676, 246,
369, 970, 294, 750, 807, 827, 150, 790, 288, 923,
804, 378, 215, 828, 592, 281, 565, 555, 710, 82,
896, 831, 547, 261, 524, 462, 293, 465, 502, 56,
661, 821, 976, 991, 658, 869, 905, 758, 745, 193,
768, 550, 608, 933, 378, 286, 215, 979, 792, 961,
61, 688, 793, 644, 986, 403, 106, 366, 905, 644,
372, 567, 466, 434, 645, 210, 389, 550, 919, 135,
780, 773, 635, 389, 707, 100, 626, 958, 165, 504,
920, 176, 193, 713, 857, 265, 203, 50, 668, 108,
645, 990, 626, 197, 510, 357, 358, 850, 858, 364,
936, 638
};
/// <summary>
/// When multiplied by compression parameter (1-9) gives the block size for compression
/// 9 gives the best compression but uses the most memory.
/// </summary>
public const int BaseBlockSize = 100000;
/// <summary>
/// Backend constant
/// </summary>
public const int MaximumAlphaSize = 258;
/// <summary>
/// Backend constant
/// </summary>
public const int MaximumCodeLength = 23;
/// <summary>
/// Backend constant
/// </summary>
public const int RunA = 0;
/// <summary>
/// Backend constant
/// </summary>
public const int RunB = 1;
/// <summary>
/// Backend constant
/// </summary>
public const int GroupCount = 6;
/// <summary>
/// Backend constant
/// </summary>
public const int GroupSize = 50;
/// <summary>
/// Backend constant
/// </summary>
public const int NumberOfIterations = 4;
/// <summary>
/// Backend constant
/// </summary>
public const int MaximumSelectors = (2 + (900000 / GroupSize));
/// <summary>
/// Backend constant
/// </summary>
public const int OvershootBytes = 20;
private BZip2Constants()
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5116b115cc34f4a3a85a38e46a774cf5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,54 @@
using System;
using System.Runtime.Serialization;
namespace ICSharpCode.SharpZipLib.BZip2
{
/// <summary>
/// BZip2Exception represents exceptions specific to BZip2 classes and code.
/// </summary>
[Serializable]
public class BZip2Exception : SharpZipBaseException
{
/// <summary>
/// Initialise a new instance of <see cref="BZip2Exception" />.
/// </summary>
public BZip2Exception()
{
}
/// <summary>
/// Initialise a new instance of <see cref="BZip2Exception" /> with its message string.
/// </summary>
/// <param name="message">A <see cref="string"/> that describes the error.</param>
public BZip2Exception(string message)
: base(message)
{
}
/// <summary>
/// Initialise a new instance of <see cref="BZip2Exception" />.
/// </summary>
/// <param name="message">A <see cref="string"/> that describes the error.</param>
/// <param name="innerException">The <see cref="Exception"/> that caused this exception.</param>
public BZip2Exception(string message, Exception innerException)
: base(message, innerException)
{
}
/// <summary>
/// Initializes a new instance of the BZip2Exception class with serialized data.
/// </summary>
/// <param name="info">
/// The System.Runtime.Serialization.SerializationInfo that holds the serialized
/// object data about the exception being thrown.
/// </param>
/// <param name="context">
/// The System.Runtime.Serialization.StreamingContext that contains contextual information
/// about the source or destination.
/// </param>
protected BZip2Exception(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ec6844725099c40c6a4d26affd2a02b1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: afae2e6e565a346c7aa09133811d5dbe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5183bd4f3e1234db795ab7f73a5041a0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e986f27051f504b16ad06e55a657110b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,163 @@
using System;
namespace ICSharpCode.SharpZipLib.Checksum
{
/// <summary>
/// Computes Adler32 checksum for a stream of data. An Adler32
/// checksum is not as reliable as a CRC32 checksum, but a lot faster to
/// compute.
///
/// The specification for Adler32 may be found in RFC 1950.
/// ZLIB Compressed Data Format Specification version 3.3)
///
///
/// From that document:
///
/// "ADLER32 (Adler-32 checksum)
/// This contains a checksum value of the uncompressed data
/// (excluding any dictionary data) computed according to Adler-32
/// algorithm. This algorithm is a 32-bit extension and improvement
/// of the Fletcher algorithm, used in the ITU-T X.224 / ISO 8073
/// standard.
///
/// Adler-32 is composed of two sums accumulated per byte: s1 is
/// the sum of all bytes, s2 is the sum of all s1 values. Both sums
/// are done modulo 65521. s1 is initialized to 1, s2 to zero. The
/// Adler-32 checksum is stored as s2*65536 + s1 in most-
/// significant-byte first (network) order."
///
/// "8.2. The Adler-32 algorithm
///
/// The Adler-32 algorithm is much faster than the CRC32 algorithm yet
/// still provides an extremely low probability of undetected errors.
///
/// The modulo on unsigned long accumulators can be delayed for 5552
/// bytes, so the modulo operation time is negligible. If the bytes
/// are a, b, c, the second sum is 3a + 2b + c + 3, and so is position
/// and order sensitive, unlike the first sum, which is just a
/// checksum. That 65521 is prime is important to avoid a possible
/// large class of two-byte errors that leave the check unchanged.
/// (The Fletcher checksum uses 255, which is not prime and which also
/// makes the Fletcher check insensitive to single byte changes 0 -
/// 255.)
///
/// The sum s1 is initialized to 1 instead of zero to make the length
/// of the sequence part of s2, so that the length does not have to be
/// checked separately. (Any sequence of zeroes has a Fletcher
/// checksum of zero.)"
/// </summary>
/// <see cref="ICSharpCode.SharpZipLib.Zip.Compression.Streams.InflaterInputStream"/>
/// <see cref="ICSharpCode.SharpZipLib.Zip.Compression.Streams.DeflaterOutputStream"/>
public sealed class Adler32 : IChecksum
{
#region Instance Fields
/// <summary>
/// largest prime smaller than 65536
/// </summary>
private static readonly uint BASE = 65521;
/// <summary>
/// The CRC data checksum so far.
/// </summary>
private uint checkValue;
#endregion Instance Fields
/// <summary>
/// Initialise a default instance of <see cref="Adler32"></see>
/// </summary>
public Adler32()
{
Reset();
}
/// <summary>
/// Resets the Adler32 data checksum as if no update was ever called.
/// </summary>
public void Reset()
{
checkValue = 1;
}
/// <summary>
/// Returns the Adler32 data checksum computed so far.
/// </summary>
public long Value
{
get
{
return checkValue;
}
}
/// <summary>
/// Updates the checksum with the byte b.
/// </summary>
/// <param name="bval">
/// The data value to add. The high byte of the int is ignored.
/// </param>
public void Update(int bval)
{
// We could make a length 1 byte array and call update again, but I
// would rather not have that overhead
uint s1 = checkValue & 0xFFFF;
uint s2 = checkValue >> 16;
s1 = (s1 + ((uint)bval & 0xFF)) % BASE;
s2 = (s1 + s2) % BASE;
checkValue = (s2 << 16) + s1;
}
/// <summary>
/// Updates the Adler32 data checksum with the bytes taken from
/// a block of data.
/// </summary>
/// <param name="buffer">Contains the data to update the checksum with.</param>
public void Update(byte[] buffer)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
Update(new ArraySegment<byte>(buffer, 0, buffer.Length));
}
/// <summary>
/// Update Adler32 data checksum based on a portion of a block of data
/// </summary>
/// <param name = "segment">
/// The chunk of data to add
/// </param>
public void Update(ArraySegment<byte> segment)
{
//(By Per Bothner)
uint s1 = checkValue & 0xFFFF;
uint s2 = checkValue >> 16;
var count = segment.Count;
var offset = segment.Offset;
while (count > 0)
{
// We can defer the modulo operation:
// s1 maximally grows from 65521 to 65521 + 255 * 3800
// s2 maximally grows by 3800 * median(s1) = 2090079800 < 2^31
int n = 3800;
if (n > count)
{
n = count;
}
count -= n;
while (--n >= 0)
{
s1 = s1 + (uint)(segment.Array[offset++] & 0xff);
s2 = s2 + s1;
}
s1 %= BASE;
s2 %= BASE;
}
checkValue = (s2 << 16) | s1;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b4352f5a246054cbfb026d92f47742e5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,187 @@
using System;
namespace ICSharpCode.SharpZipLib.Checksum
{
/// <summary>
/// CRC-32 with unreversed data and reversed output
/// </summary>
/// <remarks>
/// Generate a table for a byte-wise 32-bit CRC calculation on the polynomial:
/// x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0.
///
/// Polynomials over GF(2) are represented in binary, one bit per coefficient,
/// with the lowest powers in the most significant bit. Then adding polynomials
/// is just exclusive-or, and multiplying a polynomial by x is a right shift by
/// one. If we call the above polynomial p, and represent a byte as the
/// polynomial q, also with the lowest power in the most significant bit (so the
/// byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p,
/// where a mod b means the remainder after dividing a by b.
///
/// This calculation is done using the shift-register method of multiplying and
/// taking the remainder. The register is initialized to zero, and for each
/// incoming bit, x^32 is added mod p to the register if the bit is a one (where
/// x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by
/// x (which is shifting right by one and adding x^32 mod p if the bit shifted
/// out is a one). We start with the highest power (least significant bit) of
/// q and repeat for all eight bits of q.
///
/// The table is simply the CRC of all possible eight bit values. This is all
/// the information needed to generate CRC's on data a byte at a time for all
/// combinations of CRC register values and incoming bytes.
/// </remarks>
public sealed class BZip2Crc : IChecksum
{
#region Instance Fields
private const uint crcInit = 0xFFFFFFFF;
//const uint crcXor = 0x00000000;
private static readonly uint[] crcTable = {
0X00000000, 0X04C11DB7, 0X09823B6E, 0X0D4326D9,
0X130476DC, 0X17C56B6B, 0X1A864DB2, 0X1E475005,
0X2608EDB8, 0X22C9F00F, 0X2F8AD6D6, 0X2B4BCB61,
0X350C9B64, 0X31CD86D3, 0X3C8EA00A, 0X384FBDBD,
0X4C11DB70, 0X48D0C6C7, 0X4593E01E, 0X4152FDA9,
0X5F15ADAC, 0X5BD4B01B, 0X569796C2, 0X52568B75,
0X6A1936C8, 0X6ED82B7F, 0X639B0DA6, 0X675A1011,
0X791D4014, 0X7DDC5DA3, 0X709F7B7A, 0X745E66CD,
0X9823B6E0, 0X9CE2AB57, 0X91A18D8E, 0X95609039,
0X8B27C03C, 0X8FE6DD8B, 0X82A5FB52, 0X8664E6E5,
0XBE2B5B58, 0XBAEA46EF, 0XB7A96036, 0XB3687D81,
0XAD2F2D84, 0XA9EE3033, 0XA4AD16EA, 0XA06C0B5D,
0XD4326D90, 0XD0F37027, 0XDDB056FE, 0XD9714B49,
0XC7361B4C, 0XC3F706FB, 0XCEB42022, 0XCA753D95,
0XF23A8028, 0XF6FB9D9F, 0XFBB8BB46, 0XFF79A6F1,
0XE13EF6F4, 0XE5FFEB43, 0XE8BCCD9A, 0XEC7DD02D,
0X34867077, 0X30476DC0, 0X3D044B19, 0X39C556AE,
0X278206AB, 0X23431B1C, 0X2E003DC5, 0X2AC12072,
0X128E9DCF, 0X164F8078, 0X1B0CA6A1, 0X1FCDBB16,
0X018AEB13, 0X054BF6A4, 0X0808D07D, 0X0CC9CDCA,
0X7897AB07, 0X7C56B6B0, 0X71159069, 0X75D48DDE,
0X6B93DDDB, 0X6F52C06C, 0X6211E6B5, 0X66D0FB02,
0X5E9F46BF, 0X5A5E5B08, 0X571D7DD1, 0X53DC6066,
0X4D9B3063, 0X495A2DD4, 0X44190B0D, 0X40D816BA,
0XACA5C697, 0XA864DB20, 0XA527FDF9, 0XA1E6E04E,
0XBFA1B04B, 0XBB60ADFC, 0XB6238B25, 0XB2E29692,
0X8AAD2B2F, 0X8E6C3698, 0X832F1041, 0X87EE0DF6,
0X99A95DF3, 0X9D684044, 0X902B669D, 0X94EA7B2A,
0XE0B41DE7, 0XE4750050, 0XE9362689, 0XEDF73B3E,
0XF3B06B3B, 0XF771768C, 0XFA325055, 0XFEF34DE2,
0XC6BCF05F, 0XC27DEDE8, 0XCF3ECB31, 0XCBFFD686,
0XD5B88683, 0XD1799B34, 0XDC3ABDED, 0XD8FBA05A,
0X690CE0EE, 0X6DCDFD59, 0X608EDB80, 0X644FC637,
0X7A089632, 0X7EC98B85, 0X738AAD5C, 0X774BB0EB,
0X4F040D56, 0X4BC510E1, 0X46863638, 0X42472B8F,
0X5C007B8A, 0X58C1663D, 0X558240E4, 0X51435D53,
0X251D3B9E, 0X21DC2629, 0X2C9F00F0, 0X285E1D47,
0X36194D42, 0X32D850F5, 0X3F9B762C, 0X3B5A6B9B,
0X0315D626, 0X07D4CB91, 0X0A97ED48, 0X0E56F0FF,
0X1011A0FA, 0X14D0BD4D, 0X19939B94, 0X1D528623,
0XF12F560E, 0XF5EE4BB9, 0XF8AD6D60, 0XFC6C70D7,
0XE22B20D2, 0XE6EA3D65, 0XEBA91BBC, 0XEF68060B,
0XD727BBB6, 0XD3E6A601, 0XDEA580D8, 0XDA649D6F,
0XC423CD6A, 0XC0E2D0DD, 0XCDA1F604, 0XC960EBB3,
0XBD3E8D7E, 0XB9FF90C9, 0XB4BCB610, 0XB07DABA7,
0XAE3AFBA2, 0XAAFBE615, 0XA7B8C0CC, 0XA379DD7B,
0X9B3660C6, 0X9FF77D71, 0X92B45BA8, 0X9675461F,
0X8832161A, 0X8CF30BAD, 0X81B02D74, 0X857130C3,
0X5D8A9099, 0X594B8D2E, 0X5408ABF7, 0X50C9B640,
0X4E8EE645, 0X4A4FFBF2, 0X470CDD2B, 0X43CDC09C,
0X7B827D21, 0X7F436096, 0X7200464F, 0X76C15BF8,
0X68860BFD, 0X6C47164A, 0X61043093, 0X65C52D24,
0X119B4BE9, 0X155A565E, 0X18197087, 0X1CD86D30,
0X029F3D35, 0X065E2082, 0X0B1D065B, 0X0FDC1BEC,
0X3793A651, 0X3352BBE6, 0X3E119D3F, 0X3AD08088,
0X2497D08D, 0X2056CD3A, 0X2D15EBE3, 0X29D4F654,
0XC5A92679, 0XC1683BCE, 0XCC2B1D17, 0XC8EA00A0,
0XD6AD50A5, 0XD26C4D12, 0XDF2F6BCB, 0XDBEE767C,
0XE3A1CBC1, 0XE760D676, 0XEA23F0AF, 0XEEE2ED18,
0XF0A5BD1D, 0XF464A0AA, 0XF9278673, 0XFDE69BC4,
0X89B8FD09, 0X8D79E0BE, 0X803AC667, 0X84FBDBD0,
0X9ABC8BD5, 0X9E7D9662, 0X933EB0BB, 0X97FFAD0C,
0XAFB010B1, 0XAB710D06, 0XA6322BDF, 0XA2F33668,
0XBCB4666D, 0XB8757BDA, 0XB5365D03, 0XB1F740B4
};
/// <summary>
/// The CRC data checksum so far.
/// </summary>
private uint checkValue;
#endregion Instance Fields
/// <summary>
/// Initialise a default instance of <see cref="BZip2Crc"></see>
/// </summary>
public BZip2Crc()
{
Reset();
}
/// <summary>
/// Resets the CRC data checksum as if no update was ever called.
/// </summary>
public void Reset()
{
checkValue = crcInit;
}
/// <summary>
/// Returns the CRC data checksum computed so far.
/// </summary>
/// <remarks>Reversed Out = true</remarks>
public long Value
{
get
{
// Tehcnically, the output should be:
//return (long)(~checkValue ^ crcXor);
// but x ^ 0 = x, so there is no point in adding
// the XOR operation
return (long)(~checkValue);
}
}
/// <summary>
/// Updates the checksum with the int bval.
/// </summary>
/// <param name = "bval">
/// the byte is taken as the lower 8 bits of bval
/// </param>
/// <remarks>Reversed Data = false</remarks>
public void Update(int bval)
{
checkValue = unchecked(crcTable[(byte)(((checkValue >> 24) & 0xFF) ^ bval)] ^ (checkValue << 8));
}
/// <summary>
/// Updates the CRC data checksum with the bytes taken from
/// a block of data.
/// </summary>
/// <param name="buffer">Contains the data to update the CRC with.</param>
public void Update(byte[] buffer)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
Update(new ArraySegment<byte>(buffer, 0, buffer.Length));
}
/// <summary>
/// Update CRC data checksum based on a portion of a block of data
/// </summary>
/// <param name = "segment">
/// The chunk of data to add
/// </param>
public void Update(ArraySegment<byte> segment)
{
var count = segment.Count;
var offset = segment.Offset;
while (--count >= 0)
Update(segment.Array[offset++]);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3db2a91a8f7364eef9269bff9130c910
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,176 @@
using System;
namespace ICSharpCode.SharpZipLib.Checksum
{
/// <summary>
/// CRC-32 with reversed data and unreversed output
/// </summary>
/// <remarks>
/// Generate a table for a byte-wise 32-bit CRC calculation on the polynomial:
/// x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0.
///
/// Polynomials over GF(2) are represented in binary, one bit per coefficient,
/// with the lowest powers in the most significant bit. Then adding polynomials
/// is just exclusive-or, and multiplying a polynomial by x is a right shift by
/// one. If we call the above polynomial p, and represent a byte as the
/// polynomial q, also with the lowest power in the most significant bit (so the
/// byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p,
/// where a mod b means the remainder after dividing a by b.
///
/// This calculation is done using the shift-register method of multiplying and
/// taking the remainder. The register is initialized to zero, and for each
/// incoming bit, x^32 is added mod p to the register if the bit is a one (where
/// x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by
/// x (which is shifting right by one and adding x^32 mod p if the bit shifted
/// out is a one). We start with the highest power (least significant bit) of
/// q and repeat for all eight bits of q.
///
/// The table is simply the CRC of all possible eight bit values. This is all
/// the information needed to generate CRC's on data a byte at a time for all
/// combinations of CRC register values and incoming bytes.
/// </remarks>
public sealed class Crc32 : IChecksum
{
#region Instance Fields
private static readonly uint crcInit = 0xFFFFFFFF;
private static readonly uint crcXor = 0xFFFFFFFF;
private static readonly uint[] crcTable = {
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419,
0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4,
0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07,
0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856,
0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,
0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3,
0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A,
0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599,
0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190,
0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F,
0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E,
0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED,
0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3,
0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A,
0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5,
0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010,
0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17,
0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6,
0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615,
0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344,
0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A,
0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1,
0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C,
0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF,
0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE,
0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31,
0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C,
0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B,
0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1,
0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278,
0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7,
0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66,
0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605,
0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8,
0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B,
0x2D02EF8D
};
/// <summary>
/// The CRC data checksum so far.
/// </summary>
private uint checkValue;
#endregion Instance Fields
internal static uint ComputeCrc32(uint oldCrc, byte bval)
{
return (uint)(Crc32.crcTable[(oldCrc ^ bval) & 0xFF] ^ (oldCrc >> 8));
}
/// <summary>
/// Initialise a default instance of <see cref="Crc32"></see>
/// </summary>
public Crc32()
{
Reset();
}
/// <summary>
/// Resets the CRC data checksum as if no update was ever called.
/// </summary>
public void Reset()
{
checkValue = crcInit;
}
/// <summary>
/// Returns the CRC data checksum computed so far.
/// </summary>
/// <remarks>Reversed Out = false</remarks>
public long Value
{
get
{
return (long)(checkValue ^ crcXor);
}
}
/// <summary>
/// Updates the checksum with the int bval.
/// </summary>
/// <param name = "bval">
/// the byte is taken as the lower 8 bits of bval
/// </param>
/// <remarks>Reversed Data = true</remarks>
public void Update(int bval)
{
checkValue = unchecked(crcTable[(checkValue ^ bval) & 0xFF] ^ (checkValue >> 8));
}
/// <summary>
/// Updates the CRC data checksum with the bytes taken from
/// a block of data.
/// </summary>
/// <param name="buffer">Contains the data to update the CRC with.</param>
public void Update(byte[] buffer)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
Update(new ArraySegment<byte>(buffer, 0, buffer.Length));
}
/// <summary>
/// Update CRC data checksum based on a portion of a block of data
/// </summary>
/// <param name = "segment">
/// The chunk of data to add
/// </param>
public void Update(ArraySegment<byte> segment)
{
var count = segment.Count;
var offset = segment.Offset;
while (--count >= 0)
Update(segment.Array[offset++]);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e2d51386aeb444bcb9fc196b6f437a31
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,51 @@
using System;
namespace ICSharpCode.SharpZipLib.Checksum
{
/// <summary>
/// Interface to compute a data checksum used by checked input/output streams.
/// A data checksum can be updated by one byte or with a byte array. After each
/// update the value of the current checksum can be returned by calling
/// <code>getValue</code>. The complete checksum object can also be reset
/// so it can be used again with new data.
/// </summary>
public interface IChecksum
{
/// <summary>
/// Resets the data checksum as if no update was ever called.
/// </summary>
void Reset();
/// <summary>
/// Returns the data checksum computed so far.
/// </summary>
long Value
{
get;
}
/// <summary>
/// Adds one byte to the data checksum.
/// </summary>
/// <param name = "bval">
/// the data value to add. The high byte of the int is ignored.
/// </param>
void Update(int bval);
/// <summary>
/// Updates the data checksum with the bytes taken from the array.
/// </summary>
/// <param name="buffer">
/// buffer an array of bytes
/// </param>
void Update(byte[] buffer);
/// <summary>
/// Adds the byte array to the data checksum.
/// </summary>
/// <param name = "segment">
/// The chunk of data to add
/// </param>
void Update(ArraySegment<byte> segment);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8bd9f410346c4448789c44e3aa8b6215
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bd3011f324815495f97d4883ccbffc0a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 431785cde2f0a41dab285f155c099cc5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,58 @@
using System;
using System.Runtime.Serialization;
namespace ICSharpCode.SharpZipLib
{
/// <summary>
/// SharpZipBaseException is the base exception class for SharpZipLib.
/// All library exceptions are derived from this.
/// </summary>
/// <remarks>NOTE: Not all exceptions thrown will be derived from this class.
/// A variety of other exceptions are possible for example <see cref="ArgumentNullException"></see></remarks>
[Serializable]
public class SharpZipBaseException : Exception
{
/// <summary>
/// Initializes a new instance of the SharpZipBaseException class.
/// </summary>
public SharpZipBaseException()
{
}
/// <summary>
/// Initializes a new instance of the SharpZipBaseException class with a specified error message.
/// </summary>
/// <param name="message">A message describing the exception.</param>
public SharpZipBaseException(string message)
: base(message)
{
}
/// <summary>
/// Initializes a new instance of the SharpZipBaseException class with a specified
/// error message and a reference to the inner exception that is the cause of this exception.
/// </summary>
/// <param name="message">A message describing the exception.</param>
/// <param name="innerException">The inner exception</param>
public SharpZipBaseException(string message, Exception innerException)
: base(message, innerException)
{
}
/// <summary>
/// Initializes a new instance of the SharpZipBaseException class with serialized data.
/// </summary>
/// <param name="info">
/// The System.Runtime.Serialization.SerializationInfo that holds the serialized
/// object data about the exception being thrown.
/// </param>
/// <param name="context">
/// The System.Runtime.Serialization.StreamingContext that contains contextual information
/// about the source or destination.
/// </param>
protected SharpZipBaseException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2587dc945de194684bd2be78e35cf558
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,50 @@
using System;
using System.Runtime.Serialization;
namespace ICSharpCode.SharpZipLib
{
/// <summary>
/// Indicates that an error occured during decoding of a input stream due to corrupt
/// data or (unintentional) library incompability.
/// </summary>
[Serializable]
public class StreamDecodingException : SharpZipBaseException
{
private const string GenericMessage = "Input stream could not be decoded";
/// <summary>
/// Initializes a new instance of the StreamDecodingException with a generic message
/// </summary>
public StreamDecodingException() : base(GenericMessage) { }
/// <summary>
/// Initializes a new instance of the StreamDecodingException class with a specified error message.
/// </summary>
/// <param name="message">A message describing the exception.</param>
public StreamDecodingException(string message) : base(message) { }
/// <summary>
/// Initializes a new instance of the StreamDecodingException class with a specified
/// error message and a reference to the inner exception that is the cause of this exception.
/// </summary>
/// <param name="message">A message describing the exception.</param>
/// <param name="innerException">The inner exception</param>
public StreamDecodingException(string message, Exception innerException) : base(message, innerException) { }
/// <summary>
/// Initializes a new instance of the StreamDecodingException class with serialized data.
/// </summary>
/// <param name="info">
/// The System.Runtime.Serialization.SerializationInfo that holds the serialized
/// object data about the exception being thrown.
/// </param>
/// <param name="context">
/// The System.Runtime.Serialization.StreamingContext that contains contextual information
/// about the source or destination.
/// </param>
protected StreamDecodingException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5d8f09b3301b941cdac0aab28d67382a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,49 @@
using System;
using System.Runtime.Serialization;
namespace ICSharpCode.SharpZipLib
{
/// <summary>
/// Indicates that the input stream could not decoded due to known library incompability or missing features
/// </summary>
[Serializable]
public class StreamUnsupportedException : StreamDecodingException
{
private const string GenericMessage = "Input stream is in a unsupported format";
/// <summary>
/// Initializes a new instance of the StreamUnsupportedException with a generic message
/// </summary>
public StreamUnsupportedException() : base(GenericMessage) { }
/// <summary>
/// Initializes a new instance of the StreamUnsupportedException class with a specified error message.
/// </summary>
/// <param name="message">A message describing the exception.</param>
public StreamUnsupportedException(string message) : base(message) { }
/// <summary>
/// Initializes a new instance of the StreamUnsupportedException class with a specified
/// error message and a reference to the inner exception that is the cause of this exception.
/// </summary>
/// <param name="message">A message describing the exception.</param>
/// <param name="innerException">The inner exception</param>
public StreamUnsupportedException(string message, Exception innerException) : base(message, innerException) { }
/// <summary>
/// Initializes a new instance of the StreamUnsupportedException class with serialized data.
/// </summary>
/// <param name="info">
/// The System.Runtime.Serialization.SerializationInfo that holds the serialized
/// object data about the exception being thrown.
/// </param>
/// <param name="context">
/// The System.Runtime.Serialization.StreamingContext that contains contextual information
/// about the source or destination.
/// </param>
protected StreamUnsupportedException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0e2eca5bc58fd4df8a5f18ff42cb6426
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,49 @@
using System;
using System.Runtime.Serialization;
namespace ICSharpCode.SharpZipLib
{
/// <summary>
/// Indicates that the input stream could not decoded due to the stream ending before enough data had been provided
/// </summary>
[Serializable]
public class UnexpectedEndOfStreamException : StreamDecodingException
{
private const string GenericMessage = "Input stream ended unexpectedly";
/// <summary>
/// Initializes a new instance of the UnexpectedEndOfStreamException with a generic message
/// </summary>
public UnexpectedEndOfStreamException() : base(GenericMessage) { }
/// <summary>
/// Initializes a new instance of the UnexpectedEndOfStreamException class with a specified error message.
/// </summary>
/// <param name="message">A message describing the exception.</param>
public UnexpectedEndOfStreamException(string message) : base(message) { }
/// <summary>
/// Initializes a new instance of the UnexpectedEndOfStreamException class with a specified
/// error message and a reference to the inner exception that is the cause of this exception.
/// </summary>
/// <param name="message">A message describing the exception.</param>
/// <param name="innerException">The inner exception</param>
public UnexpectedEndOfStreamException(string message, Exception innerException) : base(message, innerException) { }
/// <summary>
/// Initializes a new instance of the UnexpectedEndOfStreamException class with serialized data.
/// </summary>
/// <param name="info">
/// The System.Runtime.Serialization.SerializationInfo that holds the serialized
/// object data about the exception being thrown.
/// </param>
/// <param name="context">
/// The System.Runtime.Serialization.StreamingContext that contains contextual information
/// about the source or destination.
/// </param>
protected UnexpectedEndOfStreamException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7dc02c3f297b946ce9c1125e1eb99f16
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,66 @@
using System;
using System.Runtime.Serialization;
namespace ICSharpCode.SharpZipLib
{
/// <summary>
/// Indicates that a value was outside of the expected range when decoding an input stream
/// </summary>
[Serializable]
public class ValueOutOfRangeException : StreamDecodingException
{
/// <summary>
/// Initializes a new instance of the ValueOutOfRangeException class naming the the causing variable
/// </summary>
/// <param name="nameOfValue">Name of the variable, use: nameof()</param>
public ValueOutOfRangeException(string nameOfValue)
: base($"{nameOfValue} out of range") { }
/// <summary>
/// Initializes a new instance of the ValueOutOfRangeException class naming the the causing variable,
/// it's current value and expected range.
/// </summary>
/// <param name="nameOfValue">Name of the variable, use: nameof()</param>
/// <param name="value">The invalid value</param>
/// <param name="maxValue">Expected maximum value</param>
/// <param name="minValue">Expected minimum value</param>
public ValueOutOfRangeException(string nameOfValue, long value, long maxValue, long minValue = 0)
: this(nameOfValue, value.ToString(), maxValue.ToString(), minValue.ToString()) { }
/// <summary>
/// Initializes a new instance of the ValueOutOfRangeException class naming the the causing variable,
/// it's current value and expected range.
/// </summary>
/// <param name="nameOfValue">Name of the variable, use: nameof()</param>
/// <param name="value">The invalid value</param>
/// <param name="maxValue">Expected maximum value</param>
/// <param name="minValue">Expected minimum value</param>
public ValueOutOfRangeException(string nameOfValue, string value, string maxValue, string minValue = "0") :
base($"{nameOfValue} out of range: {value}, should be {minValue}..{maxValue}")
{ }
private ValueOutOfRangeException()
{
}
private ValueOutOfRangeException(string message, Exception innerException) : base(message, innerException)
{
}
/// <summary>
/// Initializes a new instance of the ValueOutOfRangeException class with serialized data.
/// </summary>
/// <param name="info">
/// The System.Runtime.Serialization.SerializationInfo that holds the serialized
/// object data about the exception being thrown.
/// </param>
/// <param name="context">
/// The System.Runtime.Serialization.StreamingContext that contains contextual information
/// about the source or destination.
/// </param>
protected ValueOutOfRangeException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b3e0095b9d0164e539e75b4eb35e7de1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,545 @@
using System;
namespace ICSharpCode.SharpZipLib.Core
{
#region EventArgs
/// <summary>
/// Event arguments for scanning.
/// </summary>
public class ScanEventArgs : EventArgs
{
#region Constructors
/// <summary>
/// Initialise a new instance of <see cref="ScanEventArgs"/>
/// </summary>
/// <param name="name">The file or directory name.</param>
public ScanEventArgs(string name)
{
name_ = name;
}
#endregion Constructors
/// <summary>
/// The file or directory name for this event.
/// </summary>
public string Name
{
get { return name_; }
}
/// <summary>
/// Get set a value indicating if scanning should continue or not.
/// </summary>
public bool ContinueRunning
{
get { return continueRunning_; }
set { continueRunning_ = value; }
}
#region Instance Fields
private string name_;
private bool continueRunning_ = true;
#endregion Instance Fields
}
/// <summary>
/// Event arguments during processing of a single file or directory.
/// </summary>
public class ProgressEventArgs : EventArgs
{
#region Constructors
/// <summary>
/// Initialise a new instance of <see cref="ScanEventArgs"/>
/// </summary>
/// <param name="name">The file or directory name if known.</param>
/// <param name="processed">The number of bytes processed so far</param>
/// <param name="target">The total number of bytes to process, 0 if not known</param>
public ProgressEventArgs(string name, long processed, long target)
{
name_ = name;
processed_ = processed;
target_ = target;
}
#endregion Constructors
/// <summary>
/// The name for this event if known.
/// </summary>
public string Name
{
get { return name_; }
}
/// <summary>
/// Get set a value indicating wether scanning should continue or not.
/// </summary>
public bool ContinueRunning
{
get { return continueRunning_; }
set { continueRunning_ = value; }
}
/// <summary>
/// Get a percentage representing how much of the <see cref="Target"></see> has been processed
/// </summary>
/// <value>0.0 to 100.0 percent; 0 if target is not known.</value>
public float PercentComplete
{
get
{
float result;
if (target_ <= 0)
{
result = 0;
}
else
{
result = ((float)processed_ / (float)target_) * 100.0f;
}
return result;
}
}
/// <summary>
/// The number of bytes processed so far
/// </summary>
public long Processed
{
get { return processed_; }
}
/// <summary>
/// The number of bytes to process.
/// </summary>
/// <remarks>Target may be 0 or negative if the value isnt known.</remarks>
public long Target
{
get { return target_; }
}
#region Instance Fields
private string name_;
private long processed_;
private long target_;
private bool continueRunning_ = true;
#endregion Instance Fields
}
/// <summary>
/// Event arguments for directories.
/// </summary>
public class DirectoryEventArgs : ScanEventArgs
{
#region Constructors
/// <summary>
/// Initialize an instance of <see cref="DirectoryEventArgs"></see>.
/// </summary>
/// <param name="name">The name for this directory.</param>
/// <param name="hasMatchingFiles">Flag value indicating if any matching files are contained in this directory.</param>
public DirectoryEventArgs(string name, bool hasMatchingFiles)
: base(name)
{
hasMatchingFiles_ = hasMatchingFiles;
}
#endregion Constructors
/// <summary>
/// Get a value indicating if the directory contains any matching files or not.
/// </summary>
public bool HasMatchingFiles
{
get { return hasMatchingFiles_; }
}
private readonly
#region Instance Fields
bool hasMatchingFiles_;
#endregion Instance Fields
}
/// <summary>
/// Arguments passed when scan failures are detected.
/// </summary>
public class ScanFailureEventArgs : EventArgs
{
#region Constructors
/// <summary>
/// Initialise a new instance of <see cref="ScanFailureEventArgs"></see>
/// </summary>
/// <param name="name">The name to apply.</param>
/// <param name="e">The exception to use.</param>
public ScanFailureEventArgs(string name, Exception e)
{
name_ = name;
exception_ = e;
continueRunning_ = true;
}
#endregion Constructors
/// <summary>
/// The applicable name.
/// </summary>
public string Name
{
get { return name_; }
}
/// <summary>
/// The applicable exception.
/// </summary>
public Exception Exception
{
get { return exception_; }
}
/// <summary>
/// Get / set a value indicating wether scanning should continue.
/// </summary>
public bool ContinueRunning
{
get { return continueRunning_; }
set { continueRunning_ = value; }
}
#region Instance Fields
private string name_;
private Exception exception_;
private bool continueRunning_;
#endregion Instance Fields
}
#endregion EventArgs
#region Delegates
/// <summary>
/// Delegate invoked before starting to process a file.
/// </summary>
/// <param name="sender">The source of the event</param>
/// <param name="e">The event arguments.</param>
public delegate void ProcessFileHandler(object sender, ScanEventArgs e);
/// <summary>
/// Delegate invoked during processing of a file or directory
/// </summary>
/// <param name="sender">The source of the event</param>
/// <param name="e">The event arguments.</param>
public delegate void ProgressHandler(object sender, ProgressEventArgs e);
/// <summary>
/// Delegate invoked when a file has been completely processed.
/// </summary>
/// <param name="sender">The source of the event</param>
/// <param name="e">The event arguments.</param>
public delegate void CompletedFileHandler(object sender, ScanEventArgs e);
/// <summary>
/// Delegate invoked when a directory failure is detected.
/// </summary>
/// <param name="sender">The source of the event</param>
/// <param name="e">The event arguments.</param>
public delegate void DirectoryFailureHandler(object sender, ScanFailureEventArgs e);
/// <summary>
/// Delegate invoked when a file failure is detected.
/// </summary>
/// <param name="sender">The source of the event</param>
/// <param name="e">The event arguments.</param>
public delegate void FileFailureHandler(object sender, ScanFailureEventArgs e);
#endregion Delegates
/// <summary>
/// FileSystemScanner provides facilities scanning of files and directories.
/// </summary>
public class FileSystemScanner
{
#region Constructors
/// <summary>
/// Initialise a new instance of <see cref="FileSystemScanner"></see>
/// </summary>
/// <param name="filter">The <see cref="PathFilter">file filter</see> to apply when scanning.</param>
public FileSystemScanner(string filter)
{
fileFilter_ = new PathFilter(filter);
}
/// <summary>
/// Initialise a new instance of <see cref="FileSystemScanner"></see>
/// </summary>
/// <param name="fileFilter">The <see cref="PathFilter">file filter</see> to apply.</param>
/// <param name="directoryFilter">The <see cref="PathFilter"> directory filter</see> to apply.</param>
public FileSystemScanner(string fileFilter, string directoryFilter)
{
fileFilter_ = new PathFilter(fileFilter);
directoryFilter_ = new PathFilter(directoryFilter);
}
/// <summary>
/// Initialise a new instance of <see cref="FileSystemScanner"></see>
/// </summary>
/// <param name="fileFilter">The file <see cref="IScanFilter">filter</see> to apply.</param>
public FileSystemScanner(IScanFilter fileFilter)
{
fileFilter_ = fileFilter;
}
/// <summary>
/// Initialise a new instance of <see cref="FileSystemScanner"></see>
/// </summary>
/// <param name="fileFilter">The file <see cref="IScanFilter">filter</see> to apply.</param>
/// <param name="directoryFilter">The directory <see cref="IScanFilter">filter</see> to apply.</param>
public FileSystemScanner(IScanFilter fileFilter, IScanFilter directoryFilter)
{
fileFilter_ = fileFilter;
directoryFilter_ = directoryFilter;
}
#endregion Constructors
#region Delegates
/// <summary>
/// Delegate to invoke when a directory is processed.
/// </summary>
public event EventHandler<DirectoryEventArgs> ProcessDirectory;
/// <summary>
/// Delegate to invoke when a file is processed.
/// </summary>
public ProcessFileHandler ProcessFile;
/// <summary>
/// Delegate to invoke when processing for a file has finished.
/// </summary>
public CompletedFileHandler CompletedFile;
/// <summary>
/// Delegate to invoke when a directory failure is detected.
/// </summary>
public DirectoryFailureHandler DirectoryFailure;
/// <summary>
/// Delegate to invoke when a file failure is detected.
/// </summary>
public FileFailureHandler FileFailure;
#endregion Delegates
/// <summary>
/// Raise the DirectoryFailure event.
/// </summary>
/// <param name="directory">The directory name.</param>
/// <param name="e">The exception detected.</param>
private bool OnDirectoryFailure(string directory, Exception e)
{
DirectoryFailureHandler handler = DirectoryFailure;
bool result = (handler != null);
if (result)
{
var args = new ScanFailureEventArgs(directory, e);
handler(this, args);
alive_ = args.ContinueRunning;
}
return result;
}
/// <summary>
/// Raise the FileFailure event.
/// </summary>
/// <param name="file">The file name.</param>
/// <param name="e">The exception detected.</param>
private bool OnFileFailure(string file, Exception e)
{
FileFailureHandler handler = FileFailure;
bool result = (handler != null);
if (result)
{
var args = new ScanFailureEventArgs(file, e);
FileFailure(this, args);
alive_ = args.ContinueRunning;
}
return result;
}
/// <summary>
/// Raise the ProcessFile event.
/// </summary>
/// <param name="file">The file name.</param>
private void OnProcessFile(string file)
{
ProcessFileHandler handler = ProcessFile;
if (handler != null)
{
var args = new ScanEventArgs(file);
handler(this, args);
alive_ = args.ContinueRunning;
}
}
/// <summary>
/// Raise the complete file event
/// </summary>
/// <param name="file">The file name</param>
private void OnCompleteFile(string file)
{
CompletedFileHandler handler = CompletedFile;
if (handler != null)
{
var args = new ScanEventArgs(file);
handler(this, args);
alive_ = args.ContinueRunning;
}
}
/// <summary>
/// Raise the ProcessDirectory event.
/// </summary>
/// <param name="directory">The directory name.</param>
/// <param name="hasMatchingFiles">Flag indicating if the directory has matching files.</param>
private void OnProcessDirectory(string directory, bool hasMatchingFiles)
{
EventHandler<DirectoryEventArgs> handler = ProcessDirectory;
if (handler != null)
{
var args = new DirectoryEventArgs(directory, hasMatchingFiles);
handler(this, args);
alive_ = args.ContinueRunning;
}
}
/// <summary>
/// Scan a directory.
/// </summary>
/// <param name="directory">The base directory to scan.</param>
/// <param name="recurse">True to recurse subdirectories, false to scan a single directory.</param>
public void Scan(string directory, bool recurse)
{
alive_ = true;
ScanDir(directory, recurse);
}
private void ScanDir(string directory, bool recurse)
{
try
{
string[] names = System.IO.Directory.GetFiles(directory);
bool hasMatch = false;
for (int fileIndex = 0; fileIndex < names.Length; ++fileIndex)
{
if (!fileFilter_.IsMatch(names[fileIndex]))
{
names[fileIndex] = null;
}
else
{
hasMatch = true;
}
}
OnProcessDirectory(directory, hasMatch);
if (alive_ && hasMatch)
{
foreach (string fileName in names)
{
try
{
if (fileName != null)
{
OnProcessFile(fileName);
if (!alive_)
{
break;
}
}
}
catch (Exception e)
{
if (!OnFileFailure(fileName, e))
{
throw;
}
}
}
}
}
catch (Exception e)
{
if (!OnDirectoryFailure(directory, e))
{
throw;
}
}
if (alive_ && recurse)
{
try
{
string[] names = System.IO.Directory.GetDirectories(directory);
foreach (string fulldir in names)
{
if ((directoryFilter_ == null) || (directoryFilter_.IsMatch(fulldir)))
{
ScanDir(fulldir, true);
if (!alive_)
{
break;
}
}
}
}
catch (Exception e)
{
if (!OnDirectoryFailure(directory, e))
{
throw;
}
}
}
}
#region Instance Fields
/// <summary>
/// The file filter currently in use.
/// </summary>
private IScanFilter fileFilter_;
/// <summary>
/// The directory filter currently in use.
/// </summary>
private IScanFilter directoryFilter_;
/// <summary>
/// Flag indicating if scanning should continue running.
/// </summary>
private bool alive_;
#endregion Instance Fields
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 80f791307b83440b2ab80f0752ec3db8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,22 @@
namespace ICSharpCode.SharpZipLib.Core
{
/// <summary>
/// INameTransform defines how file system names are transformed for use with archives, or vice versa.
/// </summary>
public interface INameTransform
{
/// <summary>
/// Given a file name determine the transformed value.
/// </summary>
/// <param name="name">The name to transform.</param>
/// <returns>The transformed file name.</returns>
string TransformFile(string name);
/// <summary>
/// Given a directory name determine the transformed value.
/// </summary>
/// <param name="name">The name to transform.</param>
/// <returns>The transformed directory name</returns>
string TransformDirectory(string name);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9c6a26eb00fdd4295b36936e47da8d74
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,15 @@
namespace ICSharpCode.SharpZipLib.Core
{
/// <summary>
/// Scanning filters support filtering of names.
/// </summary>
public interface IScanFilter
{
/// <summary>
/// Test a name to see if it 'matches' the filter.
/// </summary>
/// <param name="name">The name to test.</param>
/// <returns>Returns true if the name matches the filter, false if it does not match.</returns>
bool IsMatch(string name);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 951bb845e80f34a3ab2fbf7efcd6b611
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,53 @@
using System;
using System.Runtime.Serialization;
namespace ICSharpCode.SharpZipLib.Core
{
/// <summary>
/// InvalidNameException is thrown for invalid names such as directory traversal paths and names with invalid characters
/// </summary>
[Serializable]
public class InvalidNameException : SharpZipBaseException
{
/// <summary>
/// Initializes a new instance of the InvalidNameException class with a default error message.
/// </summary>
public InvalidNameException() : base("An invalid name was specified")
{
}
/// <summary>
/// Initializes a new instance of the InvalidNameException class with a specified error message.
/// </summary>
/// <param name="message">A message describing the exception.</param>
public InvalidNameException(string message) : base(message)
{
}
/// <summary>
/// Initializes a new instance of the InvalidNameException class with a specified
/// error message and a reference to the inner exception that is the cause of this exception.
/// </summary>
/// <param name="message">A message describing the exception.</param>
/// <param name="innerException">The inner exception</param>
public InvalidNameException(string message, Exception innerException) : base(message, innerException)
{
}
/// <summary>
/// Initializes a new instance of the InvalidNameException class with serialized data.
/// </summary>
/// <param name="info">
/// The System.Runtime.Serialization.SerializationInfo that holds the serialized
/// object data about the exception being thrown.
/// </param>
/// <param name="context">
/// The System.Runtime.Serialization.StreamingContext that contains contextual information
/// about the source or destination.
/// </param>
protected InvalidNameException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 565765cd4c7db420b9525a5fafcee1ad
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,284 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace ICSharpCode.SharpZipLib.Core
{
/// <summary>
/// NameFilter is a string matching class which allows for both positive and negative
/// matching.
/// A filter is a sequence of independant <see cref="Regex">regular expressions</see> separated by semi-colons ';'.
/// To include a semi-colon it may be quoted as in \;. Each expression can be prefixed by a plus '+' sign or
/// a minus '-' sign to denote the expression is intended to include or exclude names.
/// If neither a plus or minus sign is found include is the default.
/// A given name is tested for inclusion before checking exclusions. Only names matching an include spec
/// and not matching an exclude spec are deemed to match the filter.
/// An empty filter matches any name.
/// </summary>
/// <example>The following expression includes all name ending in '.dat' with the exception of 'dummy.dat'
/// "+\.dat$;-^dummy\.dat$"
/// </example>
public class NameFilter : IScanFilter
{
#region Constructors
/// <summary>
/// Construct an instance based on the filter expression passed
/// </summary>
/// <param name="filter">The filter expression.</param>
public NameFilter(string filter)
{
filter_ = filter;
inclusions_ = new List<Regex>();
exclusions_ = new List<Regex>();
Compile();
}
#endregion Constructors
/// <summary>
/// Test a string to see if it is a valid regular expression.
/// </summary>
/// <param name="expression">The expression to test.</param>
/// <returns>True if expression is a valid <see cref="System.Text.RegularExpressions.Regex"/> false otherwise.</returns>
public static bool IsValidExpression(string expression)
{
bool result = true;
try
{
var exp = new Regex(expression, RegexOptions.IgnoreCase | RegexOptions.Singleline);
}
catch (ArgumentException)
{
result = false;
}
return result;
}
/// <summary>
/// Test an expression to see if it is valid as a filter.
/// </summary>
/// <param name="toTest">The filter expression to test.</param>
/// <returns>True if the expression is valid, false otherwise.</returns>
public static bool IsValidFilterExpression(string toTest)
{
bool result = true;
try
{
if (toTest != null)
{
string[] items = SplitQuoted(toTest);
for (int i = 0; i < items.Length; ++i)
{
if ((items[i] != null) && (items[i].Length > 0))
{
string toCompile;
if (items[i][0] == '+')
{
toCompile = items[i].Substring(1, items[i].Length - 1);
}
else if (items[i][0] == '-')
{
toCompile = items[i].Substring(1, items[i].Length - 1);
}
else
{
toCompile = items[i];
}
var testRegex = new Regex(toCompile, RegexOptions.IgnoreCase | RegexOptions.Singleline);
}
}
}
}
catch (ArgumentException)
{
result = false;
}
return result;
}
/// <summary>
/// Split a string into its component pieces
/// </summary>
/// <param name="original">The original string</param>
/// <returns>Returns an array of <see cref="T:System.String"/> values containing the individual filter elements.</returns>
public static string[] SplitQuoted(string original)
{
char escape = '\\';
char[] separators = { ';' };
var result = new List<string>();
if (!string.IsNullOrEmpty(original))
{
int endIndex = -1;
var b = new StringBuilder();
while (endIndex < original.Length)
{
endIndex += 1;
if (endIndex >= original.Length)
{
result.Add(b.ToString());
}
else if (original[endIndex] == escape)
{
endIndex += 1;
if (endIndex >= original.Length)
{
throw new ArgumentException("Missing terminating escape character", nameof(original));
}
// include escape if this is not an escaped separator
if (Array.IndexOf(separators, original[endIndex]) < 0)
b.Append(escape);
b.Append(original[endIndex]);
}
else
{
if (Array.IndexOf(separators, original[endIndex]) >= 0)
{
result.Add(b.ToString());
b.Length = 0;
}
else
{
b.Append(original[endIndex]);
}
}
}
}
return result.ToArray();
}
/// <summary>
/// Convert this filter to its string equivalent.
/// </summary>
/// <returns>The string equivalent for this filter.</returns>
public override string ToString()
{
return filter_;
}
/// <summary>
/// Test a value to see if it is included by the filter.
/// </summary>
/// <param name="name">The value to test.</param>
/// <returns>True if the value is included, false otherwise.</returns>
public bool IsIncluded(string name)
{
bool result = false;
if (inclusions_.Count == 0)
{
result = true;
}
else
{
foreach (Regex r in inclusions_)
{
if (r.IsMatch(name))
{
result = true;
break;
}
}
}
return result;
}
/// <summary>
/// Test a value to see if it is excluded by the filter.
/// </summary>
/// <param name="name">The value to test.</param>
/// <returns>True if the value is excluded, false otherwise.</returns>
public bool IsExcluded(string name)
{
bool result = false;
foreach (Regex r in exclusions_)
{
if (r.IsMatch(name))
{
result = true;
break;
}
}
return result;
}
#region IScanFilter Members
/// <summary>
/// Test a value to see if it matches the filter.
/// </summary>
/// <param name="name">The value to test.</param>
/// <returns>True if the value matches, false otherwise.</returns>
public bool IsMatch(string name)
{
return (IsIncluded(name) && !IsExcluded(name));
}
#endregion IScanFilter Members
/// <summary>
/// Compile this filter.
/// </summary>
private void Compile()
{
// TODO: Check to see if combining RE's makes it faster/smaller.
// simple scheme would be to have one RE for inclusion and one for exclusion.
if (filter_ == null)
{
return;
}
string[] items = SplitQuoted(filter_);
for (int i = 0; i < items.Length; ++i)
{
if ((items[i] != null) && (items[i].Length > 0))
{
bool include = (items[i][0] != '-');
string toCompile;
if (items[i][0] == '+')
{
toCompile = items[i].Substring(1, items[i].Length - 1);
}
else if (items[i][0] == '-')
{
toCompile = items[i].Substring(1, items[i].Length - 1);
}
else
{
toCompile = items[i];
}
// NOTE: Regular expressions can fail to compile here for a number of reasons that cause an exception
// these are left unhandled here as the caller is responsible for ensuring all is valid.
// several functions IsValidFilterExpression and IsValidExpression are provided for such checking
if (include)
{
inclusions_.Add(new Regex(toCompile, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline));
}
else
{
exclusions_.Add(new Regex(toCompile, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline));
}
}
}
}
#region Instance Fields
private string filter_;
private List<Regex> inclusions_;
private List<Regex> exclusions_;
#endregion Instance Fields
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d65184d689ab043cda3d982c683699be
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,318 @@
using System;
using System.IO;
namespace ICSharpCode.SharpZipLib.Core
{
/// <summary>
/// PathFilter filters directories and files using a form of <see cref="System.Text.RegularExpressions.Regex">regular expressions</see>
/// by full path name.
/// See <see cref="NameFilter">NameFilter</see> for more detail on filtering.
/// </summary>
public class PathFilter : IScanFilter
{
#region Constructors
/// <summary>
/// Initialise a new instance of <see cref="PathFilter"></see>.
/// </summary>
/// <param name="filter">The <see cref="NameFilter">filter</see> expression to apply.</param>
public PathFilter(string filter)
{
nameFilter_ = new NameFilter(filter);
}
#endregion Constructors
#region IScanFilter Members
/// <summary>
/// Test a name to see if it matches the filter.
/// </summary>
/// <param name="name">The name to test.</param>
/// <returns>True if the name matches, false otherwise.</returns>
/// <remarks><see cref="Path.GetFullPath(string)"/> is used to get the full path before matching.</remarks>
public virtual bool IsMatch(string name)
{
bool result = false;
if (name != null)
{
string cooked = (name.Length > 0) ? Path.GetFullPath(name) : "";
result = nameFilter_.IsMatch(cooked);
}
return result;
}
private readonly
#endregion IScanFilter Members
#region Instance Fields
NameFilter nameFilter_;
#endregion Instance Fields
}
/// <summary>
/// ExtendedPathFilter filters based on name, file size, and the last write time of the file.
/// </summary>
/// <remarks>Provides an example of how to customise filtering.</remarks>
public class ExtendedPathFilter : PathFilter
{
#region Constructors
/// <summary>
/// Initialise a new instance of ExtendedPathFilter.
/// </summary>
/// <param name="filter">The filter to apply.</param>
/// <param name="minSize">The minimum file size to include.</param>
/// <param name="maxSize">The maximum file size to include.</param>
public ExtendedPathFilter(string filter,
long minSize, long maxSize)
: base(filter)
{
MinSize = minSize;
MaxSize = maxSize;
}
/// <summary>
/// Initialise a new instance of ExtendedPathFilter.
/// </summary>
/// <param name="filter">The filter to apply.</param>
/// <param name="minDate">The minimum <see cref="DateTime"/> to include.</param>
/// <param name="maxDate">The maximum <see cref="DateTime"/> to include.</param>
public ExtendedPathFilter(string filter,
DateTime minDate, DateTime maxDate)
: base(filter)
{
MinDate = minDate;
MaxDate = maxDate;
}
/// <summary>
/// Initialise a new instance of ExtendedPathFilter.
/// </summary>
/// <param name="filter">The filter to apply.</param>
/// <param name="minSize">The minimum file size to include.</param>
/// <param name="maxSize">The maximum file size to include.</param>
/// <param name="minDate">The minimum <see cref="DateTime"/> to include.</param>
/// <param name="maxDate">The maximum <see cref="DateTime"/> to include.</param>
public ExtendedPathFilter(string filter,
long minSize, long maxSize,
DateTime minDate, DateTime maxDate)
: base(filter)
{
MinSize = minSize;
MaxSize = maxSize;
MinDate = minDate;
MaxDate = maxDate;
}
#endregion Constructors
#region IScanFilter Members
/// <summary>
/// Test a filename to see if it matches the filter.
/// </summary>
/// <param name="name">The filename to test.</param>
/// <returns>True if the filter matches, false otherwise.</returns>
/// <exception cref="System.IO.FileNotFoundException">The <see paramref="fileName"/> doesnt exist</exception>
public override bool IsMatch(string name)
{
bool result = base.IsMatch(name);
if (result)
{
var fileInfo = new FileInfo(name);
result =
(MinSize <= fileInfo.Length) &&
(MaxSize >= fileInfo.Length) &&
(MinDate <= fileInfo.LastWriteTime) &&
(MaxDate >= fileInfo.LastWriteTime)
;
}
return result;
}
#endregion IScanFilter Members
#region Properties
/// <summary>
/// Get/set the minimum size/length for a file that will match this filter.
/// </summary>
/// <remarks>The default value is zero.</remarks>
/// <exception cref="ArgumentOutOfRangeException">value is less than zero; greater than <see cref="MaxSize"/></exception>
public long MinSize
{
get { return minSize_; }
set
{
if ((value < 0) || (maxSize_ < value))
{
throw new ArgumentOutOfRangeException(nameof(value));
}
minSize_ = value;
}
}
/// <summary>
/// Get/set the maximum size/length for a file that will match this filter.
/// </summary>
/// <remarks>The default value is <see cref="System.Int64.MaxValue"/></remarks>
/// <exception cref="ArgumentOutOfRangeException">value is less than zero or less than <see cref="MinSize"/></exception>
public long MaxSize
{
get { return maxSize_; }
set
{
if ((value < 0) || (minSize_ > value))
{
throw new ArgumentOutOfRangeException(nameof(value));
}
maxSize_ = value;
}
}
/// <summary>
/// Get/set the minimum <see cref="DateTime"/> value that will match for this filter.
/// </summary>
/// <remarks>Files with a LastWrite time less than this value are excluded by the filter.</remarks>
public DateTime MinDate
{
get
{
return minDate_;
}
set
{
if (value > maxDate_)
{
throw new ArgumentOutOfRangeException(nameof(value), "Exceeds MaxDate");
}
minDate_ = value;
}
}
/// <summary>
/// Get/set the maximum <see cref="DateTime"/> value that will match for this filter.
/// </summary>
/// <remarks>Files with a LastWrite time greater than this value are excluded by the filter.</remarks>
public DateTime MaxDate
{
get
{
return maxDate_;
}
set
{
if (minDate_ > value)
{
throw new ArgumentOutOfRangeException(nameof(value), "Exceeds MinDate");
}
maxDate_ = value;
}
}
#endregion Properties
#region Instance Fields
private long minSize_;
private long maxSize_ = long.MaxValue;
private DateTime minDate_ = DateTime.MinValue;
private DateTime maxDate_ = DateTime.MaxValue;
#endregion Instance Fields
}
/// <summary>
/// NameAndSizeFilter filters based on name and file size.
/// </summary>
/// <remarks>A sample showing how filters might be extended.</remarks>
[Obsolete("Use ExtendedPathFilter instead")]
public class NameAndSizeFilter : PathFilter
{
/// <summary>
/// Initialise a new instance of NameAndSizeFilter.
/// </summary>
/// <param name="filter">The filter to apply.</param>
/// <param name="minSize">The minimum file size to include.</param>
/// <param name="maxSize">The maximum file size to include.</param>
public NameAndSizeFilter(string filter, long minSize, long maxSize)
: base(filter)
{
MinSize = minSize;
MaxSize = maxSize;
}
/// <summary>
/// Test a filename to see if it matches the filter.
/// </summary>
/// <param name="name">The filename to test.</param>
/// <returns>True if the filter matches, false otherwise.</returns>
public override bool IsMatch(string name)
{
bool result = base.IsMatch(name);
if (result)
{
var fileInfo = new FileInfo(name);
long length = fileInfo.Length;
result =
(MinSize <= length) &&
(MaxSize >= length);
}
return result;
}
/// <summary>
/// Get/set the minimum size for a file that will match this filter.
/// </summary>
public long MinSize
{
get { return minSize_; }
set
{
if ((value < 0) || (maxSize_ < value))
{
throw new ArgumentOutOfRangeException(nameof(value));
}
minSize_ = value;
}
}
/// <summary>
/// Get/set the maximum size for a file that will match this filter.
/// </summary>
public long MaxSize
{
get { return maxSize_; }
set
{
if ((value < 0) || (minSize_ > value))
{
throw new ArgumentOutOfRangeException(nameof(value));
}
maxSize_ = value;
}
}
#region Instance Fields
private long minSize_;
private long maxSize_ = long.MaxValue;
#endregion Instance Fields
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ef9f01b76ab384a8cb6c754d616c2e83
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,284 @@
using System;
using System.IO;
namespace ICSharpCode.SharpZipLib.Core
{
/// <summary>
/// Provides simple <see cref="Stream"/>" utilities.
/// </summary>
public sealed class StreamUtils
{
/// <summary>
/// Read from a <see cref="Stream"/> ensuring all the required data is read.
/// </summary>
/// <param name="stream">The stream to read.</param>
/// <param name="buffer">The buffer to fill.</param>
/// <seealso cref="ReadFully(Stream,byte[],int,int)"/>
static public void ReadFully(Stream stream, byte[] buffer)
{
ReadFully(stream, buffer, 0, buffer.Length);
}
/// <summary>
/// Read from a <see cref="Stream"/>" ensuring all the required data is read.
/// </summary>
/// <param name="stream">The stream to read data from.</param>
/// <param name="buffer">The buffer to store data in.</param>
/// <param name="offset">The offset at which to begin storing data.</param>
/// <param name="count">The number of bytes of data to store.</param>
/// <exception cref="ArgumentNullException">Required parameter is null</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> and or <paramref name="count"/> are invalid.</exception>
/// <exception cref="EndOfStreamException">End of stream is encountered before all the data has been read.</exception>
static public void ReadFully(Stream stream, byte[] buffer, int offset, int count)
{
if (stream == null)
{
throw new ArgumentNullException(nameof(stream));
}
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
// Offset can equal length when buffer and count are 0.
if ((offset < 0) || (offset > buffer.Length))
{
throw new ArgumentOutOfRangeException(nameof(offset));
}
if ((count < 0) || (offset + count > buffer.Length))
{
throw new ArgumentOutOfRangeException(nameof(count));
}
while (count > 0)
{
int readCount = stream.Read(buffer, offset, count);
if (readCount <= 0)
{
throw new EndOfStreamException();
}
offset += readCount;
count -= readCount;
}
}
/// <summary>
/// Read as much data as possible from a <see cref="Stream"/>", up to the requested number of bytes
/// </summary>
/// <param name="stream">The stream to read data from.</param>
/// <param name="buffer">The buffer to store data in.</param>
/// <param name="offset">The offset at which to begin storing data.</param>
/// <param name="count">The number of bytes of data to store.</param>
/// <exception cref="ArgumentNullException">Required parameter is null</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> and or <paramref name="count"/> are invalid.</exception>
static public int ReadRequestedBytes(Stream stream, byte[] buffer, int offset, int count)
{
if (stream == null)
{
throw new ArgumentNullException(nameof(stream));
}
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
// Offset can equal length when buffer and count are 0.
if ((offset < 0) || (offset > buffer.Length))
{
throw new ArgumentOutOfRangeException(nameof(offset));
}
if ((count < 0) || (offset + count > buffer.Length))
{
throw new ArgumentOutOfRangeException(nameof(count));
}
int totalReadCount = 0;
while (count > 0)
{
int readCount = stream.Read(buffer, offset, count);
if (readCount <= 0)
{
break;
}
offset += readCount;
count -= readCount;
totalReadCount += readCount;
}
return totalReadCount;
}
/// <summary>
/// Copy the contents of one <see cref="Stream"/> to another.
/// </summary>
/// <param name="source">The stream to source data from.</param>
/// <param name="destination">The stream to write data to.</param>
/// <param name="buffer">The buffer to use during copying.</param>
static public void Copy(Stream source, Stream destination, byte[] buffer)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (destination == null)
{
throw new ArgumentNullException(nameof(destination));
}
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
// Ensure a reasonable size of buffer is used without being prohibitive.
if (buffer.Length < 128)
{
throw new ArgumentException("Buffer is too small", nameof(buffer));
}
bool copying = true;
while (copying)
{
int bytesRead = source.Read(buffer, 0, buffer.Length);
if (bytesRead > 0)
{
destination.Write(buffer, 0, bytesRead);
}
else
{
destination.Flush();
copying = false;
}
}
}
/// <summary>
/// Copy the contents of one <see cref="Stream"/> to another.
/// </summary>
/// <param name="source">The stream to source data from.</param>
/// <param name="destination">The stream to write data to.</param>
/// <param name="buffer">The buffer to use during copying.</param>
/// <param name="progressHandler">The <see cref="ProgressHandler">progress handler delegate</see> to use.</param>
/// <param name="updateInterval">The minimum <see cref="TimeSpan"/> between progress updates.</param>
/// <param name="sender">The source for this event.</param>
/// <param name="name">The name to use with the event.</param>
/// <remarks>This form is specialised for use within #Zip to support events during archive operations.</remarks>
static public void Copy(Stream source, Stream destination,
byte[] buffer, ProgressHandler progressHandler, TimeSpan updateInterval, object sender, string name)
{
Copy(source, destination, buffer, progressHandler, updateInterval, sender, name, -1);
}
/// <summary>
/// Copy the contents of one <see cref="Stream"/> to another.
/// </summary>
/// <param name="source">The stream to source data from.</param>
/// <param name="destination">The stream to write data to.</param>
/// <param name="buffer">The buffer to use during copying.</param>
/// <param name="progressHandler">The <see cref="ProgressHandler">progress handler delegate</see> to use.</param>
/// <param name="updateInterval">The minimum <see cref="TimeSpan"/> between progress updates.</param>
/// <param name="sender">The source for this event.</param>
/// <param name="name">The name to use with the event.</param>
/// <param name="fixedTarget">A predetermined fixed target value to use with progress updates.
/// If the value is negative the target is calculated by looking at the stream.</param>
/// <remarks>This form is specialised for use within #Zip to support events during archive operations.</remarks>
static public void Copy(Stream source, Stream destination,
byte[] buffer,
ProgressHandler progressHandler, TimeSpan updateInterval,
object sender, string name, long fixedTarget)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (destination == null)
{
throw new ArgumentNullException(nameof(destination));
}
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
// Ensure a reasonable size of buffer is used without being prohibitive.
if (buffer.Length < 128)
{
throw new ArgumentException("Buffer is too small", nameof(buffer));
}
if (progressHandler == null)
{
throw new ArgumentNullException(nameof(progressHandler));
}
bool copying = true;
DateTime marker = DateTime.Now;
long processed = 0;
long target = 0;
if (fixedTarget >= 0)
{
target = fixedTarget;
}
else if (source.CanSeek)
{
target = source.Length - source.Position;
}
// Always fire 0% progress..
var args = new ProgressEventArgs(name, processed, target);
progressHandler(sender, args);
bool progressFired = true;
while (copying)
{
int bytesRead = source.Read(buffer, 0, buffer.Length);
if (bytesRead > 0)
{
processed += bytesRead;
progressFired = false;
destination.Write(buffer, 0, bytesRead);
}
else
{
destination.Flush();
copying = false;
}
if (DateTime.Now - marker > updateInterval)
{
progressFired = true;
marker = DateTime.Now;
args = new ProgressEventArgs(name, processed, target);
progressHandler(sender, args);
copying = args.ContinueRunning;
}
}
if (!progressFired)
{
args = new ProgressEventArgs(name, processed, target);
progressHandler(sender, args);
}
}
/// <summary>
/// Initialise an instance of <see cref="StreamUtils"></see>
/// </summary>
private StreamUtils()
{
// Do nothing.
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fedc6c8660cce4c318952e309a50456c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,67 @@
namespace ICSharpCode.SharpZipLib.Core
{
/// <summary>
/// WindowsPathUtils provides simple utilities for handling windows paths.
/// </summary>
public abstract class WindowsPathUtils
{
/// <summary>
/// Initializes a new instance of the <see cref="WindowsPathUtils"/> class.
/// </summary>
internal WindowsPathUtils()
{
}
/// <summary>
/// Remove any path root present in the path
/// </summary>
/// <param name="path">A <see cref="string"/> containing path information.</param>
/// <returns>The path with the root removed if it was present; path otherwise.</returns>
/// <remarks>Unlike the <see cref="System.IO.Path"/> class the path isnt otherwise checked for validity.</remarks>
public static string DropPathRoot(string path)
{
string result = path;
if (!string.IsNullOrEmpty(path))
{
if ((path[0] == '\\') || (path[0] == '/'))
{
// UNC name ?
if ((path.Length > 1) && ((path[1] == '\\') || (path[1] == '/')))
{
int index = 2;
int elements = 2;
// Scan for two separate elements \\machine\share\restofpath
while ((index <= path.Length) &&
(((path[index] != '\\') && (path[index] != '/')) || (--elements > 0)))
{
index++;
}
index++;
if (index < path.Length)
{
result = path.Substring(index);
}
else
{
result = "";
}
}
}
else if ((path.Length > 1) && (path[1] == ':'))
{
int dropCount = 2;
if ((path.Length > 2) && ((path[2] == '\\') || (path[2] == '/')))
{
dropCount = 3;
}
result = result.Remove(0, dropCount);
}
}
return result;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3dc3b394f2dd94d92935a1fdef49cd30
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1b1ff9c7d136b4cfeb6874fde63a9e4d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,485 @@
using ICSharpCode.SharpZipLib.Checksum;
using System;
using System.Security.Cryptography;
namespace ICSharpCode.SharpZipLib.Encryption
{
/// <summary>
/// PkzipClassic embodies the classic or original encryption facilities used in Pkzip archives.
/// While it has been superceded by more recent and more powerful algorithms, its still in use and
/// is viable for preventing casual snooping
/// </summary>
public abstract class PkzipClassic : SymmetricAlgorithm
{
/// <summary>
/// Generates new encryption keys based on given seed
/// </summary>
/// <param name="seed">The seed value to initialise keys with.</param>
/// <returns>A new key value.</returns>
static public byte[] GenerateKeys(byte[] seed)
{
if (seed == null)
{
throw new ArgumentNullException(nameof(seed));
}
if (seed.Length == 0)
{
throw new ArgumentException("Length is zero", nameof(seed));
}
uint[] newKeys = {
0x12345678,
0x23456789,
0x34567890
};
for (int i = 0; i < seed.Length; ++i)
{
newKeys[0] = Crc32.ComputeCrc32(newKeys[0], seed[i]);
newKeys[1] = newKeys[1] + (byte)newKeys[0];
newKeys[1] = newKeys[1] * 134775813 + 1;
newKeys[2] = Crc32.ComputeCrc32(newKeys[2], (byte)(newKeys[1] >> 24));
}
byte[] result = new byte[12];
result[0] = (byte)(newKeys[0] & 0xff);
result[1] = (byte)((newKeys[0] >> 8) & 0xff);
result[2] = (byte)((newKeys[0] >> 16) & 0xff);
result[3] = (byte)((newKeys[0] >> 24) & 0xff);
result[4] = (byte)(newKeys[1] & 0xff);
result[5] = (byte)((newKeys[1] >> 8) & 0xff);
result[6] = (byte)((newKeys[1] >> 16) & 0xff);
result[7] = (byte)((newKeys[1] >> 24) & 0xff);
result[8] = (byte)(newKeys[2] & 0xff);
result[9] = (byte)((newKeys[2] >> 8) & 0xff);
result[10] = (byte)((newKeys[2] >> 16) & 0xff);
result[11] = (byte)((newKeys[2] >> 24) & 0xff);
return result;
}
}
/// <summary>
/// PkzipClassicCryptoBase provides the low level facilities for encryption
/// and decryption using the PkzipClassic algorithm.
/// </summary>
internal class PkzipClassicCryptoBase
{
/// <summary>
/// Transform a single byte
/// </summary>
/// <returns>
/// The transformed value
/// </returns>
protected byte TransformByte()
{
uint temp = ((keys[2] & 0xFFFF) | 2);
return (byte)((temp * (temp ^ 1)) >> 8);
}
/// <summary>
/// Set the key schedule for encryption/decryption.
/// </summary>
/// <param name="keyData">The data use to set the keys from.</param>
protected void SetKeys(byte[] keyData)
{
if (keyData == null)
{
throw new ArgumentNullException(nameof(keyData));
}
if (keyData.Length != 12)
{
throw new InvalidOperationException("Key length is not valid");
}
keys = new uint[3];
keys[0] = (uint)((keyData[3] << 24) | (keyData[2] << 16) | (keyData[1] << 8) | keyData[0]);
keys[1] = (uint)((keyData[7] << 24) | (keyData[6] << 16) | (keyData[5] << 8) | keyData[4]);
keys[2] = (uint)((keyData[11] << 24) | (keyData[10] << 16) | (keyData[9] << 8) | keyData[8]);
}
/// <summary>
/// Update encryption keys
/// </summary>
protected void UpdateKeys(byte ch)
{
keys[0] = Crc32.ComputeCrc32(keys[0], ch);
keys[1] = keys[1] + (byte)keys[0];
keys[1] = keys[1] * 134775813 + 1;
keys[2] = Crc32.ComputeCrc32(keys[2], (byte)(keys[1] >> 24));
}
/// <summary>
/// Reset the internal state.
/// </summary>
protected void Reset()
{
keys[0] = 0;
keys[1] = 0;
keys[2] = 0;
}
#region Instance Fields
private uint[] keys;
#endregion Instance Fields
}
/// <summary>
/// PkzipClassic CryptoTransform for encryption.
/// </summary>
internal class PkzipClassicEncryptCryptoTransform : PkzipClassicCryptoBase, ICryptoTransform
{
/// <summary>
/// Initialise a new instance of <see cref="PkzipClassicEncryptCryptoTransform"></see>
/// </summary>
/// <param name="keyBlock">The key block to use.</param>
internal PkzipClassicEncryptCryptoTransform(byte[] keyBlock)
{
SetKeys(keyBlock);
}
#region ICryptoTransform Members
/// <summary>
/// Transforms the specified region of the specified byte array.
/// </summary>
/// <param name="inputBuffer">The input for which to compute the transform.</param>
/// <param name="inputOffset">The offset into the byte array from which to begin using data.</param>
/// <param name="inputCount">The number of bytes in the byte array to use as data.</param>
/// <returns>The computed transform.</returns>
public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
{
byte[] result = new byte[inputCount];
TransformBlock(inputBuffer, inputOffset, inputCount, result, 0);
return result;
}
/// <summary>
/// Transforms the specified region of the input byte array and copies
/// the resulting transform to the specified region of the output byte array.
/// </summary>
/// <param name="inputBuffer">The input for which to compute the transform.</param>
/// <param name="inputOffset">The offset into the input byte array from which to begin using data.</param>
/// <param name="inputCount">The number of bytes in the input byte array to use as data.</param>
/// <param name="outputBuffer">The output to which to write the transform.</param>
/// <param name="outputOffset">The offset into the output byte array from which to begin writing data.</param>
/// <returns>The number of bytes written.</returns>
public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
for (int i = inputOffset; i < inputOffset + inputCount; ++i)
{
byte oldbyte = inputBuffer[i];
outputBuffer[outputOffset++] = (byte)(inputBuffer[i] ^ TransformByte());
UpdateKeys(oldbyte);
}
return inputCount;
}
/// <summary>
/// Gets a value indicating whether the current transform can be reused.
/// </summary>
public bool CanReuseTransform
{
get
{
return true;
}
}
/// <summary>
/// Gets the size of the input data blocks in bytes.
/// </summary>
public int InputBlockSize
{
get
{
return 1;
}
}
/// <summary>
/// Gets the size of the output data blocks in bytes.
/// </summary>
public int OutputBlockSize
{
get
{
return 1;
}
}
/// <summary>
/// Gets a value indicating whether multiple blocks can be transformed.
/// </summary>
public bool CanTransformMultipleBlocks
{
get
{
return true;
}
}
#endregion ICryptoTransform Members
#region IDisposable Members
/// <summary>
/// Cleanup internal state.
/// </summary>
public void Dispose()
{
Reset();
}
#endregion IDisposable Members
}
/// <summary>
/// PkzipClassic CryptoTransform for decryption.
/// </summary>
internal class PkzipClassicDecryptCryptoTransform : PkzipClassicCryptoBase, ICryptoTransform
{
/// <summary>
/// Initialise a new instance of <see cref="PkzipClassicDecryptCryptoTransform"></see>.
/// </summary>
/// <param name="keyBlock">The key block to decrypt with.</param>
internal PkzipClassicDecryptCryptoTransform(byte[] keyBlock)
{
SetKeys(keyBlock);
}
#region ICryptoTransform Members
/// <summary>
/// Transforms the specified region of the specified byte array.
/// </summary>
/// <param name="inputBuffer">The input for which to compute the transform.</param>
/// <param name="inputOffset">The offset into the byte array from which to begin using data.</param>
/// <param name="inputCount">The number of bytes in the byte array to use as data.</param>
/// <returns>The computed transform.</returns>
public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
{
byte[] result = new byte[inputCount];
TransformBlock(inputBuffer, inputOffset, inputCount, result, 0);
return result;
}
/// <summary>
/// Transforms the specified region of the input byte array and copies
/// the resulting transform to the specified region of the output byte array.
/// </summary>
/// <param name="inputBuffer">The input for which to compute the transform.</param>
/// <param name="inputOffset">The offset into the input byte array from which to begin using data.</param>
/// <param name="inputCount">The number of bytes in the input byte array to use as data.</param>
/// <param name="outputBuffer">The output to which to write the transform.</param>
/// <param name="outputOffset">The offset into the output byte array from which to begin writing data.</param>
/// <returns>The number of bytes written.</returns>
public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
for (int i = inputOffset; i < inputOffset + inputCount; ++i)
{
var newByte = (byte)(inputBuffer[i] ^ TransformByte());
outputBuffer[outputOffset++] = newByte;
UpdateKeys(newByte);
}
return inputCount;
}
/// <summary>
/// Gets a value indicating whether the current transform can be reused.
/// </summary>
public bool CanReuseTransform
{
get
{
return true;
}
}
/// <summary>
/// Gets the size of the input data blocks in bytes.
/// </summary>
public int InputBlockSize
{
get
{
return 1;
}
}
/// <summary>
/// Gets the size of the output data blocks in bytes.
/// </summary>
public int OutputBlockSize
{
get
{
return 1;
}
}
/// <summary>
/// Gets a value indicating whether multiple blocks can be transformed.
/// </summary>
public bool CanTransformMultipleBlocks
{
get
{
return true;
}
}
#endregion ICryptoTransform Members
#region IDisposable Members
/// <summary>
/// Cleanup internal state.
/// </summary>
public void Dispose()
{
Reset();
}
#endregion IDisposable Members
}
/// <summary>
/// Defines a wrapper object to access the Pkzip algorithm.
/// This class cannot be inherited.
/// </summary>
public sealed class PkzipClassicManaged : PkzipClassic
{
/// <summary>
/// Get / set the applicable block size in bits.
/// </summary>
/// <remarks>The only valid block size is 8.</remarks>
public override int BlockSize
{
get
{
return 8;
}
set
{
if (value != 8)
{
throw new CryptographicException("Block size is invalid");
}
}
}
/// <summary>
/// Get an array of legal <see cref="KeySizes">key sizes.</see>
/// </summary>
public override KeySizes[] LegalKeySizes
{
get
{
KeySizes[] keySizes = new KeySizes[1];
keySizes[0] = new KeySizes(12 * 8, 12 * 8, 0);
return keySizes;
}
}
/// <summary>
/// Generate an initial vector.
/// </summary>
public override void GenerateIV()
{
// Do nothing.
}
/// <summary>
/// Get an array of legal <see cref="KeySizes">block sizes</see>.
/// </summary>
public override KeySizes[] LegalBlockSizes
{
get
{
KeySizes[] keySizes = new KeySizes[1];
keySizes[0] = new KeySizes(1 * 8, 1 * 8, 0);
return keySizes;
}
}
/// <summary>
/// Get / set the key value applicable.
/// </summary>
public override byte[] Key
{
get
{
if (key_ == null)
{
GenerateKey();
}
return (byte[])key_.Clone();
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
if (value.Length != 12)
{
throw new CryptographicException("Key size is illegal");
}
key_ = (byte[])value.Clone();
}
}
/// <summary>
/// Generate a new random key.
/// </summary>
public override void GenerateKey()
{
key_ = new byte[12];
var rnd = new Random();
rnd.NextBytes(key_);
}
/// <summary>
/// Create an encryptor.
/// </summary>
/// <param name="rgbKey">The key to use for this encryptor.</param>
/// <param name="rgbIV">Initialisation vector for the new encryptor.</param>
/// <returns>Returns a new PkzipClassic encryptor</returns>
public override ICryptoTransform CreateEncryptor(
byte[] rgbKey,
byte[] rgbIV)
{
key_ = rgbKey;
return new PkzipClassicEncryptCryptoTransform(Key);
}
/// <summary>
/// Create a decryptor.
/// </summary>
/// <param name="rgbKey">Keys to use for this new decryptor.</param>
/// <param name="rgbIV">Initialisation vector for the new decryptor.</param>
/// <returns>Returns a new decryptor.</returns>
public override ICryptoTransform CreateDecryptor(
byte[] rgbKey,
byte[] rgbIV)
{
key_ = rgbKey;
return new PkzipClassicDecryptCryptoTransform(Key);
}
#region Instance Fields
private byte[] key_;
#endregion Instance Fields
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d8180cabc4a33448d95217ac63a54690
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,220 @@
using System;
using System.IO;
using System.Security.Cryptography;
using ICSharpCode.SharpZipLib.Core;
namespace ICSharpCode.SharpZipLib.Encryption
{
/// <summary>
/// Encrypts and decrypts AES ZIP
/// </summary>
/// <remarks>
/// Based on information from http://www.winzip.com/aes_info.htm
/// and http://www.gladman.me.uk/cryptography_technology/fileencrypt/
/// </remarks>
internal class ZipAESStream : CryptoStream
{
/// <summary>
/// Constructor
/// </summary>
/// <param name="stream">The stream on which to perform the cryptographic transformation.</param>
/// <param name="transform">Instance of ZipAESTransform</param>
/// <param name="mode">Read or Write</param>
public ZipAESStream(Stream stream, ZipAESTransform transform, CryptoStreamMode mode)
: base(stream, transform, mode)
{
_stream = stream;
_transform = transform;
_slideBuffer = new byte[1024];
// mode:
// CryptoStreamMode.Read means we read from "stream" and pass decrypted to our Read() method.
// Write bypasses this stream and uses the Transform directly.
if (mode != CryptoStreamMode.Read)
{
throw new Exception("ZipAESStream only for read");
}
}
// The final n bytes of the AES stream contain the Auth Code.
private const int AUTH_CODE_LENGTH = 10;
// Blocksize is always 16 here, even for AES-256 which has transform.InputBlockSize of 32.
private const int CRYPTO_BLOCK_SIZE = 16;
// total length of block + auth code
private const int BLOCK_AND_AUTH = CRYPTO_BLOCK_SIZE + AUTH_CODE_LENGTH;
private Stream _stream;
private ZipAESTransform _transform;
private byte[] _slideBuffer;
private int _slideBufStartPos;
private int _slideBufFreePos;
// Buffer block transforms to enable partial reads
private byte[] _transformBuffer = null;// new byte[CRYPTO_BLOCK_SIZE];
private int _transformBufferFreePos;
private int _transformBufferStartPos;
// Do we have some buffered data available?
private bool HasBufferedData =>_transformBuffer != null && _transformBufferStartPos < _transformBufferFreePos;
/// <summary>
/// Reads a sequence of bytes from the current CryptoStream into buffer,
/// and advances the position within the stream by the number of bytes read.
/// </summary>
public override int Read(byte[] buffer, int offset, int count)
{
// Nothing to do
if (count == 0)
return 0;
// If we have buffered data, read that first
int nBytes = 0;
if (HasBufferedData)
{
nBytes = ReadBufferedData(buffer, offset, count);
// Read all requested data from the buffer
if (nBytes == count)
return nBytes;
offset += nBytes;
count -= nBytes;
}
// Read more data from the input, if available
if (_slideBuffer != null)
nBytes += ReadAndTransform(buffer, offset, count);
return nBytes;
}
// Read data from the underlying stream and decrypt it
private int ReadAndTransform(byte[] buffer, int offset, int count)
{
int nBytes = 0;
while (nBytes < count)
{
int bytesLeftToRead = count - nBytes;
// Calculate buffer quantities vs read-ahead size, and check for sufficient free space
int byteCount = _slideBufFreePos - _slideBufStartPos;
// Need to handle final block and Auth Code specially, but don't know total data length.
// Maintain a read-ahead equal to the length of (crypto block + Auth Code).
// When that runs out we can detect these final sections.
int lengthToRead = BLOCK_AND_AUTH - byteCount;
if (_slideBuffer.Length - _slideBufFreePos < lengthToRead)
{
// Shift the data to the beginning of the buffer
int iTo = 0;
for (int iFrom = _slideBufStartPos; iFrom < _slideBufFreePos; iFrom++, iTo++)
{
_slideBuffer[iTo] = _slideBuffer[iFrom];
}
_slideBufFreePos -= _slideBufStartPos; // Note the -=
_slideBufStartPos = 0;
}
int obtained = StreamUtils.ReadRequestedBytes(_stream, _slideBuffer, _slideBufFreePos, lengthToRead);
_slideBufFreePos += obtained;
// Recalculate how much data we now have
byteCount = _slideBufFreePos - _slideBufStartPos;
if (byteCount >= BLOCK_AND_AUTH)
{
var read = TransformAndBufferBlock(buffer, offset, bytesLeftToRead, CRYPTO_BLOCK_SIZE);
nBytes += read;
offset += read;
}
else
{
// Last round.
if (byteCount > AUTH_CODE_LENGTH)
{
// At least one byte of data plus auth code
int finalBlock = byteCount - AUTH_CODE_LENGTH;
nBytes += TransformAndBufferBlock(buffer, offset, bytesLeftToRead, finalBlock);
}
else if (byteCount < AUTH_CODE_LENGTH)
throw new Exception("Internal error missed auth code"); // Coding bug
// Final block done. Check Auth code.
byte[] calcAuthCode = _transform.GetAuthCode();
for (int i = 0; i < AUTH_CODE_LENGTH; i++)
{
if (calcAuthCode[i] != _slideBuffer[_slideBufStartPos + i])
{
throw new Exception("AES Authentication Code does not match. This is a super-CRC check on the data in the file after compression and encryption. \r\n"
+ "The file may be damaged.");
}
}
// don't need this any more, so use it as a 'complete' flag
_slideBuffer = null;
break; // Reached the auth code
}
}
return nBytes;
}
// read some buffered data
private int ReadBufferedData(byte[] buffer, int offset, int count)
{
int copyCount = Math.Min(count, _transformBufferFreePos - _transformBufferStartPos);
Array.Copy(_transformBuffer, _transformBufferStartPos, buffer, offset, count);
_transformBufferStartPos += copyCount;
return copyCount;
}
// Perform the crypto transform, and buffer the data if less than one block has been requested.
private int TransformAndBufferBlock(byte[] buffer, int offset, int count, int blockSize)
{
// If the requested data is greater than one block, transform it directly into the output
// If it's smaller, do it into a temporary buffer and copy the requested part
bool bufferRequired = (blockSize > count);
if (bufferRequired && _transformBuffer == null)
_transformBuffer = new byte[CRYPTO_BLOCK_SIZE];
var targetBuffer = bufferRequired ? _transformBuffer : buffer;
var targetOffset = bufferRequired ? 0 : offset;
// Transform the data
_transform.TransformBlock(_slideBuffer,
_slideBufStartPos,
blockSize,
targetBuffer,
targetOffset);
_slideBufStartPos += blockSize;
if (!bufferRequired)
{
return blockSize;
}
else
{
Array.Copy(_transformBuffer, 0, buffer, offset, count);
_transformBufferStartPos = count;
_transformBufferFreePos = blockSize;
return count;
}
}
/// <summary>
/// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
/// </summary>
/// <param name="buffer">An array of bytes. This method copies count bytes from buffer to the current stream. </param>
/// <param name="offset">The byte offset in buffer at which to begin copying bytes to the current stream. </param>
/// <param name="count">The number of bytes to be written to the current stream. </param>
public override void Write(byte[] buffer, int offset, int count)
{
// ZipAESStream is used for reading but not for writing. Writing uses the ZipAESTransform directly.
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a4f2f5d3f5758466d9a77ea638e41f1e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,223 @@
using System;
using System.Security.Cryptography;
namespace ICSharpCode.SharpZipLib.Encryption
{
/// <summary>
/// Transforms stream using AES in CTR mode
/// </summary>
internal class ZipAESTransform : ICryptoTransform
{
#if NET45
class IncrementalHash : HMACSHA1
{
bool _finalised;
public IncrementalHash(byte[] key) : base(key) { }
public static IncrementalHash CreateHMAC(string n, byte[] key) => new IncrementalHash(key);
public void AppendData(byte[] buffer, int offset, int count) => TransformBlock(buffer, offset, count, buffer, offset);
public byte[] GetHashAndReset()
{
if (!_finalised)
{
byte[] dummy = new byte[0];
TransformFinalBlock(dummy, 0, 0);
_finalised = true;
}
return Hash;
}
}
static class HashAlgorithmName
{
public static string SHA1 = null;
}
#endif
private const int PWD_VER_LENGTH = 2;
// WinZip use iteration count of 1000 for PBKDF2 key generation
private const int KEY_ROUNDS = 1000;
// For 128-bit AES (16 bytes) the encryption is implemented as expected.
// For 256-bit AES (32 bytes) WinZip do full 256 bit AES of the nonce to create the encryption
// block but use only the first 16 bytes of it, and discard the second half.
private const int ENCRYPT_BLOCK = 16;
private int _blockSize;
private readonly ICryptoTransform _encryptor;
private readonly byte[] _counterNonce;
private byte[] _encryptBuffer;
private int _encrPos;
private byte[] _pwdVerifier;
private IncrementalHash _hmacsha1;
private byte[] _authCode = null;
private bool _writeMode;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="key">Password string</param>
/// <param name="saltBytes">Random bytes, length depends on encryption strength.
/// 128 bits = 8 bytes, 192 bits = 12 bytes, 256 bits = 16 bytes.</param>
/// <param name="blockSize">The encryption strength, in bytes eg 16 for 128 bits.</param>
/// <param name="writeMode">True when creating a zip, false when reading. For the AuthCode.</param>
///
public ZipAESTransform(string key, byte[] saltBytes, int blockSize, bool writeMode)
{
if (blockSize != 16 && blockSize != 32) // 24 valid for AES but not supported by Winzip
throw new Exception("Invalid blocksize " + blockSize + ". Must be 16 or 32.");
if (saltBytes.Length != blockSize / 2)
throw new Exception("Invalid salt len. Must be " + blockSize / 2 + " for blocksize " + blockSize);
// initialise the encryption buffer and buffer pos
_blockSize = blockSize;
_encryptBuffer = new byte[_blockSize];
_encrPos = ENCRYPT_BLOCK;
// Performs the equivalent of derive_key in Dr Brian Gladman's pwd2key.c
var pdb = new Rfc2898DeriveBytes(key, saltBytes, KEY_ROUNDS);
var rm = Aes.Create();
rm.Mode = CipherMode.ECB; // No feedback from cipher for CTR mode
_counterNonce = new byte[_blockSize];
byte[] key1bytes = pdb.GetBytes(_blockSize);
byte[] key2bytes = pdb.GetBytes(_blockSize);
// Use empty IV for AES
_encryptor = rm.CreateEncryptor(key1bytes, new byte[16]);
_pwdVerifier = pdb.GetBytes(PWD_VER_LENGTH);
//
_hmacsha1 = IncrementalHash.CreateHMAC(HashAlgorithmName.SHA1, key2bytes);
_writeMode = writeMode;
}
/// <summary>
/// Implement the ICryptoTransform method.
/// </summary>
public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
// Pass the data stream to the hash algorithm for generating the Auth Code.
// This does not change the inputBuffer. Do this before decryption for read mode.
if (!_writeMode)
{
_hmacsha1.AppendData(inputBuffer, inputOffset, inputCount);
}
// Encrypt with AES in CTR mode. Regards to Dr Brian Gladman for this.
int ix = 0;
while (ix < inputCount)
{
if (_encrPos == ENCRYPT_BLOCK)
{
/* increment encryption nonce */
int j = 0;
while (++_counterNonce[j] == 0)
{
++j;
}
/* encrypt the nonce to form next xor buffer */
_encryptor.TransformBlock(_counterNonce, 0, _blockSize, _encryptBuffer, 0);
_encrPos = 0;
}
outputBuffer[ix + outputOffset] = (byte)(inputBuffer[ix + inputOffset] ^ _encryptBuffer[_encrPos++]);
//
ix++;
}
if (_writeMode)
{
// This does not change the buffer.
_hmacsha1.AppendData(outputBuffer, outputOffset, inputCount);
}
return inputCount;
}
/// <summary>
/// Returns the 2 byte password verifier
/// </summary>
public byte[] PwdVerifier
{
get
{
return _pwdVerifier;
}
}
/// <summary>
/// Returns the 10 byte AUTH CODE to be checked or appended immediately following the AES data stream.
/// </summary>
public byte[] GetAuthCode()
{
if (_authCode == null)
{
_authCode = _hmacsha1.GetHashAndReset();
}
return _authCode;
}
#region ICryptoTransform Members
/// <summary>
/// Not implemented.
/// </summary>
public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
{
if(inputCount > 0)
{
throw new NotImplementedException("TransformFinalBlock is not implemented and inputCount is greater than 0");
}
return new byte[0];
}
/// <summary>
/// Gets the size of the input data blocks in bytes.
/// </summary>
public int InputBlockSize
{
get
{
return _blockSize;
}
}
/// <summary>
/// Gets the size of the output data blocks in bytes.
/// </summary>
public int OutputBlockSize
{
get
{
return _blockSize;
}
}
/// <summary>
/// Gets a value indicating whether multiple blocks can be transformed.
/// </summary>
public bool CanTransformMultipleBlocks
{
get
{
return true;
}
}
/// <summary>
/// Gets a value indicating whether the current transform can be reused.
/// </summary>
public bool CanReuseTransform
{
get
{
return true;
}
}
/// <summary>
/// Cleanup internal state.
/// </summary>
public void Dispose()
{
_encryptor.Dispose();
}
#endregion ICryptoTransform Members
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2019340bbe07d459f9bba918ee00204f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2a8be8e6f57bf4c35a13654079bc298e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,92 @@
using System;
using System.IO;
namespace ICSharpCode.SharpZipLib.GZip
{
using static Zip.Compression.Deflater;
/// <summary>
/// An example class to demonstrate compression and decompression of GZip streams.
/// </summary>
public static class GZip
{
/// <summary>
/// Decompress the <paramref name="inStream">input</paramref> writing
/// uncompressed data to the <paramref name="outStream">output stream</paramref>
/// </summary>
/// <param name="inStream">The readable stream containing data to decompress.</param>
/// <param name="outStream">The output stream to receive the decompressed data.</param>
/// <param name="isStreamOwner">Both streams are closed on completion if true.</param>
/// <exception cref="ArgumentNullException">Input or output stream is null</exception>
public static void Decompress(Stream inStream, Stream outStream, bool isStreamOwner)
{
if (inStream == null)
throw new ArgumentNullException(nameof(inStream), "Input stream is null");
if (outStream == null)
throw new ArgumentNullException(nameof(outStream), "Output stream is null");
try
{
using (GZipInputStream gzipInput = new GZipInputStream(inStream))
{
gzipInput.IsStreamOwner = isStreamOwner;
Core.StreamUtils.Copy(gzipInput, outStream, new byte[4096]);
}
}
finally
{
if (isStreamOwner)
{
// inStream is closed by the GZipInputStream if stream owner
outStream.Dispose();
}
}
}
/// <summary>
/// Compress the <paramref name="inStream">input stream</paramref> sending
/// result data to <paramref name="outStream">output stream</paramref>
/// </summary>
/// <param name="inStream">The readable stream to compress.</param>
/// <param name="outStream">The output stream to receive the compressed data.</param>
/// <param name="isStreamOwner">Both streams are closed on completion if true.</param>
/// <param name="bufferSize">Deflate buffer size, minimum 512</param>
/// <param name="level">Deflate compression level, 0-9</param>
/// <exception cref="ArgumentNullException">Input or output stream is null</exception>
/// <exception cref="ArgumentOutOfRangeException">Buffer Size is smaller than 512</exception>
/// <exception cref="ArgumentOutOfRangeException">Compression level outside 0-9</exception>
public static void Compress(Stream inStream, Stream outStream, bool isStreamOwner, int bufferSize = 512, int level = 6)
{
if (inStream == null)
throw new ArgumentNullException(nameof(inStream), "Input stream is null");
if (outStream == null)
throw new ArgumentNullException(nameof(outStream), "Output stream is null");
if (bufferSize < 512)
throw new ArgumentOutOfRangeException(nameof(bufferSize), "Deflate buffer size must be >= 512");
if (level < NO_COMPRESSION || level > BEST_COMPRESSION)
throw new ArgumentOutOfRangeException(nameof(level), "Compression level must be 0-9");
try
{
using (GZipOutputStream gzipOutput = new GZipOutputStream(outStream, bufferSize))
{
gzipOutput.SetLevel(level);
gzipOutput.IsStreamOwner = isStreamOwner;
Core.StreamUtils.Copy(inStream, gzipOutput, new byte[bufferSize]);
}
}
finally
{
if (isStreamOwner)
{
// outStream is closed by the GZipOutputStream if stream owner
inStream.Dispose();
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fede8bcc50bbb44ab90bb9541e6fdf54
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,58 @@
namespace ICSharpCode.SharpZipLib.GZip
{
/// <summary>
/// This class contains constants used for gzip.
/// </summary>
sealed public class GZipConstants
{
/// <summary>
/// Magic number found at start of GZIP header
/// </summary>
public const int GZIP_MAGIC = 0x1F8B;
/* The flag byte is divided into individual bits as follows:
bit 0 FTEXT
bit 1 FHCRC
bit 2 FEXTRA
bit 3 FNAME
bit 4 FCOMMENT
bit 5 reserved
bit 6 reserved
bit 7 reserved
*/
/// <summary>
/// Flag bit mask for text
/// </summary>
public const int FTEXT = 0x1;
/// <summary>
/// Flag bitmask for Crc
/// </summary>
public const int FHCRC = 0x2;
/// <summary>
/// Flag bit mask for extra
/// </summary>
public const int FEXTRA = 0x4;
/// <summary>
/// flag bitmask for name
/// </summary>
public const int FNAME = 0x8;
/// <summary>
/// flag bit mask indicating comment is present
/// </summary>
public const int FCOMMENT = 0x10;
/// <summary>
/// Initialise default instance.
/// </summary>
/// <remarks>Constructor is private to prevent instances being created.</remarks>
private GZipConstants()
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6aa6622f860324d43b7bc61243818fb7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,54 @@
using System;
using System.Runtime.Serialization;
namespace ICSharpCode.SharpZipLib.GZip
{
/// <summary>
/// GZipException represents exceptions specific to GZip classes and code.
/// </summary>
[Serializable]
public class GZipException : SharpZipBaseException
{
/// <summary>
/// Initialise a new instance of <see cref="GZipException" />.
/// </summary>
public GZipException()
{
}
/// <summary>
/// Initialise a new instance of <see cref="GZipException" /> with its message string.
/// </summary>
/// <param name="message">A <see cref="string"/> that describes the error.</param>
public GZipException(string message)
: base(message)
{
}
/// <summary>
/// Initialise a new instance of <see cref="GZipException" />.
/// </summary>
/// <param name="message">A <see cref="string"/> that describes the error.</param>
/// <param name="innerException">The <see cref="Exception"/> that caused this exception.</param>
public GZipException(string message, Exception innerException)
: base(message, innerException)
{
}
/// <summary>
/// Initializes a new instance of the GZipException class with serialized data.
/// </summary>
/// <param name="info">
/// The System.Runtime.Serialization.SerializationInfo that holds the serialized
/// object data about the exception being thrown.
/// </param>
/// <param name="context">
/// The System.Runtime.Serialization.StreamingContext that contains contextual information
/// about the source or destination.
/// </param>
protected GZipException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e1df6af64e82941b18c7e29f706b719c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,390 @@
using ICSharpCode.SharpZipLib.Checksum;
using ICSharpCode.SharpZipLib.Zip.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using System;
using System.IO;
namespace ICSharpCode.SharpZipLib.GZip
{
/// <summary>
/// This filter stream is used to decompress a "GZIP" format stream.
/// The "GZIP" format is described baseInputStream RFC 1952.
///
/// author of the original java version : John Leuner
/// </summary>
/// <example> This sample shows how to unzip a gzipped file
/// <code>
/// using System;
/// using System.IO;
///
/// using ICSharpCode.SharpZipLib.Core;
/// using ICSharpCode.SharpZipLib.GZip;
///
/// class MainClass
/// {
/// public static void Main(string[] args)
/// {
/// using (Stream inStream = new GZipInputStream(File.OpenRead(args[0])))
/// using (FileStream outStream = File.Create(Path.GetFileNameWithoutExtension(args[0]))) {
/// byte[] buffer = new byte[4096];
/// StreamUtils.Copy(inStream, outStream, buffer);
/// }
/// }
/// }
/// </code>
/// </example>
public class GZipInputStream : InflaterInputStream
{
#region Instance Fields
/// <summary>
/// CRC-32 value for uncompressed data
/// </summary>
protected Crc32 crc;
/// <summary>
/// Flag to indicate if we've read the GZIP header yet for the current member (block of compressed data).
/// This is tracked per-block as the file is parsed.
/// </summary>
private bool readGZIPHeader;
/// <summary>
/// Flag to indicate if at least one block in a stream with concatenated blocks was read successfully.
/// This allows us to exit gracefully if downstream data is not in gzip format.
/// </summary>
private bool completedLastBlock;
#endregion Instance Fields
#region Constructors
/// <summary>
/// Creates a GZipInputStream with the default buffer size
/// </summary>
/// <param name="baseInputStream">
/// The stream to read compressed data from (baseInputStream GZIP format)
/// </param>
public GZipInputStream(Stream baseInputStream)
: this(baseInputStream, 4096)
{
}
/// <summary>
/// Creates a GZIPInputStream with the specified buffer size
/// </summary>
/// <param name="baseInputStream">
/// The stream to read compressed data from (baseInputStream GZIP format)
/// </param>
/// <param name="size">
/// Size of the buffer to use
/// </param>
public GZipInputStream(Stream baseInputStream, int size)
: base(baseInputStream, new Inflater(true), size)
{
}
#endregion Constructors
#region Stream overrides
/// <summary>
/// Reads uncompressed data into an array of bytes
/// </summary>
/// <param name="buffer">
/// The buffer to read uncompressed data into
/// </param>
/// <param name="offset">
/// The offset indicating where the data should be placed
/// </param>
/// <param name="count">
/// The number of uncompressed bytes to be read
/// </param>
/// <returns>Returns the number of bytes actually read.</returns>
public override int Read(byte[] buffer, int offset, int count)
{
// A GZIP file can contain multiple blocks of compressed data, although this is quite rare.
// A compressed block could potentially be empty, so we need to loop until we reach EOF or
// we find data.
while (true)
{
// If we haven't read the header for this block, read it
if (!readGZIPHeader)
{
// Try to read header. If there is no header (0 bytes available), this is EOF. If there is
// an incomplete header, this will throw an exception.
try
{
if (!ReadHeader())
{
return 0;
}
}
catch (Exception ex) when (completedLastBlock && (ex is GZipException || ex is EndOfStreamException))
{
// if we completed the last block (i.e. we're in a stream that has multiple blocks concatenated
// we want to return gracefully from any header parsing exceptions since sometimes there may
// be trailing garbage on a stream
return 0;
}
}
// Try to read compressed data
int bytesRead = base.Read(buffer, offset, count);
if (bytesRead > 0)
{
crc.Update(new ArraySegment<byte>(buffer, offset, bytesRead));
}
// If this is the end of stream, read the footer
if (inf.IsFinished)
{
ReadFooter();
}
// Attempting to read 0 bytes will never yield any bytesRead, so we return instead of looping forever
if (bytesRead > 0 || count == 0)
{
return bytesRead;
}
}
}
#endregion Stream overrides
#region Support routines
private bool ReadHeader()
{
// Initialize CRC for this block
crc = new Crc32();
// Make sure there is data in file. We can't rely on ReadLeByte() to fill the buffer, as this could be EOF,
// which is fine, but ReadLeByte() throws an exception if it doesn't find data, so we do this part ourselves.
if (inputBuffer.Available <= 0)
{
inputBuffer.Fill();
if (inputBuffer.Available <= 0)
{
// No header, EOF.
return false;
}
}
// 1. Check the two magic bytes
var headCRC = new Crc32();
int magic = inputBuffer.ReadLeByte();
if (magic < 0)
{
throw new EndOfStreamException("EOS reading GZIP header");
}
headCRC.Update(magic);
if (magic != (GZipConstants.GZIP_MAGIC >> 8))
{
throw new GZipException("Error GZIP header, first magic byte doesn't match");
}
//magic = baseInputStream.ReadByte();
magic = inputBuffer.ReadLeByte();
if (magic < 0)
{
throw new EndOfStreamException("EOS reading GZIP header");
}
if (magic != (GZipConstants.GZIP_MAGIC & 0xFF))
{
throw new GZipException("Error GZIP header, second magic byte doesn't match");
}
headCRC.Update(magic);
// 2. Check the compression type (must be 8)
int compressionType = inputBuffer.ReadLeByte();
if (compressionType < 0)
{
throw new EndOfStreamException("EOS reading GZIP header");
}
if (compressionType != 8)
{
throw new GZipException("Error GZIP header, data not in deflate format");
}
headCRC.Update(compressionType);
// 3. Check the flags
int flags = inputBuffer.ReadLeByte();
if (flags < 0)
{
throw new EndOfStreamException("EOS reading GZIP header");
}
headCRC.Update(flags);
/* This flag byte is divided into individual bits as follows:
bit 0 FTEXT
bit 1 FHCRC
bit 2 FEXTRA
bit 3 FNAME
bit 4 FCOMMENT
bit 5 reserved
bit 6 reserved
bit 7 reserved
*/
// 3.1 Check the reserved bits are zero
if ((flags & 0xE0) != 0)
{
throw new GZipException("Reserved flag bits in GZIP header != 0");
}
// 4.-6. Skip the modification time, extra flags, and OS type
for (int i = 0; i < 6; i++)
{
int readByte = inputBuffer.ReadLeByte();
if (readByte < 0)
{
throw new EndOfStreamException("EOS reading GZIP header");
}
headCRC.Update(readByte);
}
// 7. Read extra field
if ((flags & GZipConstants.FEXTRA) != 0)
{
// XLEN is total length of extra subfields, we will skip them all
int len1, len2;
len1 = inputBuffer.ReadLeByte();
len2 = inputBuffer.ReadLeByte();
if ((len1 < 0) || (len2 < 0))
{
throw new EndOfStreamException("EOS reading GZIP header");
}
headCRC.Update(len1);
headCRC.Update(len2);
int extraLen = (len2 << 8) | len1; // gzip is LSB first
for (int i = 0; i < extraLen; i++)
{
int readByte = inputBuffer.ReadLeByte();
if (readByte < 0)
{
throw new EndOfStreamException("EOS reading GZIP header");
}
headCRC.Update(readByte);
}
}
// 8. Read file name
if ((flags & GZipConstants.FNAME) != 0)
{
int readByte;
while ((readByte = inputBuffer.ReadLeByte()) > 0)
{
headCRC.Update(readByte);
}
if (readByte < 0)
{
throw new EndOfStreamException("EOS reading GZIP header");
}
headCRC.Update(readByte);
}
// 9. Read comment
if ((flags & GZipConstants.FCOMMENT) != 0)
{
int readByte;
while ((readByte = inputBuffer.ReadLeByte()) > 0)
{
headCRC.Update(readByte);
}
if (readByte < 0)
{
throw new EndOfStreamException("EOS reading GZIP header");
}
headCRC.Update(readByte);
}
// 10. Read header CRC
if ((flags & GZipConstants.FHCRC) != 0)
{
int tempByte;
int crcval = inputBuffer.ReadLeByte();
if (crcval < 0)
{
throw new EndOfStreamException("EOS reading GZIP header");
}
tempByte = inputBuffer.ReadLeByte();
if (tempByte < 0)
{
throw new EndOfStreamException("EOS reading GZIP header");
}
crcval = (crcval << 8) | tempByte;
if (crcval != ((int)headCRC.Value & 0xffff))
{
throw new GZipException("Header CRC value mismatch");
}
}
readGZIPHeader = true;
return true;
}
private void ReadFooter()
{
byte[] footer = new byte[8];
// End of stream; reclaim all bytes from inf, read the final byte count, and reset the inflator
long bytesRead = inf.TotalOut & 0xffffffff;
inputBuffer.Available += inf.RemainingInput;
inf.Reset();
// Read footer from inputBuffer
int needed = 8;
while (needed > 0)
{
int count = inputBuffer.ReadClearTextBuffer(footer, 8 - needed, needed);
if (count <= 0)
{
throw new EndOfStreamException("EOS reading GZIP footer");
}
needed -= count; // Jewel Jan 16
}
// Calculate CRC
int crcval = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8) | ((footer[2] & 0xff) << 16) | (footer[3] << 24);
if (crcval != (int)crc.Value)
{
throw new GZipException("GZIP crc sum mismatch, theirs \"" + crcval + "\" and ours \"" + (int)crc.Value);
}
// NOTE The total here is the original total modulo 2 ^ 32.
uint total =
(uint)((uint)footer[4] & 0xff) |
(uint)(((uint)footer[5] & 0xff) << 8) |
(uint)(((uint)footer[6] & 0xff) << 16) |
(uint)((uint)footer[7] << 24);
if (bytesRead != total)
{
throw new GZipException("Number of bytes mismatch in footer");
}
// Mark header read as false so if another header exists, we'll continue reading through the file
readGZIPHeader = false;
// Indicate that we succeeded on at least one block so we can exit gracefully if there is trailing garbage downstream
completedLastBlock = true;
}
#endregion Support routines
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6ccd6b70f6c034f9babe0d730642fa04
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,240 @@
using ICSharpCode.SharpZipLib.Checksum;
using ICSharpCode.SharpZipLib.Zip.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using System;
using System.IO;
namespace ICSharpCode.SharpZipLib.GZip
{
/// <summary>
/// This filter stream is used to compress a stream into a "GZIP" stream.
/// The "GZIP" format is described in RFC 1952.
///
/// author of the original java version : John Leuner
/// </summary>
/// <example> This sample shows how to gzip a file
/// <code>
/// using System;
/// using System.IO;
///
/// using ICSharpCode.SharpZipLib.GZip;
/// using ICSharpCode.SharpZipLib.Core;
///
/// class MainClass
/// {
/// public static void Main(string[] args)
/// {
/// using (Stream s = new GZipOutputStream(File.Create(args[0] + ".gz")))
/// using (FileStream fs = File.OpenRead(args[0])) {
/// byte[] writeData = new byte[4096];
/// Streamutils.Copy(s, fs, writeData);
/// }
/// }
/// }
/// }
/// </code>
/// </example>
public class GZipOutputStream : DeflaterOutputStream
{
private enum OutputState
{
Header,
Footer,
Finished,
Closed,
};
#region Instance Fields
/// <summary>
/// CRC-32 value for uncompressed data
/// </summary>
protected Crc32 crc = new Crc32();
private OutputState state_ = OutputState.Header;
#endregion Instance Fields
#region Constructors
/// <summary>
/// Creates a GzipOutputStream with the default buffer size
/// </summary>
/// <param name="baseOutputStream">
/// The stream to read data (to be compressed) from
/// </param>
public GZipOutputStream(Stream baseOutputStream)
: this(baseOutputStream, 4096)
{
}
/// <summary>
/// Creates a GZipOutputStream with the specified buffer size
/// </summary>
/// <param name="baseOutputStream">
/// The stream to read data (to be compressed) from
/// </param>
/// <param name="size">
/// Size of the buffer to use
/// </param>
public GZipOutputStream(Stream baseOutputStream, int size) : base(baseOutputStream, new Deflater(Deflater.DEFAULT_COMPRESSION, true), size)
{
}
#endregion Constructors
#region Public API
/// <summary>
/// Sets the active compression level (0-9). The new level will be activated
/// immediately.
/// </summary>
/// <param name="level">The compression level to set.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Level specified is not supported.
/// </exception>
/// <see cref="Deflater"/>
public void SetLevel(int level)
{
if (level < Deflater.NO_COMPRESSION || level > Deflater.BEST_COMPRESSION)
throw new ArgumentOutOfRangeException(nameof(level), "Compression level must be 0-9");
deflater_.SetLevel(level);
}
/// <summary>
/// Get the current compression level.
/// </summary>
/// <returns>The current compression level.</returns>
public int GetLevel()
{
return deflater_.GetLevel();
}
#endregion Public API
#region Stream overrides
/// <summary>
/// Write given buffer to output updating crc
/// </summary>
/// <param name="buffer">Buffer to write</param>
/// <param name="offset">Offset of first byte in buf to write</param>
/// <param name="count">Number of bytes to write</param>
public override void Write(byte[] buffer, int offset, int count)
{
if (state_ == OutputState.Header)
{
WriteHeader();
}
if (state_ != OutputState.Footer)
{
throw new InvalidOperationException("Write not permitted in current state");
}
crc.Update(new ArraySegment<byte>(buffer, offset, count));
base.Write(buffer, offset, count);
}
/// <summary>
/// Writes remaining compressed output data to the output stream
/// and closes it.
/// </summary>
protected override void Dispose(bool disposing)
{
try
{
Finish();
}
finally
{
if (state_ != OutputState.Closed)
{
state_ = OutputState.Closed;
if (IsStreamOwner)
{
baseOutputStream_.Dispose();
}
}
}
}
#endregion Stream overrides
#region DeflaterOutputStream overrides
/// <summary>
/// Finish compression and write any footer information required to stream
/// </summary>
public override void Finish()
{
// If no data has been written a header should be added.
if (state_ == OutputState.Header)
{
WriteHeader();
}
if (state_ == OutputState.Footer)
{
state_ = OutputState.Finished;
base.Finish();
var totalin = (uint)(deflater_.TotalIn & 0xffffffff);
var crcval = (uint)(crc.Value & 0xffffffff);
byte[] gzipFooter;
unchecked
{
gzipFooter = new byte[] {
(byte) crcval, (byte) (crcval >> 8),
(byte) (crcval >> 16), (byte) (crcval >> 24),
(byte) totalin, (byte) (totalin >> 8),
(byte) (totalin >> 16), (byte) (totalin >> 24)
};
}
baseOutputStream_.Write(gzipFooter, 0, gzipFooter.Length);
}
}
#endregion DeflaterOutputStream overrides
#region Support Routines
private void WriteHeader()
{
if (state_ == OutputState.Header)
{
state_ = OutputState.Footer;
var mod_time = (int)((DateTime.Now.Ticks - new DateTime(1970, 1, 1).Ticks) / 10000000L); // Ticks give back 100ns intervals
byte[] gzipHeader = {
// The two magic bytes
(byte) (GZipConstants.GZIP_MAGIC >> 8), (byte) (GZipConstants.GZIP_MAGIC & 0xff),
// The compression type
(byte) Deflater.DEFLATED,
// The flags (not set)
0,
// The modification time
(byte) mod_time, (byte) (mod_time >> 8),
(byte) (mod_time >> 16), (byte) (mod_time >> 24),
// The extra flags
0,
// The OS type (unknown)
(byte) 255
};
baseOutputStream_.Write(gzipHeader, 0, gzipHeader.Length);
}
}
#endregion Support Routines
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5df203e543fb2446cbad5c0478a1fc2b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e3f71533c9b984a87acd16abd94707ef
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 5ec20aaa34ede4b24996c8efaa77561b
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b2f01f99bb8924d42a113ff1133e73a5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,62 @@
namespace ICSharpCode.SharpZipLib.Lzw
{
/// <summary>
/// This class contains constants used for LZW
/// </summary>
sealed public class LzwConstants
{
/// <summary>
/// Magic number found at start of LZW header: 0x1f 0x9d
/// </summary>
public const int MAGIC = 0x1f9d;
/// <summary>
/// Maximum number of bits per code
/// </summary>
public const int MAX_BITS = 16;
/* 3rd header byte:
* bit 0..4 Number of compression bits
* bit 5 Extended header
* bit 6 Free
* bit 7 Block mode
*/
/// <summary>
/// Mask for 'number of compression bits'
/// </summary>
public const int BIT_MASK = 0x1f;
/// <summary>
/// Indicates the presence of a fourth header byte
/// </summary>
public const int EXTENDED_MASK = 0x20;
//public const int FREE_MASK = 0x40;
/// <summary>
/// Reserved bits
/// </summary>
public const int RESERVED_MASK = 0x60;
/// <summary>
/// Block compression: if table is full and compression rate is dropping,
/// clear the dictionary.
/// </summary>
public const int BLOCK_MODE_MASK = 0x80;
/// <summary>
/// LZW file header size (in bytes)
/// </summary>
public const int HDR_SIZE = 3;
/// <summary>
/// Initial number of bits per code
/// </summary>
public const int INIT_BITS = 9;
private LzwConstants()
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d00c79cf86e3e47acaa10a6293604cf4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,54 @@
using System;
using System.Runtime.Serialization;
namespace ICSharpCode.SharpZipLib.Lzw
{
/// <summary>
/// LzwException represents exceptions specific to LZW classes and code.
/// </summary>
[Serializable]
public class LzwException : SharpZipBaseException
{
/// <summary>
/// Initialise a new instance of <see cref="LzwException" />.
/// </summary>
public LzwException()
{
}
/// <summary>
/// Initialise a new instance of <see cref="LzwException" /> with its message string.
/// </summary>
/// <param name="message">A <see cref="string"/> that describes the error.</param>
public LzwException(string message)
: base(message)
{
}
/// <summary>
/// Initialise a new instance of <see cref="LzwException" />.
/// </summary>
/// <param name="message">A <see cref="string"/> that describes the error.</param>
/// <param name="innerException">The <see cref="Exception"/> that caused this exception.</param>
public LzwException(string message, Exception innerException)
: base(message, innerException)
{
}
/// <summary>
/// Initializes a new instance of the LzwException class with serialized data.
/// </summary>
/// <param name="info">
/// The System.Runtime.Serialization.SerializationInfo that holds the serialized
/// object data about the exception being thrown.
/// </param>
/// <param name="context">
/// The System.Runtime.Serialization.StreamingContext that contains contextual information
/// about the source or destination.
/// </param>
protected LzwException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f7b6f0de997aa46b798577288051edbb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,572 @@
using System;
using System.IO;
namespace ICSharpCode.SharpZipLib.Lzw
{
/// <summary>
/// This filter stream is used to decompress a LZW format stream.
/// Specifically, a stream that uses the LZC compression method.
/// This file format is usually associated with the .Z file extension.
///
/// See http://en.wikipedia.org/wiki/Compress
/// See http://wiki.wxwidgets.org/Development:_Z_File_Format
///
/// The file header consists of 3 (or optionally 4) bytes. The first two bytes
/// contain the magic marker "0x1f 0x9d", followed by a byte of flags.
///
/// Based on Java code by Ronald Tschalar, which in turn was based on the unlzw.c
/// code in the gzip package.
/// </summary>
/// <example> This sample shows how to unzip a compressed file
/// <code>
/// using System;
/// using System.IO;
///
/// using ICSharpCode.SharpZipLib.Core;
/// using ICSharpCode.SharpZipLib.LZW;
///
/// class MainClass
/// {
/// public static void Main(string[] args)
/// {
/// using (Stream inStream = new LzwInputStream(File.OpenRead(args[0])))
/// using (FileStream outStream = File.Create(Path.GetFileNameWithoutExtension(args[0]))) {
/// byte[] buffer = new byte[4096];
/// StreamUtils.Copy(inStream, outStream, buffer);
/// // OR
/// inStream.Read(buffer, 0, buffer.Length);
/// // now do something with the buffer
/// }
/// }
/// }
/// </code>
/// </example>
public class LzwInputStream : Stream
{
/// <summary>
/// Gets or sets a flag indicating ownership of underlying stream.
/// When the flag is true <see cref="Stream.Dispose()" /> will close the underlying stream also.
/// </summary>
/// <remarks>The default value is true.</remarks>
public bool IsStreamOwner { get; set; } = true;
/// <summary>
/// Creates a LzwInputStream
/// </summary>
/// <param name="baseInputStream">
/// The stream to read compressed data from (baseInputStream LZW format)
/// </param>
public LzwInputStream(Stream baseInputStream)
{
this.baseInputStream = baseInputStream;
}
/// <summary>
/// See <see cref="System.IO.Stream.ReadByte"/>
/// </summary>
/// <returns></returns>
public override int ReadByte()
{
int b = Read(one, 0, 1);
if (b == 1)
return (one[0] & 0xff);
return -1;
}
/// <summary>
/// Reads decompressed data into the provided buffer byte array
/// </summary>
/// <param name ="buffer">
/// The array to read and decompress data into
/// </param>
/// <param name ="offset">
/// The offset indicating where the data should be placed
/// </param>
/// <param name ="count">
/// The number of bytes to decompress
/// </param>
/// <returns>The number of bytes read. Zero signals the end of stream</returns>
public override int Read(byte[] buffer, int offset, int count)
{
if (!headerParsed)
ParseHeader();
if (eof)
return 0;
int start = offset;
/* Using local copies of various variables speeds things up by as
* much as 30% in Java! Performance not tested in C#.
*/
int[] lTabPrefix = tabPrefix;
byte[] lTabSuffix = tabSuffix;
byte[] lStack = stack;
int lNBits = nBits;
int lMaxCode = maxCode;
int lMaxMaxCode = maxMaxCode;
int lBitMask = bitMask;
int lOldCode = oldCode;
byte lFinChar = finChar;
int lStackP = stackP;
int lFreeEnt = freeEnt;
byte[] lData = data;
int lBitPos = bitPos;
// empty stack if stuff still left
int sSize = lStack.Length - lStackP;
if (sSize > 0)
{
int num = (sSize >= count) ? count : sSize;
Array.Copy(lStack, lStackP, buffer, offset, num);
offset += num;
count -= num;
lStackP += num;
}
if (count == 0)
{
stackP = lStackP;
return offset - start;
}
// loop, filling local buffer until enough data has been decompressed
MainLoop:
do
{
if (end < EXTRA)
{
Fill();
}
int bitIn = (got > 0) ? (end - end % lNBits) << 3 :
(end << 3) - (lNBits - 1);
while (lBitPos < bitIn)
{
#region A
// handle 1-byte reads correctly
if (count == 0)
{
nBits = lNBits;
maxCode = lMaxCode;
maxMaxCode = lMaxMaxCode;
bitMask = lBitMask;
oldCode = lOldCode;
finChar = lFinChar;
stackP = lStackP;
freeEnt = lFreeEnt;
bitPos = lBitPos;
return offset - start;
}
// check for code-width expansion
if (lFreeEnt > lMaxCode)
{
int nBytes = lNBits << 3;
lBitPos = (lBitPos - 1) +
nBytes - (lBitPos - 1 + nBytes) % nBytes;
lNBits++;
lMaxCode = (lNBits == maxBits) ? lMaxMaxCode :
(1 << lNBits) - 1;
lBitMask = (1 << lNBits) - 1;
lBitPos = ResetBuf(lBitPos);
goto MainLoop;
}
#endregion A
#region B
// read next code
int pos = lBitPos >> 3;
int code = (((lData[pos] & 0xFF) |
((lData[pos + 1] & 0xFF) << 8) |
((lData[pos + 2] & 0xFF) << 16)) >>
(lBitPos & 0x7)) & lBitMask;
lBitPos += lNBits;
// handle first iteration
if (lOldCode == -1)
{
if (code >= 256)
throw new LzwException("corrupt input: " + code + " > 255");
lFinChar = (byte)(lOldCode = code);
buffer[offset++] = lFinChar;
count--;
continue;
}
// handle CLEAR code
if (code == TBL_CLEAR && blockMode)
{
Array.Copy(zeros, 0, lTabPrefix, 0, zeros.Length);
lFreeEnt = TBL_FIRST - 1;
int nBytes = lNBits << 3;
lBitPos = (lBitPos - 1) + nBytes - (lBitPos - 1 + nBytes) % nBytes;
lNBits = LzwConstants.INIT_BITS;
lMaxCode = (1 << lNBits) - 1;
lBitMask = lMaxCode;
// Code tables reset
lBitPos = ResetBuf(lBitPos);
goto MainLoop;
}
#endregion B
#region C
// setup
int inCode = code;
lStackP = lStack.Length;
// Handle KwK case
if (code >= lFreeEnt)
{
if (code > lFreeEnt)
{
throw new LzwException("corrupt input: code=" + code +
", freeEnt=" + lFreeEnt);
}
lStack[--lStackP] = lFinChar;
code = lOldCode;
}
// Generate output characters in reverse order
while (code >= 256)
{
lStack[--lStackP] = lTabSuffix[code];
code = lTabPrefix[code];
}
lFinChar = lTabSuffix[code];
buffer[offset++] = lFinChar;
count--;
// And put them out in forward order
sSize = lStack.Length - lStackP;
int num = (sSize >= count) ? count : sSize;
Array.Copy(lStack, lStackP, buffer, offset, num);
offset += num;
count -= num;
lStackP += num;
#endregion C
#region D
// generate new entry in table
if (lFreeEnt < lMaxMaxCode)
{
lTabPrefix[lFreeEnt] = lOldCode;
lTabSuffix[lFreeEnt] = lFinChar;
lFreeEnt++;
}
// Remember previous code
lOldCode = inCode;
// if output buffer full, then return
if (count == 0)
{
nBits = lNBits;
maxCode = lMaxCode;
bitMask = lBitMask;
oldCode = lOldCode;
finChar = lFinChar;
stackP = lStackP;
freeEnt = lFreeEnt;
bitPos = lBitPos;
return offset - start;
}
#endregion D
} // while
lBitPos = ResetBuf(lBitPos);
} while (got > 0); // do..while
nBits = lNBits;
maxCode = lMaxCode;
bitMask = lBitMask;
oldCode = lOldCode;
finChar = lFinChar;
stackP = lStackP;
freeEnt = lFreeEnt;
bitPos = lBitPos;
eof = true;
return offset - start;
}
/// <summary>
/// Moves the unread data in the buffer to the beginning and resets
/// the pointers.
/// </summary>
/// <param name="bitPosition"></param>
/// <returns></returns>
private int ResetBuf(int bitPosition)
{
int pos = bitPosition >> 3;
Array.Copy(data, pos, data, 0, end - pos);
end -= pos;
return 0;
}
private void Fill()
{
got = baseInputStream.Read(data, end, data.Length - 1 - end);
if (got > 0)
{
end += got;
}
}
private void ParseHeader()
{
headerParsed = true;
byte[] hdr = new byte[LzwConstants.HDR_SIZE];
int result = baseInputStream.Read(hdr, 0, hdr.Length);
// Check the magic marker
if (result < 0)
throw new LzwException("Failed to read LZW header");
if (hdr[0] != (LzwConstants.MAGIC >> 8) || hdr[1] != (LzwConstants.MAGIC & 0xff))
{
throw new LzwException(String.Format(
"Wrong LZW header. Magic bytes don't match. 0x{0:x2} 0x{1:x2}",
hdr[0], hdr[1]));
}
// Check the 3rd header byte
blockMode = (hdr[2] & LzwConstants.BLOCK_MODE_MASK) > 0;
maxBits = hdr[2] & LzwConstants.BIT_MASK;
if (maxBits > LzwConstants.MAX_BITS)
{
throw new LzwException("Stream compressed with " + maxBits +
" bits, but decompression can only handle " +
LzwConstants.MAX_BITS + " bits.");
}
if ((hdr[2] & LzwConstants.RESERVED_MASK) > 0)
{
throw new LzwException("Unsupported bits set in the header.");
}
// Initialize variables
maxMaxCode = 1 << maxBits;
nBits = LzwConstants.INIT_BITS;
maxCode = (1 << nBits) - 1;
bitMask = maxCode;
oldCode = -1;
finChar = 0;
freeEnt = blockMode ? TBL_FIRST : 256;
tabPrefix = new int[1 << maxBits];
tabSuffix = new byte[1 << maxBits];
stack = new byte[1 << maxBits];
stackP = stack.Length;
for (int idx = 255; idx >= 0; idx--)
tabSuffix[idx] = (byte)idx;
}
#region Stream Overrides
/// <summary>
/// Gets a value indicating whether the current stream supports reading
/// </summary>
public override bool CanRead
{
get
{
return baseInputStream.CanRead;
}
}
/// <summary>
/// Gets a value of false indicating seeking is not supported for this stream.
/// </summary>
public override bool CanSeek
{
get
{
return false;
}
}
/// <summary>
/// Gets a value of false indicating that this stream is not writeable.
/// </summary>
public override bool CanWrite
{
get
{
return false;
}
}
/// <summary>
/// A value representing the length of the stream in bytes.
/// </summary>
public override long Length
{
get
{
return got;
}
}
/// <summary>
/// The current position within the stream.
/// Throws a NotSupportedException when attempting to set the position
/// </summary>
/// <exception cref="NotSupportedException">Attempting to set the position</exception>
public override long Position
{
get
{
return baseInputStream.Position;
}
set
{
throw new NotSupportedException("InflaterInputStream Position not supported");
}
}
/// <summary>
/// Flushes the baseInputStream
/// </summary>
public override void Flush()
{
baseInputStream.Flush();
}
/// <summary>
/// Sets the position within the current stream
/// Always throws a NotSupportedException
/// </summary>
/// <param name="offset">The relative offset to seek to.</param>
/// <param name="origin">The <see cref="SeekOrigin"/> defining where to seek from.</param>
/// <returns>The new position in the stream.</returns>
/// <exception cref="NotSupportedException">Any access</exception>
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException("Seek not supported");
}
/// <summary>
/// Set the length of the current stream
/// Always throws a NotSupportedException
/// </summary>
/// <param name="value">The new length value for the stream.</param>
/// <exception cref="NotSupportedException">Any access</exception>
public override void SetLength(long value)
{
throw new NotSupportedException("InflaterInputStream SetLength not supported");
}
/// <summary>
/// Writes a sequence of bytes to stream and advances the current position
/// This method always throws a NotSupportedException
/// </summary>
/// <param name="buffer">Thew buffer containing data to write.</param>
/// <param name="offset">The offset of the first byte to write.</param>
/// <param name="count">The number of bytes to write.</param>
/// <exception cref="NotSupportedException">Any access</exception>
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException("InflaterInputStream Write not supported");
}
/// <summary>
/// Writes one byte to the current stream and advances the current position
/// Always throws a NotSupportedException
/// </summary>
/// <param name="value">The byte to write.</param>
/// <exception cref="NotSupportedException">Any access</exception>
public override void WriteByte(byte value)
{
throw new NotSupportedException("InflaterInputStream WriteByte not supported");
}
/// <summary>
/// Closes the input stream. When <see cref="IsStreamOwner"></see>
/// is true the underlying stream is also closed.
/// </summary>
protected override void Dispose(bool disposing)
{
if (!isClosed)
{
isClosed = true;
if (IsStreamOwner)
{
baseInputStream.Dispose();
}
}
}
#endregion Stream Overrides
#region Instance Fields
private Stream baseInputStream;
/// <summary>
/// Flag indicating wether this instance has been closed or not.
/// </summary>
private bool isClosed;
private readonly byte[] one = new byte[1];
private bool headerParsed;
// string table stuff
private const int TBL_CLEAR = 0x100;
private const int TBL_FIRST = TBL_CLEAR + 1;
private int[] tabPrefix;
private byte[] tabSuffix;
private readonly int[] zeros = new int[256];
private byte[] stack;
// various state
private bool blockMode;
private int nBits;
private int maxBits;
private int maxMaxCode;
private int maxCode;
private int bitMask;
private int oldCode;
private byte finChar;
private int stackP;
private int freeEnt;
// input buffer
private readonly byte[] data = new byte[1024 * 8];
private int bitPos;
private int end;
private int got;
private bool eof;
private const int EXTRA = 64;
#endregion Instance Fields
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ac99656d2ede240eba8daa0d4136c678
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 272f4d15328d84c238276bf977ac67cd
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,37 @@
using System;
namespace ICSharpCode.SharpZipLib.Tar
{
/// <summary>
/// This exception is used to indicate that there is a problem
/// with a TAR archive header.
/// </summary>
public class InvalidHeaderException : TarException
{
/// <summary>
/// Initialise a new instance of the InvalidHeaderException class.
/// </summary>
public InvalidHeaderException()
{
}
/// <summary>
/// Initialises a new instance of the InvalidHeaderException class with a specified message.
/// </summary>
/// <param name="message">Message describing the exception cause.</param>
public InvalidHeaderException(string message)
: base(message)
{
}
/// <summary>
/// Initialise a new instance of InvalidHeaderException
/// </summary>
/// <param name="message">Message describing the problem.</param>
/// <param name="exception">The exception that is the cause of the current exception.</param>
public InvalidHeaderException(string message, Exception exception)
: base(message, exception)
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 89dceccc821084f3ca52948807e3ef21
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,959 @@
using System;
using System.IO;
using System.Text;
namespace ICSharpCode.SharpZipLib.Tar
{
/// <summary>
/// Used to advise clients of 'events' while processing archives
/// </summary>
public delegate void ProgressMessageHandler(TarArchive archive, TarEntry entry, string message);
/// <summary>
/// The TarArchive class implements the concept of a
/// 'Tape Archive'. A tar archive is a series of entries, each of
/// which represents a file system object. Each entry in
/// the archive consists of a header block followed by 0 or more data blocks.
/// Directory entries consist only of the header block, and are followed by entries
/// for the directory's contents. File entries consist of a
/// header followed by the number of blocks needed to
/// contain the file's contents. All entries are written on
/// block boundaries. Blocks are 512 bytes long.
///
/// TarArchives are instantiated in either read or write mode,
/// based upon whether they are instantiated with an InputStream
/// or an OutputStream. Once instantiated TarArchives read/write
/// mode can not be changed.
///
/// There is currently no support for random access to tar archives.
/// However, it seems that subclassing TarArchive, and using the
/// TarBuffer.CurrentRecord and TarBuffer.CurrentBlock
/// properties, this would be rather trivial.
/// </summary>
public class TarArchive : IDisposable
{
/// <summary>
/// Client hook allowing detailed information to be reported during processing
/// </summary>
public event ProgressMessageHandler ProgressMessageEvent;
/// <summary>
/// Raises the ProgressMessage event
/// </summary>
/// <param name="entry">The <see cref="TarEntry">TarEntry</see> for this event</param>
/// <param name="message">message for this event. Null is no message</param>
protected virtual void OnProgressMessageEvent(TarEntry entry, string message)
{
ProgressMessageHandler handler = ProgressMessageEvent;
if (handler != null)
{
handler(this, entry, message);
}
}
#region Constructors
/// <summary>
/// Constructor for a default <see cref="TarArchive"/>.
/// </summary>
protected TarArchive()
{
}
/// <summary>
/// Initalise a TarArchive for input.
/// </summary>
/// <param name="stream">The <see cref="TarInputStream"/> to use for input.</param>
protected TarArchive(TarInputStream stream)
{
if (stream == null)
{
throw new ArgumentNullException(nameof(stream));
}
tarIn = stream;
}
/// <summary>
/// Initialise a TarArchive for output.
/// </summary>
/// <param name="stream">The <see cref="TarOutputStream"/> to use for output.</param>
protected TarArchive(TarOutputStream stream)
{
if (stream == null)
{
throw new ArgumentNullException(nameof(stream));
}
tarOut = stream;
}
#endregion Constructors
#region Static factory methods
/// <summary>
/// The InputStream based constructors create a TarArchive for the
/// purposes of extracting or listing a tar archive. Thus, use
/// these constructors when you wish to extract files from or list
/// the contents of an existing tar archive.
/// </summary>
/// <param name="inputStream">The stream to retrieve archive data from.</param>
/// <returns>Returns a new <see cref="TarArchive"/> suitable for reading from.</returns>
public static TarArchive CreateInputTarArchive(Stream inputStream)
{
if (inputStream == null)
{
throw new ArgumentNullException(nameof(inputStream));
}
var tarStream = inputStream as TarInputStream;
TarArchive result;
if (tarStream != null)
{
result = new TarArchive(tarStream);
}
else
{
result = CreateInputTarArchive(inputStream, TarBuffer.DefaultBlockFactor);
}
return result;
}
/// <summary>
/// Create TarArchive for reading setting block factor
/// </summary>
/// <param name="inputStream">A stream containing the tar archive contents</param>
/// <param name="blockFactor">The blocking factor to apply</param>
/// <returns>Returns a <see cref="TarArchive"/> suitable for reading.</returns>
public static TarArchive CreateInputTarArchive(Stream inputStream, int blockFactor)
{
if (inputStream == null)
{
throw new ArgumentNullException(nameof(inputStream));
}
if (inputStream is TarInputStream)
{
throw new ArgumentException("TarInputStream not valid");
}
return new TarArchive(new TarInputStream(inputStream, blockFactor));
}
/// <summary>
/// Create a TarArchive for writing to, using the default blocking factor
/// </summary>
/// <param name="outputStream">The <see cref="Stream"/> to write to</param>
/// <returns>Returns a <see cref="TarArchive"/> suitable for writing.</returns>
public static TarArchive CreateOutputTarArchive(Stream outputStream)
{
if (outputStream == null)
{
throw new ArgumentNullException(nameof(outputStream));
}
var tarStream = outputStream as TarOutputStream;
TarArchive result;
if (tarStream != null)
{
result = new TarArchive(tarStream);
}
else
{
result = CreateOutputTarArchive(outputStream, TarBuffer.DefaultBlockFactor);
}
return result;
}
/// <summary>
/// Create a <see cref="TarArchive">tar archive</see> for writing.
/// </summary>
/// <param name="outputStream">The stream to write to</param>
/// <param name="blockFactor">The blocking factor to use for buffering.</param>
/// <returns>Returns a <see cref="TarArchive"/> suitable for writing.</returns>
public static TarArchive CreateOutputTarArchive(Stream outputStream, int blockFactor)
{
if (outputStream == null)
{
throw new ArgumentNullException(nameof(outputStream));
}
if (outputStream is TarOutputStream)
{
throw new ArgumentException("TarOutputStream is not valid");
}
return new TarArchive(new TarOutputStream(outputStream, blockFactor));
}
#endregion Static factory methods
/// <summary>
/// Set the flag that determines whether existing files are
/// kept, or overwritten during extraction.
/// </summary>
/// <param name="keepExistingFiles">
/// If true, do not overwrite existing files.
/// </param>
public void SetKeepOldFiles(bool keepExistingFiles)
{
if (isDisposed)
{
throw new ObjectDisposedException("TarArchive");
}
keepOldFiles = keepExistingFiles;
}
/// <summary>
/// Get/set the ascii file translation flag. If ascii file translation
/// is true, then the file is checked to see if it a binary file or not.
/// If the flag is true and the test indicates it is ascii text
/// file, it will be translated. The translation converts the local
/// operating system's concept of line ends into the UNIX line end,
/// '\n', which is the defacto standard for a TAR archive. This makes
/// text files compatible with UNIX.
/// </summary>
public bool AsciiTranslate
{
get
{
if (isDisposed)
{
throw new ObjectDisposedException("TarArchive");
}
return asciiTranslate;
}
set
{
if (isDisposed)
{
throw new ObjectDisposedException("TarArchive");
}
asciiTranslate = value;
}
}
/// <summary>
/// Set the ascii file translation flag.
/// </summary>
/// <param name= "translateAsciiFiles">
/// If true, translate ascii text files.
/// </param>
[Obsolete("Use the AsciiTranslate property")]
public void SetAsciiTranslation(bool translateAsciiFiles)
{
if (isDisposed)
{
throw new ObjectDisposedException("TarArchive");
}
asciiTranslate = translateAsciiFiles;
}
/// <summary>
/// PathPrefix is added to entry names as they are written if the value is not null.
/// A slash character is appended after PathPrefix
/// </summary>
public string PathPrefix
{
get
{
if (isDisposed)
{
throw new ObjectDisposedException("TarArchive");
}
return pathPrefix;
}
set
{
if (isDisposed)
{
throw new ObjectDisposedException("TarArchive");
}
pathPrefix = value;
}
}
/// <summary>
/// RootPath is removed from entry names if it is found at the
/// beginning of the name.
/// </summary>
public string RootPath
{
get
{
if (isDisposed)
{
throw new ObjectDisposedException("TarArchive");
}
return rootPath;
}
set
{
if (isDisposed)
{
throw new ObjectDisposedException("TarArchive");
}
// Convert to forward slashes for matching. Trim trailing / for correct final path
rootPath = value.Replace('\\', '/').TrimEnd('/');
}
}
/// <summary>
/// Set user and group information that will be used to fill in the
/// tar archive's entry headers. This information is based on that available
/// for the linux operating system, which is not always available on other
/// operating systems. TarArchive allows the programmer to specify values
/// to be used in their place.
/// <see cref="ApplyUserInfoOverrides"/> is set to true by this call.
/// </summary>
/// <param name="userId">
/// The user id to use in the headers.
/// </param>
/// <param name="userName">
/// The user name to use in the headers.
/// </param>
/// <param name="groupId">
/// The group id to use in the headers.
/// </param>
/// <param name="groupName">
/// The group name to use in the headers.
/// </param>
public void SetUserInfo(int userId, string userName, int groupId, string groupName)
{
if (isDisposed)
{
throw new ObjectDisposedException("TarArchive");
}
this.userId = userId;
this.userName = userName;
this.groupId = groupId;
this.groupName = groupName;
applyUserInfoOverrides = true;
}
/// <summary>
/// Get or set a value indicating if overrides defined by <see cref="SetUserInfo">SetUserInfo</see> should be applied.
/// </summary>
/// <remarks>If overrides are not applied then the values as set in each header will be used.</remarks>
public bool ApplyUserInfoOverrides
{
get
{
if (isDisposed)
{
throw new ObjectDisposedException("TarArchive");
}
return applyUserInfoOverrides;
}
set
{
if (isDisposed)
{
throw new ObjectDisposedException("TarArchive");
}
applyUserInfoOverrides = value;
}
}
/// <summary>
/// Get the archive user id.
/// See <see cref="ApplyUserInfoOverrides">ApplyUserInfoOverrides</see> for detail
/// on how to allow setting values on a per entry basis.
/// </summary>
/// <returns>
/// The current user id.
/// </returns>
public int UserId
{
get
{
if (isDisposed)
{
throw new ObjectDisposedException("TarArchive");
}
return userId;
}
}
/// <summary>
/// Get the archive user name.
/// See <see cref="ApplyUserInfoOverrides">ApplyUserInfoOverrides</see> for detail
/// on how to allow setting values on a per entry basis.
/// </summary>
/// <returns>
/// The current user name.
/// </returns>
public string UserName
{
get
{
if (isDisposed)
{
throw new ObjectDisposedException("TarArchive");
}
return userName;
}
}
/// <summary>
/// Get the archive group id.
/// See <see cref="ApplyUserInfoOverrides">ApplyUserInfoOverrides</see> for detail
/// on how to allow setting values on a per entry basis.
/// </summary>
/// <returns>
/// The current group id.
/// </returns>
public int GroupId
{
get
{
if (isDisposed)
{
throw new ObjectDisposedException("TarArchive");
}
return groupId;
}
}
/// <summary>
/// Get the archive group name.
/// See <see cref="ApplyUserInfoOverrides">ApplyUserInfoOverrides</see> for detail
/// on how to allow setting values on a per entry basis.
/// </summary>
/// <returns>
/// The current group name.
/// </returns>
public string GroupName
{
get
{
if (isDisposed)
{
throw new ObjectDisposedException("TarArchive");
}
return groupName;
}
}
/// <summary>
/// Get the archive's record size. Tar archives are composed of
/// a series of RECORDS each containing a number of BLOCKS.
/// This allowed tar archives to match the IO characteristics of
/// the physical device being used. Archives are expected
/// to be properly "blocked".
/// </summary>
/// <returns>
/// The record size this archive is using.
/// </returns>
public int RecordSize
{
get
{
if (isDisposed)
{
throw new ObjectDisposedException("TarArchive");
}
if (tarIn != null)
{
return tarIn.RecordSize;
}
else if (tarOut != null)
{
return tarOut.RecordSize;
}
return TarBuffer.DefaultRecordSize;
}
}
/// <summary>
/// Sets the IsStreamOwner property on the underlying stream.
/// Set this to false to prevent the Close of the TarArchive from closing the stream.
/// </summary>
public bool IsStreamOwner
{
set
{
if (tarIn != null)
{
tarIn.IsStreamOwner = value;
}
else
{
tarOut.IsStreamOwner = value;
}
}
}
/// <summary>
/// Close the archive.
/// </summary>
[Obsolete("Use Close instead")]
public void CloseArchive()
{
Close();
}
/// <summary>
/// Perform the "list" command for the archive contents.
///
/// NOTE That this method uses the <see cref="ProgressMessageEvent"> progress event</see> to actually list
/// the contents. If the progress display event is not set, nothing will be listed!
/// </summary>
public void ListContents()
{
if (isDisposed)
{
throw new ObjectDisposedException("TarArchive");
}
while (true)
{
TarEntry entry = tarIn.GetNextEntry();
if (entry == null)
{
break;
}
OnProgressMessageEvent(entry, null);
}
}
/// <summary>
/// Perform the "extract" command and extract the contents of the archive.
/// </summary>
/// <param name="destinationDirectory">
/// The destination directory into which to extract.
/// </param>
public void ExtractContents(string destinationDirectory)
{
if (isDisposed)
{
throw new ObjectDisposedException("TarArchive");
}
while (true)
{
TarEntry entry = tarIn.GetNextEntry();
if (entry == null)
{
break;
}
if (entry.TarHeader.TypeFlag == TarHeader.LF_LINK || entry.TarHeader.TypeFlag == TarHeader.LF_SYMLINK)
continue;
ExtractEntry(destinationDirectory, entry);
}
}
/// <summary>
/// Extract an entry from the archive. This method assumes that the
/// tarIn stream has been properly set with a call to GetNextEntry().
/// </summary>
/// <param name="destDir">
/// The destination directory into which to extract.
/// </param>
/// <param name="entry">
/// The TarEntry returned by tarIn.GetNextEntry().
/// </param>
private void ExtractEntry(string destDir, TarEntry entry)
{
OnProgressMessageEvent(entry, null);
string name = entry.Name;
if (Path.IsPathRooted(name))
{
// NOTE:
// for UNC names... \\machine\share\zoom\beet.txt gives \zoom\beet.txt
name = name.Substring(Path.GetPathRoot(name).Length);
}
name = name.Replace('/', Path.DirectorySeparatorChar);
string destFile = Path.Combine(destDir, name);
if (entry.IsDirectory)
{
EnsureDirectoryExists(destFile);
}
else
{
string parentDirectory = Path.GetDirectoryName(destFile);
EnsureDirectoryExists(parentDirectory);
bool process = true;
var fileInfo = new FileInfo(destFile);
if (fileInfo.Exists)
{
if (keepOldFiles)
{
OnProgressMessageEvent(entry, "Destination file already exists");
process = false;
}
else if ((fileInfo.Attributes & FileAttributes.ReadOnly) != 0)
{
OnProgressMessageEvent(entry, "Destination file already exists, and is read-only");
process = false;
}
}
if (process)
{
using (var outputStream = File.Create(destFile))
{
if (this.asciiTranslate)
{
// May need to translate the file.
ExtractAndTranslateEntry(destFile, outputStream);
}
else
{
// If translation is disabled, just copy the entry across directly.
tarIn.CopyEntryContents(outputStream);
}
}
}
}
}
// Extract a TAR entry, and perform an ASCII translation if required.
private void ExtractAndTranslateEntry(string destFile, Stream outputStream)
{
bool asciiTrans = !IsBinary(destFile);
if (asciiTrans)
{
using (var outw = new StreamWriter(outputStream, new UTF8Encoding(false), 1024, true))
{
byte[] rdbuf = new byte[32 * 1024];
while (true)
{
int numRead = tarIn.Read(rdbuf, 0, rdbuf.Length);
if (numRead <= 0)
{
break;
}
for (int off = 0, b = 0; b < numRead; ++b)
{
if (rdbuf[b] == 10)
{
string s = Encoding.ASCII.GetString(rdbuf, off, (b - off));
outw.WriteLine(s);
off = b + 1;
}
}
}
}
}
else
{
// No translation required.
tarIn.CopyEntryContents(outputStream);
}
}
/// <summary>
/// Write an entry to the archive. This method will call the putNextEntry
/// and then write the contents of the entry, and finally call closeEntry()
/// for entries that are files. For directories, it will call putNextEntry(),
/// and then, if the recurse flag is true, process each entry that is a
/// child of the directory.
/// </summary>
/// <param name="sourceEntry">
/// The TarEntry representing the entry to write to the archive.
/// </param>
/// <param name="recurse">
/// If true, process the children of directory entries.
/// </param>
public void WriteEntry(TarEntry sourceEntry, bool recurse)
{
if (sourceEntry == null)
{
throw new ArgumentNullException(nameof(sourceEntry));
}
if (isDisposed)
{
throw new ObjectDisposedException("TarArchive");
}
try
{
if (recurse)
{
TarHeader.SetValueDefaults(sourceEntry.UserId, sourceEntry.UserName,
sourceEntry.GroupId, sourceEntry.GroupName);
}
WriteEntryCore(sourceEntry, recurse);
}
finally
{
if (recurse)
{
TarHeader.RestoreSetValues();
}
}
}
/// <summary>
/// Write an entry to the archive. This method will call the putNextEntry
/// and then write the contents of the entry, and finally call closeEntry()
/// for entries that are files. For directories, it will call putNextEntry(),
/// and then, if the recurse flag is true, process each entry that is a
/// child of the directory.
/// </summary>
/// <param name="sourceEntry">
/// The TarEntry representing the entry to write to the archive.
/// </param>
/// <param name="recurse">
/// If true, process the children of directory entries.
/// </param>
private void WriteEntryCore(TarEntry sourceEntry, bool recurse)
{
string tempFileName = null;
string entryFilename = sourceEntry.File;
var entry = (TarEntry)sourceEntry.Clone();
if (applyUserInfoOverrides)
{
entry.GroupId = groupId;
entry.GroupName = groupName;
entry.UserId = userId;
entry.UserName = userName;
}
OnProgressMessageEvent(entry, null);
if (asciiTranslate && !entry.IsDirectory)
{
if (!IsBinary(entryFilename))
{
tempFileName = Path.GetTempFileName();
using (StreamReader inStream = File.OpenText(entryFilename))
{
using (Stream outStream = File.Create(tempFileName))
{
while (true)
{
string line = inStream.ReadLine();
if (line == null)
{
break;
}
byte[] data = Encoding.ASCII.GetBytes(line);
outStream.Write(data, 0, data.Length);
outStream.WriteByte((byte)'\n');
}
outStream.Flush();
}
}
entry.Size = new FileInfo(tempFileName).Length;
entryFilename = tempFileName;
}
}
string newName = null;
if (!String.IsNullOrEmpty(rootPath))
{
if (entry.Name.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase))
{
newName = entry.Name.Substring(rootPath.Length + 1);
}
}
if (pathPrefix != null)
{
newName = (newName == null) ? pathPrefix + "/" + entry.Name : pathPrefix + "/" + newName;
}
if (newName != null)
{
entry.Name = newName;
}
tarOut.PutNextEntry(entry);
if (entry.IsDirectory)
{
if (recurse)
{
TarEntry[] list = entry.GetDirectoryEntries();
for (int i = 0; i < list.Length; ++i)
{
WriteEntryCore(list[i], recurse);
}
}
}
else
{
using (Stream inputStream = File.OpenRead(entryFilename))
{
byte[] localBuffer = new byte[32 * 1024];
while (true)
{
int numRead = inputStream.Read(localBuffer, 0, localBuffer.Length);
if (numRead <= 0)
{
break;
}
tarOut.Write(localBuffer, 0, numRead);
}
}
if (!string.IsNullOrEmpty(tempFileName))
{
File.Delete(tempFileName);
}
tarOut.CloseEntry();
}
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases the unmanaged resources used by the FileStream and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">true to release both managed and unmanaged resources;
/// false to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (!isDisposed)
{
isDisposed = true;
if (disposing)
{
if (tarOut != null)
{
tarOut.Flush();
tarOut.Dispose();
}
if (tarIn != null)
{
tarIn.Dispose();
}
}
}
}
/// <summary>
/// Closes the archive and releases any associated resources.
/// </summary>
public virtual void Close()
{
Dispose(true);
}
/// <summary>
/// Ensures that resources are freed and other cleanup operations are performed
/// when the garbage collector reclaims the <see cref="TarArchive"/>.
/// </summary>
~TarArchive()
{
Dispose(false);
}
private static void EnsureDirectoryExists(string directoryName)
{
if (!Directory.Exists(directoryName))
{
try
{
Directory.CreateDirectory(directoryName);
}
catch (Exception e)
{
throw new TarException("Exception creating directory '" + directoryName + "', " + e.Message, e);
}
}
}
// TODO: TarArchive - Is there a better way to test for a text file?
// It no longer reads entire files into memory but is still a weak test!
// This assumes that byte values 0-7, 14-31 or 255 are binary
// and that all non text files contain one of these values
private static bool IsBinary(string filename)
{
using (FileStream fs = File.OpenRead(filename))
{
int sampleSize = Math.Min(4096, (int)fs.Length);
byte[] content = new byte[sampleSize];
int bytesRead = fs.Read(content, 0, sampleSize);
for (int i = 0; i < bytesRead; ++i)
{
byte b = content[i];
if ((b < 8) || ((b > 13) && (b < 32)) || (b == 255))
{
return true;
}
}
}
return false;
}
#region Instance Fields
private bool keepOldFiles;
private bool asciiTranslate;
private int userId;
private string userName = string.Empty;
private int groupId;
private string groupName = string.Empty;
private string rootPath;
private string pathPrefix;
private bool applyUserInfoOverrides;
private TarInputStream tarIn;
private TarOutputStream tarOut;
private bool isDisposed;
#endregion Instance Fields
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2822f3658ffeb4d498c1df7ed3110f5f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,599 @@
using System;
using System.IO;
namespace ICSharpCode.SharpZipLib.Tar
{
/// <summary>
/// The TarBuffer class implements the tar archive concept
/// of a buffered input stream. This concept goes back to the
/// days of blocked tape drives and special io devices. In the
/// C# universe, the only real function that this class
/// performs is to ensure that files have the correct "record"
/// size, or other tars will complain.
/// <p>
/// You should never have a need to access this class directly.
/// TarBuffers are created by Tar IO Streams.
/// </p>
/// </summary>
public class TarBuffer
{
/* A quote from GNU tar man file on blocking and records
A `tar' archive file contains a series of blocks. Each block
contains `BLOCKSIZE' bytes. Although this format may be thought of as
being on magnetic tape, other media are often used.
Each file archived is represented by a header block which describes
the file, followed by zero or more blocks which give the contents of
the file. At the end of the archive file there may be a block filled
with binary zeros as an end-of-file marker. A reasonable system should
write a block of zeros at the end, but must not assume that such a
block exists when reading an archive.
The blocks may be "blocked" for physical I/O operations. Each
record of N blocks is written with a single 'write ()'
operation. On magnetic tapes, the result of such a write is a single
record. When writing an archive, the last record of blocks should be
written at the full size, with blocks after the zero block containing
all zeros. When reading an archive, a reasonable system should
properly handle an archive whose last record is shorter than the rest,
or which contains garbage records after a zero block.
*/
#region Constants
/// <summary>
/// The size of a block in a tar archive in bytes.
/// </summary>
/// <remarks>This is 512 bytes.</remarks>
public const int BlockSize = 512;
/// <summary>
/// The number of blocks in a default record.
/// </summary>
/// <remarks>
/// The default value is 20 blocks per record.
/// </remarks>
public const int DefaultBlockFactor = 20;
/// <summary>
/// The size in bytes of a default record.
/// </summary>
/// <remarks>
/// The default size is 10KB.
/// </remarks>
public const int DefaultRecordSize = BlockSize * DefaultBlockFactor;
#endregion Constants
/// <summary>
/// Get the record size for this buffer
/// </summary>
/// <value>The record size in bytes.
/// This is equal to the <see cref="BlockFactor"/> multiplied by the <see cref="BlockSize"/></value>
public int RecordSize
{
get
{
return recordSize;
}
}
/// <summary>
/// Get the TAR Buffer's record size.
/// </summary>
/// <returns>The record size in bytes.
/// This is equal to the <see cref="BlockFactor"/> multiplied by the <see cref="BlockSize"/></returns>
[Obsolete("Use RecordSize property instead")]
public int GetRecordSize()
{
return recordSize;
}
/// <summary>
/// Get the Blocking factor for the buffer
/// </summary>
/// <value>This is the number of blocks in each record.</value>
public int BlockFactor
{
get
{
return blockFactor;
}
}
/// <summary>
/// Get the TAR Buffer's block factor
/// </summary>
/// <returns>The block factor; the number of blocks per record.</returns>
[Obsolete("Use BlockFactor property instead")]
public int GetBlockFactor()
{
return blockFactor;
}
/// <summary>
/// Construct a default TarBuffer
/// </summary>
protected TarBuffer()
{
}
/// <summary>
/// Create TarBuffer for reading with default BlockFactor
/// </summary>
/// <param name="inputStream">Stream to buffer</param>
/// <returns>A new <see cref="TarBuffer"/> suitable for input.</returns>
public static TarBuffer CreateInputTarBuffer(Stream inputStream)
{
if (inputStream == null)
{
throw new ArgumentNullException(nameof(inputStream));
}
return CreateInputTarBuffer(inputStream, DefaultBlockFactor);
}
/// <summary>
/// Construct TarBuffer for reading inputStream setting BlockFactor
/// </summary>
/// <param name="inputStream">Stream to buffer</param>
/// <param name="blockFactor">Blocking factor to apply</param>
/// <returns>A new <see cref="TarBuffer"/> suitable for input.</returns>
public static TarBuffer CreateInputTarBuffer(Stream inputStream, int blockFactor)
{
if (inputStream == null)
{
throw new ArgumentNullException(nameof(inputStream));
}
if (blockFactor <= 0)
{
throw new ArgumentOutOfRangeException(nameof(blockFactor), "Factor cannot be negative");
}
var tarBuffer = new TarBuffer();
tarBuffer.inputStream = inputStream;
tarBuffer.outputStream = null;
tarBuffer.Initialize(blockFactor);
return tarBuffer;
}
/// <summary>
/// Construct TarBuffer for writing with default BlockFactor
/// </summary>
/// <param name="outputStream">output stream for buffer</param>
/// <returns>A new <see cref="TarBuffer"/> suitable for output.</returns>
public static TarBuffer CreateOutputTarBuffer(Stream outputStream)
{
if (outputStream == null)
{
throw new ArgumentNullException(nameof(outputStream));
}
return CreateOutputTarBuffer(outputStream, DefaultBlockFactor);
}
/// <summary>
/// Construct TarBuffer for writing Tar output to streams.
/// </summary>
/// <param name="outputStream">Output stream to write to.</param>
/// <param name="blockFactor">Blocking factor to apply</param>
/// <returns>A new <see cref="TarBuffer"/> suitable for output.</returns>
public static TarBuffer CreateOutputTarBuffer(Stream outputStream, int blockFactor)
{
if (outputStream == null)
{
throw new ArgumentNullException(nameof(outputStream));
}
if (blockFactor <= 0)
{
throw new ArgumentOutOfRangeException(nameof(blockFactor), "Factor cannot be negative");
}
var tarBuffer = new TarBuffer();
tarBuffer.inputStream = null;
tarBuffer.outputStream = outputStream;
tarBuffer.Initialize(blockFactor);
return tarBuffer;
}
/// <summary>
/// Initialization common to all constructors.
/// </summary>
private void Initialize(int archiveBlockFactor)
{
blockFactor = archiveBlockFactor;
recordSize = archiveBlockFactor * BlockSize;
recordBuffer = new byte[RecordSize];
if (inputStream != null)
{
currentRecordIndex = -1;
currentBlockIndex = BlockFactor;
}
else
{
currentRecordIndex = 0;
currentBlockIndex = 0;
}
}
/// <summary>
/// Determine if an archive block indicates End of Archive. End of
/// archive is indicated by a block that consists entirely of null bytes.
/// All remaining blocks for the record should also be null's
/// However some older tars only do a couple of null blocks (Old GNU tar for one)
/// and also partial records
/// </summary>
/// <param name = "block">The data block to check.</param>
/// <returns>Returns true if the block is an EOF block; false otherwise.</returns>
[Obsolete("Use IsEndOfArchiveBlock instead")]
public bool IsEOFBlock(byte[] block)
{
if (block == null)
{
throw new ArgumentNullException(nameof(block));
}
if (block.Length != BlockSize)
{
throw new ArgumentException("block length is invalid");
}
for (int i = 0; i < BlockSize; ++i)
{
if (block[i] != 0)
{
return false;
}
}
return true;
}
/// <summary>
/// Determine if an archive block indicates the End of an Archive has been reached.
/// End of archive is indicated by a block that consists entirely of null bytes.
/// All remaining blocks for the record should also be null's
/// However some older tars only do a couple of null blocks (Old GNU tar for one)
/// and also partial records
/// </summary>
/// <param name = "block">The data block to check.</param>
/// <returns>Returns true if the block is an EOF block; false otherwise.</returns>
public static bool IsEndOfArchiveBlock(byte[] block)
{
if (block == null)
{
throw new ArgumentNullException(nameof(block));
}
if (block.Length != BlockSize)
{
throw new ArgumentException("block length is invalid");
}
for (int i = 0; i < BlockSize; ++i)
{
if (block[i] != 0)
{
return false;
}
}
return true;
}
/// <summary>
/// Skip over a block on the input stream.
/// </summary>
public void SkipBlock()
{
if (inputStream == null)
{
throw new TarException("no input stream defined");
}
if (currentBlockIndex >= BlockFactor)
{
if (!ReadRecord())
{
throw new TarException("Failed to read a record");
}
}
currentBlockIndex++;
}
/// <summary>
/// Read a block from the input stream.
/// </summary>
/// <returns>
/// The block of data read.
/// </returns>
public byte[] ReadBlock()
{
if (inputStream == null)
{
throw new TarException("TarBuffer.ReadBlock - no input stream defined");
}
if (currentBlockIndex >= BlockFactor)
{
if (!ReadRecord())
{
throw new TarException("Failed to read a record");
}
}
byte[] result = new byte[BlockSize];
Array.Copy(recordBuffer, (currentBlockIndex * BlockSize), result, 0, BlockSize);
currentBlockIndex++;
return result;
}
/// <summary>
/// Read a record from data stream.
/// </summary>
/// <returns>
/// false if End-Of-File, else true.
/// </returns>
private bool ReadRecord()
{
if (inputStream == null)
{
throw new TarException("no input stream stream defined");
}
currentBlockIndex = 0;
int offset = 0;
int bytesNeeded = RecordSize;
while (bytesNeeded > 0)
{
long numBytes = inputStream.Read(recordBuffer, offset, bytesNeeded);
//
// NOTE
// We have found EOF, and the record is not full!
//
// This is a broken archive. It does not follow the standard
// blocking algorithm. However, because we are generous, and
// it requires little effort, we will simply ignore the error
// and continue as if the entire record were read. This does
// not appear to break anything upstream. We used to return
// false in this case.
//
// Thanks to 'Yohann.Roussel@alcatel.fr' for this fix.
//
if (numBytes <= 0)
{
break;
}
offset += (int)numBytes;
bytesNeeded -= (int)numBytes;
}
currentRecordIndex++;
return true;
}
/// <summary>
/// Get the current block number, within the current record, zero based.
/// </summary>
/// <remarks>Block numbers are zero based values</remarks>
/// <seealso cref="RecordSize"/>
public int CurrentBlock
{
get { return currentBlockIndex; }
}
/// <summary>
/// Gets or sets a flag indicating ownership of underlying stream.
/// When the flag is true <see cref="Close" /> will close the underlying stream also.
/// </summary>
/// <remarks>The default value is true.</remarks>
public bool IsStreamOwner { get; set; } = true;
/// <summary>
/// Get the current block number, within the current record, zero based.
/// </summary>
/// <returns>
/// The current zero based block number.
/// </returns>
/// <remarks>
/// The absolute block number = (<see cref="GetCurrentRecordNum">record number</see> * <see cref="BlockFactor">block factor</see>) + <see cref="GetCurrentBlockNum">block number</see>.
/// </remarks>
[Obsolete("Use CurrentBlock property instead")]
public int GetCurrentBlockNum()
{
return currentBlockIndex;
}
/// <summary>
/// Get the current record number.
/// </summary>
/// <returns>
/// The current zero based record number.
/// </returns>
public int CurrentRecord
{
get { return currentRecordIndex; }
}
/// <summary>
/// Get the current record number.
/// </summary>
/// <returns>
/// The current zero based record number.
/// </returns>
[Obsolete("Use CurrentRecord property instead")]
public int GetCurrentRecordNum()
{
return currentRecordIndex;
}
/// <summary>
/// Write a block of data to the archive.
/// </summary>
/// <param name="block">
/// The data to write to the archive.
/// </param>
public void WriteBlock(byte[] block)
{
if (block == null)
{
throw new ArgumentNullException(nameof(block));
}
if (outputStream == null)
{
throw new TarException("TarBuffer.WriteBlock - no output stream defined");
}
if (block.Length != BlockSize)
{
string errorText = string.Format("TarBuffer.WriteBlock - block to write has length '{0}' which is not the block size of '{1}'",
block.Length, BlockSize);
throw new TarException(errorText);
}
if (currentBlockIndex >= BlockFactor)
{
WriteRecord();
}
Array.Copy(block, 0, recordBuffer, (currentBlockIndex * BlockSize), BlockSize);
currentBlockIndex++;
}
/// <summary>
/// Write an archive record to the archive, where the record may be
/// inside of a larger array buffer. The buffer must be "offset plus
/// record size" long.
/// </summary>
/// <param name="buffer">
/// The buffer containing the record data to write.
/// </param>
/// <param name="offset">
/// The offset of the record data within buffer.
/// </param>
public void WriteBlock(byte[] buffer, int offset)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
if (outputStream == null)
{
throw new TarException("TarBuffer.WriteBlock - no output stream stream defined");
}
if ((offset < 0) || (offset >= buffer.Length))
{
throw new ArgumentOutOfRangeException(nameof(offset));
}
if ((offset + BlockSize) > buffer.Length)
{
string errorText = string.Format("TarBuffer.WriteBlock - record has length '{0}' with offset '{1}' which is less than the record size of '{2}'",
buffer.Length, offset, recordSize);
throw new TarException(errorText);
}
if (currentBlockIndex >= BlockFactor)
{
WriteRecord();
}
Array.Copy(buffer, offset, recordBuffer, (currentBlockIndex * BlockSize), BlockSize);
currentBlockIndex++;
}
/// <summary>
/// Write a TarBuffer record to the archive.
/// </summary>
private void WriteRecord()
{
if (outputStream == null)
{
throw new TarException("TarBuffer.WriteRecord no output stream defined");
}
outputStream.Write(recordBuffer, 0, RecordSize);
outputStream.Flush();
currentBlockIndex = 0;
currentRecordIndex++;
}
/// <summary>
/// WriteFinalRecord writes the current record buffer to output any unwritten data is present.
/// </summary>
/// <remarks>Any trailing bytes are set to zero which is by definition correct behaviour
/// for the end of a tar stream.</remarks>
private void WriteFinalRecord()
{
if (outputStream == null)
{
throw new TarException("TarBuffer.WriteFinalRecord no output stream defined");
}
if (currentBlockIndex > 0)
{
int dataBytes = currentBlockIndex * BlockSize;
Array.Clear(recordBuffer, dataBytes, RecordSize - dataBytes);
WriteRecord();
}
outputStream.Flush();
}
/// <summary>
/// Close the TarBuffer. If this is an output buffer, also flush the
/// current block before closing.
/// </summary>
public void Close()
{
if (outputStream != null)
{
WriteFinalRecord();
if (IsStreamOwner)
{
outputStream.Dispose();
}
outputStream = null;
}
else if (inputStream != null)
{
if (IsStreamOwner)
{
inputStream.Dispose();
}
inputStream = null;
}
}
#region Instance Fields
private Stream inputStream;
private Stream outputStream;
private byte[] recordBuffer;
private int currentBlockIndex;
private int currentRecordIndex;
private int recordSize = DefaultRecordSize;
private int blockFactor = DefaultBlockFactor;
#endregion Instance Fields
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 80e87b27138c94dd5bc4ed24acfd4394
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,547 @@
using System;
using System.IO;
namespace ICSharpCode.SharpZipLib.Tar
{
/// <summary>
/// This class represents an entry in a Tar archive. It consists
/// of the entry's header, as well as the entry's File. Entries
/// can be instantiated in one of three ways, depending on how
/// they are to be used.
/// <p>
/// TarEntries that are created from the header bytes read from
/// an archive are instantiated with the TarEntry( byte[] )
/// constructor. These entries will be used when extracting from
/// or listing the contents of an archive. These entries have their
/// header filled in using the header bytes. They also set the File
/// to null, since they reference an archive entry not a file.</p>
/// <p>
/// TarEntries that are created from files that are to be written
/// into an archive are instantiated with the CreateEntryFromFile(string)
/// pseudo constructor. These entries have their header filled in using
/// the File's information. They also keep a reference to the File
/// for convenience when writing entries.</p>
/// <p>
/// Finally, TarEntries can be constructed from nothing but a name.
/// This allows the programmer to construct the entry by hand, for
/// instance when only an InputStream is available for writing to
/// the archive, and the header information is constructed from
/// other information. In this case the header fields are set to
/// defaults and the File is set to null.</p>
/// <see cref="TarHeader"/>
/// </summary>
public class TarEntry
{
#region Constructors
/// <summary>
/// Initialise a default instance of <see cref="TarEntry"/>.
/// </summary>
private TarEntry()
{
header = new TarHeader();
}
/// <summary>
/// Construct an entry from an archive's header bytes. File is set
/// to null.
/// </summary>
/// <param name = "headerBuffer">
/// The header bytes from a tar archive entry.
/// </param>
public TarEntry(byte[] headerBuffer)
{
header = new TarHeader();
header.ParseBuffer(headerBuffer);
}
/// <summary>
/// Construct a TarEntry using the <paramref name="header">header</paramref> provided
/// </summary>
/// <param name="header">Header details for entry</param>
public TarEntry(TarHeader header)
{
if (header == null)
{
throw new ArgumentNullException(nameof(header));
}
this.header = (TarHeader)header.Clone();
}
#endregion Constructors
#region ICloneable Members
/// <summary>
/// Clone this tar entry.
/// </summary>
/// <returns>Returns a clone of this entry.</returns>
public object Clone()
{
var entry = new TarEntry();
entry.file = file;
entry.header = (TarHeader)header.Clone();
entry.Name = Name;
return entry;
}
#endregion ICloneable Members
/// <summary>
/// Construct an entry with only a <paramref name="name">name</paramref>.
/// This allows the programmer to construct the entry's header "by hand".
/// </summary>
/// <param name="name">The name to use for the entry</param>
/// <returns>Returns the newly created <see cref="TarEntry"/></returns>
public static TarEntry CreateTarEntry(string name)
{
var entry = new TarEntry();
TarEntry.NameTarHeader(entry.header, name);
return entry;
}
/// <summary>
/// Construct an entry for a file. File is set to file, and the
/// header is constructed from information from the file.
/// </summary>
/// <param name = "fileName">The file name that the entry represents.</param>
/// <returns>Returns the newly created <see cref="TarEntry"/></returns>
public static TarEntry CreateEntryFromFile(string fileName)
{
var entry = new TarEntry();
entry.GetFileTarHeader(entry.header, fileName);
return entry;
}
/// <summary>
/// Determine if the two entries are equal. Equality is determined
/// by the header names being equal.
/// </summary>
/// <param name="obj">The <see cref="Object"/> to compare with the current Object.</param>
/// <returns>
/// True if the entries are equal; false if not.
/// </returns>
public override bool Equals(object obj)
{
var localEntry = obj as TarEntry;
if (localEntry != null)
{
return Name.Equals(localEntry.Name);
}
return false;
}
/// <summary>
/// Derive a Hash value for the current <see cref="Object"/>
/// </summary>
/// <returns>A Hash code for the current <see cref="Object"/></returns>
public override int GetHashCode()
{
return Name.GetHashCode();
}
/// <summary>
/// Determine if the given entry is a descendant of this entry.
/// Descendancy is determined by the name of the descendant
/// starting with this entry's name.
/// </summary>
/// <param name = "toTest">
/// Entry to be checked as a descendent of this.
/// </param>
/// <returns>
/// True if entry is a descendant of this.
/// </returns>
public bool IsDescendent(TarEntry toTest)
{
if (toTest == null)
{
throw new ArgumentNullException(nameof(toTest));
}
return toTest.Name.StartsWith(Name, StringComparison.Ordinal);
}
/// <summary>
/// Get this entry's header.
/// </summary>
/// <returns>
/// This entry's TarHeader.
/// </returns>
public TarHeader TarHeader
{
get
{
return header;
}
}
/// <summary>
/// Get/Set this entry's name.
/// </summary>
public string Name
{
get
{
return header.Name;
}
set
{
header.Name = value;
}
}
/// <summary>
/// Get/set this entry's user id.
/// </summary>
public int UserId
{
get
{
return header.UserId;
}
set
{
header.UserId = value;
}
}
/// <summary>
/// Get/set this entry's group id.
/// </summary>
public int GroupId
{
get
{
return header.GroupId;
}
set
{
header.GroupId = value;
}
}
/// <summary>
/// Get/set this entry's user name.
/// </summary>
public string UserName
{
get
{
return header.UserName;
}
set
{
header.UserName = value;
}
}
/// <summary>
/// Get/set this entry's group name.
/// </summary>
public string GroupName
{
get
{
return header.GroupName;
}
set
{
header.GroupName = value;
}
}
/// <summary>
/// Convenience method to set this entry's group and user ids.
/// </summary>
/// <param name="userId">
/// This entry's new user id.
/// </param>
/// <param name="groupId">
/// This entry's new group id.
/// </param>
public void SetIds(int userId, int groupId)
{
UserId = userId;
GroupId = groupId;
}
/// <summary>
/// Convenience method to set this entry's group and user names.
/// </summary>
/// <param name="userName">
/// This entry's new user name.
/// </param>
/// <param name="groupName">
/// This entry's new group name.
/// </param>
public void SetNames(string userName, string groupName)
{
UserName = userName;
GroupName = groupName;
}
/// <summary>
/// Get/Set the modification time for this entry
/// </summary>
public DateTime ModTime
{
get
{
return header.ModTime;
}
set
{
header.ModTime = value;
}
}
/// <summary>
/// Get this entry's file.
/// </summary>
/// <returns>
/// This entry's file.
/// </returns>
public string File
{
get
{
return file;
}
}
/// <summary>
/// Get/set this entry's recorded file size.
/// </summary>
public long Size
{
get
{
return header.Size;
}
set
{
header.Size = value;
}
}
/// <summary>
/// Return true if this entry represents a directory, false otherwise
/// </summary>
/// <returns>
/// True if this entry is a directory.
/// </returns>
public bool IsDirectory
{
get
{
if (file != null)
{
return Directory.Exists(file);
}
if (header != null)
{
if ((header.TypeFlag == TarHeader.LF_DIR) || Name.EndsWith("/", StringComparison.Ordinal))
{
return true;
}
}
return false;
}
}
/// <summary>
/// Fill in a TarHeader with information from a File.
/// </summary>
/// <param name="header">
/// The TarHeader to fill in.
/// </param>
/// <param name="file">
/// The file from which to get the header information.
/// </param>
public void GetFileTarHeader(TarHeader header, string file)
{
if (header == null)
{
throw new ArgumentNullException(nameof(header));
}
if (file == null)
{
throw new ArgumentNullException(nameof(file));
}
this.file = file;
// bugfix from torhovl from #D forum:
string name = file;
// 23-Jan-2004 GnuTar allows device names in path where the name is not local to the current directory
if (name.IndexOf(Directory.GetCurrentDirectory(), StringComparison.Ordinal) == 0)
{
name = name.Substring(Directory.GetCurrentDirectory().Length);
}
/*
if (Path.DirectorySeparatorChar == '\\')
{
// check if the OS is Windows
// Strip off drive letters!
if (name.Length > 2)
{
char ch1 = name[0];
char ch2 = name[1];
if (ch2 == ':' && Char.IsLetter(ch1))
{
name = name.Substring(2);
}
}
}
*/
name = name.Replace(Path.DirectorySeparatorChar, '/');
// No absolute pathnames
// Windows (and Posix?) paths can start with UNC style "\\NetworkDrive\",
// so we loop on starting /'s.
while (name.StartsWith("/", StringComparison.Ordinal))
{
name = name.Substring(1);
}
header.LinkName = String.Empty;
header.Name = name;
if (Directory.Exists(file))
{
header.Mode = 1003; // Magic number for security access for a UNIX filesystem
header.TypeFlag = TarHeader.LF_DIR;
if ((header.Name.Length == 0) || header.Name[header.Name.Length - 1] != '/')
{
header.Name = header.Name + "/";
}
header.Size = 0;
}
else
{
header.Mode = 33216; // Magic number for security access for a UNIX filesystem
header.TypeFlag = TarHeader.LF_NORMAL;
header.Size = new FileInfo(file.Replace('/', Path.DirectorySeparatorChar)).Length;
}
header.ModTime = System.IO.File.GetLastWriteTime(file.Replace('/', Path.DirectorySeparatorChar)).ToUniversalTime();
header.DevMajor = 0;
header.DevMinor = 0;
}
/// <summary>
/// Get entries for all files present in this entries directory.
/// If this entry doesnt represent a directory zero entries are returned.
/// </summary>
/// <returns>
/// An array of TarEntry's for this entry's children.
/// </returns>
public TarEntry[] GetDirectoryEntries()
{
if ((file == null) || !Directory.Exists(file))
{
return new TarEntry[0];
}
string[] list = Directory.GetFileSystemEntries(file);
TarEntry[] result = new TarEntry[list.Length];
for (int i = 0; i < list.Length; ++i)
{
result[i] = TarEntry.CreateEntryFromFile(list[i]);
}
return result;
}
/// <summary>
/// Write an entry's header information to a header buffer.
/// </summary>
/// <param name = "outBuffer">
/// The tar entry header buffer to fill in.
/// </param>
public void WriteEntryHeader(byte[] outBuffer)
{
header.WriteHeader(outBuffer);
}
/// <summary>
/// Convenience method that will modify an entry's name directly
/// in place in an entry header buffer byte array.
/// </summary>
/// <param name="buffer">
/// The buffer containing the entry header to modify.
/// </param>
/// <param name="newName">
/// The new name to place into the header buffer.
/// </param>
static public void AdjustEntryName(byte[] buffer, string newName)
{
TarHeader.GetNameBytes(newName, buffer, 0, TarHeader.NAMELEN);
}
/// <summary>
/// Fill in a TarHeader given only the entry's name.
/// </summary>
/// <param name="header">
/// The TarHeader to fill in.
/// </param>
/// <param name="name">
/// The tar entry name.
/// </param>
static public void NameTarHeader(TarHeader header, string name)
{
if (header == null)
{
throw new ArgumentNullException(nameof(header));
}
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
bool isDir = name.EndsWith("/", StringComparison.Ordinal);
header.Name = name;
header.Mode = isDir ? 1003 : 33216;
header.UserId = 0;
header.GroupId = 0;
header.Size = 0;
header.ModTime = DateTime.UtcNow;
header.TypeFlag = isDir ? TarHeader.LF_DIR : TarHeader.LF_NORMAL;
header.LinkName = String.Empty;
header.UserName = String.Empty;
header.GroupName = String.Empty;
header.DevMajor = 0;
header.DevMinor = 0;
}
#region Instance Fields
/// <summary>
/// The name of the file this entry represents or null if the entry is not based on a file.
/// </summary>
private string file;
/// <summary>
/// The entry's header information.
/// </summary>
private TarHeader header;
#endregion Instance Fields
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 71d74fccb444244a5b0183dcca3e7e3b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,54 @@
using System;
using System.Runtime.Serialization;
namespace ICSharpCode.SharpZipLib.Tar
{
/// <summary>
/// TarException represents exceptions specific to Tar classes and code.
/// </summary>
[Serializable]
public class TarException : SharpZipBaseException
{
/// <summary>
/// Initialise a new instance of <see cref="TarException" />.
/// </summary>
public TarException()
{
}
/// <summary>
/// Initialise a new instance of <see cref="TarException" /> with its message string.
/// </summary>
/// <param name="message">A <see cref="string"/> that describes the error.</param>
public TarException(string message)
: base(message)
{
}
/// <summary>
/// Initialise a new instance of <see cref="TarException" />.
/// </summary>
/// <param name="message">A <see cref="string"/> that describes the error.</param>
/// <param name="innerException">The <see cref="Exception"/> that caused this exception.</param>
public TarException(string message, Exception innerException)
: base(message, innerException)
{
}
/// <summary>
/// Initializes a new instance of the TarException class with serialized data.
/// </summary>
/// <param name="info">
/// The System.Runtime.Serialization.SerializationInfo that holds the serialized
/// object data about the exception being thrown.
/// </param>
/// <param name="context">
/// The System.Runtime.Serialization.StreamingContext that contains contextual information
/// about the source or destination.
/// </param>
protected TarException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ccdfee54c1d5b4ea5971d2b9e875fd11
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,99 @@
using System.Collections.Generic;
using System.Text;
namespace ICSharpCode.SharpZipLib.Tar
{
/// <summary>
/// Reads the extended header of a Tar stream
/// </summary>
public class TarExtendedHeaderReader
{
private const byte LENGTH = 0;
private const byte KEY = 1;
private const byte VALUE = 2;
private const byte END = 3;
private readonly Dictionary<string, string> headers = new Dictionary<string, string>();
private string[] headerParts = new string[3];
private int bbIndex;
private byte[] byteBuffer;
private char[] charBuffer;
private readonly StringBuilder sb = new StringBuilder();
private readonly Decoder decoder = Encoding.UTF8.GetDecoder();
private int state = LENGTH;
private static readonly byte[] StateNext = new[] { (byte)' ', (byte)'=', (byte)'\n' };
/// <summary>
/// Creates a new <see cref="TarExtendedHeaderReader"/>.
/// </summary>
public TarExtendedHeaderReader()
{
ResetBuffers();
}
/// <summary>
/// Read <paramref name="length"/> bytes from <paramref name="buffer"/>
/// </summary>
/// <param name="buffer"></param>
/// <param name="length"></param>
public void Read(byte[] buffer, int length)
{
for (int i = 0; i < length; i++)
{
byte next = buffer[i];
if (next == StateNext[state])
{
Flush();
headerParts[state] = sb.ToString();
sb.Clear();
if (++state == END)
{
headers.Add(headerParts[KEY], headerParts[VALUE]);
headerParts = new string[3];
state = LENGTH;
}
}
else
{
byteBuffer[bbIndex++] = next;
if (bbIndex == 4)
Flush();
}
}
}
private void Flush()
{
decoder.Convert(byteBuffer, 0, bbIndex, charBuffer, 0, 4, false, out int bytesUsed, out int charsUsed, out bool completed);
sb.Append(charBuffer, 0, charsUsed);
ResetBuffers();
}
private void ResetBuffers()
{
charBuffer = new char[4];
byteBuffer = new byte[4];
bbIndex = 0;
}
/// <summary>
/// Returns the parsed headers as key-value strings
/// </summary>
public Dictionary<string, string> Headers
{
get
{
// TODO: Check for invalid state? -NM 2018-07-01
return headers;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5c2988d61755f4662b1898f848bb3f4e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cfda2392b8aaf4099add24ae2b4b23bc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,726 @@
using System;
using System.IO;
using System.Text;
namespace ICSharpCode.SharpZipLib.Tar
{
/// <summary>
/// The TarInputStream reads a UNIX tar archive as an InputStream.
/// methods are provided to position at each successive entry in
/// the archive, and the read each entry as a normal input stream
/// using read().
/// </summary>
public class TarInputStream : Stream
{
#region Constructors
/// <summary>
/// Construct a TarInputStream with default block factor
/// </summary>
/// <param name="inputStream">stream to source data from</param>
public TarInputStream(Stream inputStream)
: this(inputStream, TarBuffer.DefaultBlockFactor)
{
}
/// <summary>
/// Construct a TarInputStream with user specified block factor
/// </summary>
/// <param name="inputStream">stream to source data from</param>
/// <param name="blockFactor">block factor to apply to archive</param>
public TarInputStream(Stream inputStream, int blockFactor)
{
this.inputStream = inputStream;
tarBuffer = TarBuffer.CreateInputTarBuffer(inputStream, blockFactor);
}
#endregion Constructors
/// <summary>
/// Gets or sets a flag indicating ownership of underlying stream.
/// When the flag is true <see cref="Stream.Dispose()" /> will close the underlying stream also.
/// </summary>
/// <remarks>The default value is true.</remarks>
public bool IsStreamOwner
{
get { return tarBuffer.IsStreamOwner; }
set { tarBuffer.IsStreamOwner = value; }
}
#region Stream Overrides
/// <summary>
/// Gets a value indicating whether the current stream supports reading
/// </summary>
public override bool CanRead
{
get
{
return inputStream.CanRead;
}
}
/// <summary>
/// Gets a value indicating whether the current stream supports seeking
/// This property always returns false.
/// </summary>
public override bool CanSeek
{
get
{
return false;
}
}
/// <summary>
/// Gets a value indicating if the stream supports writing.
/// This property always returns false.
/// </summary>
public override bool CanWrite
{
get
{
return false;
}
}
/// <summary>
/// The length in bytes of the stream
/// </summary>
public override long Length
{
get
{
return inputStream.Length;
}
}
/// <summary>
/// Gets or sets the position within the stream.
/// Setting the Position is not supported and throws a NotSupportedExceptionNotSupportedException
/// </summary>
/// <exception cref="NotSupportedException">Any attempt to set position</exception>
public override long Position
{
get
{
return inputStream.Position;
}
set
{
throw new NotSupportedException("TarInputStream Seek not supported");
}
}
/// <summary>
/// Flushes the baseInputStream
/// </summary>
public override void Flush()
{
inputStream.Flush();
}
/// <summary>
/// Set the streams position. This operation is not supported and will throw a NotSupportedException
/// </summary>
/// <param name="offset">The offset relative to the origin to seek to.</param>
/// <param name="origin">The <see cref="SeekOrigin"/> to start seeking from.</param>
/// <returns>The new position in the stream.</returns>
/// <exception cref="NotSupportedException">Any access</exception>
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException("TarInputStream Seek not supported");
}
/// <summary>
/// Sets the length of the stream
/// This operation is not supported and will throw a NotSupportedException
/// </summary>
/// <param name="value">The new stream length.</param>
/// <exception cref="NotSupportedException">Any access</exception>
public override void SetLength(long value)
{
throw new NotSupportedException("TarInputStream SetLength not supported");
}
/// <summary>
/// Writes a block of bytes to this stream using data from a buffer.
/// This operation is not supported and will throw a NotSupportedException
/// </summary>
/// <param name="buffer">The buffer containing bytes to write.</param>
/// <param name="offset">The offset in the buffer of the frist byte to write.</param>
/// <param name="count">The number of bytes to write.</param>
/// <exception cref="NotSupportedException">Any access</exception>
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException("TarInputStream Write not supported");
}
/// <summary>
/// Writes a byte to the current position in the file stream.
/// This operation is not supported and will throw a NotSupportedException
/// </summary>
/// <param name="value">The byte value to write.</param>
/// <exception cref="NotSupportedException">Any access</exception>
public override void WriteByte(byte value)
{
throw new NotSupportedException("TarInputStream WriteByte not supported");
}
/// <summary>
/// Reads a byte from the current tar archive entry.
/// </summary>
/// <returns>A byte cast to an int; -1 if the at the end of the stream.</returns>
public override int ReadByte()
{
byte[] oneByteBuffer = new byte[1];
int num = Read(oneByteBuffer, 0, 1);
if (num <= 0)
{
// return -1 to indicate that no byte was read.
return -1;
}
return oneByteBuffer[0];
}
/// <summary>
/// Reads bytes from the current tar archive entry.
///
/// This method is aware of the boundaries of the current
/// entry in the archive and will deal with them appropriately
/// </summary>
/// <param name="buffer">
/// The buffer into which to place bytes read.
/// </param>
/// <param name="offset">
/// The offset at which to place bytes read.
/// </param>
/// <param name="count">
/// The number of bytes to read.
/// </param>
/// <returns>
/// The number of bytes read, or 0 at end of stream/EOF.
/// </returns>
public override int Read(byte[] buffer, int offset, int count)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
int totalRead = 0;
if (entryOffset >= entrySize)
{
return 0;
}
long numToRead = count;
if ((numToRead + entryOffset) > entrySize)
{
numToRead = entrySize - entryOffset;
}
if (readBuffer != null)
{
int sz = (numToRead > readBuffer.Length) ? readBuffer.Length : (int)numToRead;
Array.Copy(readBuffer, 0, buffer, offset, sz);
if (sz >= readBuffer.Length)
{
readBuffer = null;
}
else
{
int newLen = readBuffer.Length - sz;
byte[] newBuf = new byte[newLen];
Array.Copy(readBuffer, sz, newBuf, 0, newLen);
readBuffer = newBuf;
}
totalRead += sz;
numToRead -= sz;
offset += sz;
}
while (numToRead > 0)
{
byte[] rec = tarBuffer.ReadBlock();
if (rec == null)
{
// Unexpected EOF!
throw new TarException("unexpected EOF with " + numToRead + " bytes unread");
}
var sz = (int)numToRead;
int recLen = rec.Length;
if (recLen > sz)
{
Array.Copy(rec, 0, buffer, offset, sz);
readBuffer = new byte[recLen - sz];
Array.Copy(rec, sz, readBuffer, 0, recLen - sz);
}
else
{
sz = recLen;
Array.Copy(rec, 0, buffer, offset, recLen);
}
totalRead += sz;
numToRead -= sz;
offset += sz;
}
entryOffset += totalRead;
return totalRead;
}
/// <summary>
/// Closes this stream. Calls the TarBuffer's close() method.
/// The underlying stream is closed by the TarBuffer.
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing)
{
tarBuffer.Close();
}
}
#endregion Stream Overrides
/// <summary>
/// Set the entry factory for this instance.
/// </summary>
/// <param name="factory">The factory for creating new entries</param>
public void SetEntryFactory(IEntryFactory factory)
{
entryFactory = factory;
}
/// <summary>
/// Get the record size being used by this stream's TarBuffer.
/// </summary>
public int RecordSize
{
get { return tarBuffer.RecordSize; }
}
/// <summary>
/// Get the record size being used by this stream's TarBuffer.
/// </summary>
/// <returns>
/// TarBuffer record size.
/// </returns>
[Obsolete("Use RecordSize property instead")]
public int GetRecordSize()
{
return tarBuffer.RecordSize;
}
/// <summary>
/// Get the available data that can be read from the current
/// entry in the archive. This does not indicate how much data
/// is left in the entire archive, only in the current entry.
/// This value is determined from the entry's size header field
/// and the amount of data already read from the current entry.
/// </summary>
/// <returns>
/// The number of available bytes for the current entry.
/// </returns>
public long Available
{
get
{
return entrySize - entryOffset;
}
}
/// <summary>
/// Skip bytes in the input buffer. This skips bytes in the
/// current entry's data, not the entire archive, and will
/// stop at the end of the current entry's data if the number
/// to skip extends beyond that point.
/// </summary>
/// <param name="skipCount">
/// The number of bytes to skip.
/// </param>
public void Skip(long skipCount)
{
// TODO: REVIEW efficiency of TarInputStream.Skip
// This is horribly inefficient, but it ensures that we
// properly skip over bytes via the TarBuffer...
//
byte[] skipBuf = new byte[8 * 1024];
for (long num = skipCount; num > 0;)
{
int toRead = num > skipBuf.Length ? skipBuf.Length : (int)num;
int numRead = Read(skipBuf, 0, toRead);
if (numRead == -1)
{
break;
}
num -= numRead;
}
}
/// <summary>
/// Return a value of true if marking is supported; false otherwise.
/// </summary>
/// <remarks>Currently marking is not supported, the return value is always false.</remarks>
public bool IsMarkSupported
{
get
{
return false;
}
}
/// <summary>
/// Since we do not support marking just yet, we do nothing.
/// </summary>
/// <param name ="markLimit">
/// The limit to mark.
/// </param>
public void Mark(int markLimit)
{
}
/// <summary>
/// Since we do not support marking just yet, we do nothing.
/// </summary>
public void Reset()
{
}
/// <summary>
/// Get the next entry in this tar archive. This will skip
/// over any remaining data in the current entry, if there
/// is one, and place the input stream at the header of the
/// next entry, and read the header and instantiate a new
/// TarEntry from the header bytes and return that entry.
/// If there are no more entries in the archive, null will
/// be returned to indicate that the end of the archive has
/// been reached.
/// </summary>
/// <returns>
/// The next TarEntry in the archive, or null.
/// </returns>
public TarEntry GetNextEntry()
{
if (hasHitEOF)
{
return null;
}
if (currentEntry != null)
{
SkipToNextEntry();
}
byte[] headerBuf = tarBuffer.ReadBlock();
if (headerBuf == null)
{
hasHitEOF = true;
}
else if (TarBuffer.IsEndOfArchiveBlock(headerBuf))
{
hasHitEOF = true;
// Read the second zero-filled block
tarBuffer.ReadBlock();
}
else
{
hasHitEOF = false;
}
if (hasHitEOF)
{
currentEntry = null;
}
else
{
try
{
var header = new TarHeader();
header.ParseBuffer(headerBuf);
if (!header.IsChecksumValid)
{
throw new TarException("Header checksum is invalid");
}
this.entryOffset = 0;
this.entrySize = header.Size;
StringBuilder longName = null;
if (header.TypeFlag == TarHeader.LF_GNU_LONGNAME)
{
byte[] nameBuffer = new byte[TarBuffer.BlockSize];
long numToRead = this.entrySize;
longName = new StringBuilder();
while (numToRead > 0)
{
int numRead = this.Read(nameBuffer, 0, (numToRead > nameBuffer.Length ? nameBuffer.Length : (int)numToRead));
if (numRead == -1)
{
throw new InvalidHeaderException("Failed to read long name entry");
}
longName.Append(TarHeader.ParseName(nameBuffer, 0, numRead).ToString());
numToRead -= numRead;
}
SkipToNextEntry();
headerBuf = this.tarBuffer.ReadBlock();
}
else if (header.TypeFlag == TarHeader.LF_GHDR)
{ // POSIX global extended header
// Ignore things we dont understand completely for now
SkipToNextEntry();
headerBuf = this.tarBuffer.ReadBlock();
}
else if (header.TypeFlag == TarHeader.LF_XHDR)
{ // POSIX extended header
byte[] nameBuffer = new byte[TarBuffer.BlockSize];
long numToRead = this.entrySize;
var xhr = new TarExtendedHeaderReader();
while (numToRead > 0)
{
int numRead = this.Read(nameBuffer, 0, (numToRead > nameBuffer.Length ? nameBuffer.Length : (int)numToRead));
if (numRead == -1)
{
throw new InvalidHeaderException("Failed to read long name entry");
}
xhr.Read(nameBuffer, numRead);
numToRead -= numRead;
}
if (xhr.Headers.TryGetValue("path", out string name))
{
longName = new StringBuilder(name);
}
SkipToNextEntry();
headerBuf = this.tarBuffer.ReadBlock();
}
else if (header.TypeFlag == TarHeader.LF_GNU_VOLHDR)
{
// TODO: could show volume name when verbose
SkipToNextEntry();
headerBuf = this.tarBuffer.ReadBlock();
}
else if (header.TypeFlag != TarHeader.LF_NORMAL &&
header.TypeFlag != TarHeader.LF_OLDNORM &&
header.TypeFlag != TarHeader.LF_LINK &&
header.TypeFlag != TarHeader.LF_SYMLINK &&
header.TypeFlag != TarHeader.LF_DIR)
{
// Ignore things we dont understand completely for now
SkipToNextEntry();
headerBuf = tarBuffer.ReadBlock();
}
if (entryFactory == null)
{
currentEntry = new TarEntry(headerBuf);
if (longName != null)
{
currentEntry.Name = longName.ToString();
}
}
else
{
currentEntry = entryFactory.CreateEntry(headerBuf);
}
// Magic was checked here for 'ustar' but there are multiple valid possibilities
// so this is not done anymore.
entryOffset = 0;
// TODO: Review How do we resolve this discrepancy?!
entrySize = this.currentEntry.Size;
}
catch (InvalidHeaderException ex)
{
entrySize = 0;
entryOffset = 0;
currentEntry = null;
string errorText = string.Format("Bad header in record {0} block {1} {2}",
tarBuffer.CurrentRecord, tarBuffer.CurrentBlock, ex.Message);
throw new InvalidHeaderException(errorText);
}
}
return currentEntry;
}
/// <summary>
/// Copies the contents of the current tar archive entry directly into
/// an output stream.
/// </summary>
/// <param name="outputStream">
/// The OutputStream into which to write the entry's data.
/// </param>
public void CopyEntryContents(Stream outputStream)
{
byte[] tempBuffer = new byte[32 * 1024];
while (true)
{
int numRead = Read(tempBuffer, 0, tempBuffer.Length);
if (numRead <= 0)
{
break;
}
outputStream.Write(tempBuffer, 0, numRead);
}
}
private void SkipToNextEntry()
{
long numToSkip = entrySize - entryOffset;
if (numToSkip > 0)
{
Skip(numToSkip);
}
readBuffer = null;
}
/// <summary>
/// This interface is provided, along with the method <see cref="SetEntryFactory"/>, to allow
/// the programmer to have their own <see cref="TarEntry"/> subclass instantiated for the
/// entries return from <see cref="GetNextEntry"/>.
/// </summary>
public interface IEntryFactory
{
/// <summary>
/// Create an entry based on name alone
/// </summary>
/// <param name="name">
/// Name of the new EntryPointNotFoundException to create
/// </param>
/// <returns>created TarEntry or descendant class</returns>
TarEntry CreateEntry(string name);
/// <summary>
/// Create an instance based on an actual file
/// </summary>
/// <param name="fileName">
/// Name of file to represent in the entry
/// </param>
/// <returns>
/// Created TarEntry or descendant class
/// </returns>
TarEntry CreateEntryFromFile(string fileName);
/// <summary>
/// Create a tar entry based on the header information passed
/// </summary>
/// <param name="headerBuffer">
/// Buffer containing header information to create an an entry from.
/// </param>
/// <returns>
/// Created TarEntry or descendant class
/// </returns>
TarEntry CreateEntry(byte[] headerBuffer);
}
/// <summary>
/// Standard entry factory class creating instances of the class TarEntry
/// </summary>
public class EntryFactoryAdapter : IEntryFactory
{
/// <summary>
/// Create a <see cref="TarEntry"/> based on named
/// </summary>
/// <param name="name">The name to use for the entry</param>
/// <returns>A new <see cref="TarEntry"/></returns>
public TarEntry CreateEntry(string name)
{
return TarEntry.CreateTarEntry(name);
}
/// <summary>
/// Create a tar entry with details obtained from <paramref name="fileName">file</paramref>
/// </summary>
/// <param name="fileName">The name of the file to retrieve details from.</param>
/// <returns>A new <see cref="TarEntry"/></returns>
public TarEntry CreateEntryFromFile(string fileName)
{
return TarEntry.CreateEntryFromFile(fileName);
}
/// <summary>
/// Create an entry based on details in <paramref name="headerBuffer">header</paramref>
/// </summary>
/// <param name="headerBuffer">The buffer containing entry details.</param>
/// <returns>A new <see cref="TarEntry"/></returns>
public TarEntry CreateEntry(byte[] headerBuffer)
{
return new TarEntry(headerBuffer);
}
}
#region Instance Fields
/// <summary>
/// Flag set when last block has been read
/// </summary>
protected bool hasHitEOF;
/// <summary>
/// Size of this entry as recorded in header
/// </summary>
protected long entrySize;
/// <summary>
/// Number of bytes read for this entry so far
/// </summary>
protected long entryOffset;
/// <summary>
/// Buffer used with calls to <code>Read()</code>
/// </summary>
protected byte[] readBuffer;
/// <summary>
/// Working buffer
/// </summary>
protected TarBuffer tarBuffer;
/// <summary>
/// Current entry being read
/// </summary>
private TarEntry currentEntry;
/// <summary>
/// Factory used to create TarEntry or descendant class instance
/// </summary>
protected IEntryFactory entryFactory;
/// <summary>
/// Stream used as the source of input data.
/// </summary>
private readonly Stream inputStream;
#endregion Instance Fields
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 43169ee68d4e24cc1bbc5616e2e431ac
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,480 @@
using System;
using System.IO;
namespace ICSharpCode.SharpZipLib.Tar
{
/// <summary>
/// The TarOutputStream writes a UNIX tar archive as an OutputStream.
/// Methods are provided to put entries, and then write their contents
/// by writing to this stream using write().
/// </summary>
/// public
public class TarOutputStream : Stream
{
#region Constructors
/// <summary>
/// Construct TarOutputStream using default block factor
/// </summary>
/// <param name="outputStream">stream to write to</param>
public TarOutputStream(Stream outputStream)
: this(outputStream, TarBuffer.DefaultBlockFactor)
{
}
/// <summary>
/// Construct TarOutputStream with user specified block factor
/// </summary>
/// <param name="outputStream">stream to write to</param>
/// <param name="blockFactor">blocking factor</param>
public TarOutputStream(Stream outputStream, int blockFactor)
{
if (outputStream == null)
{
throw new ArgumentNullException(nameof(outputStream));
}
this.outputStream = outputStream;
buffer = TarBuffer.CreateOutputTarBuffer(outputStream, blockFactor);
assemblyBuffer = new byte[TarBuffer.BlockSize];
blockBuffer = new byte[TarBuffer.BlockSize];
}
#endregion Constructors
/// <summary>
/// Gets or sets a flag indicating ownership of underlying stream.
/// When the flag is true <see cref="Stream.Dispose()" /> will close the underlying stream also.
/// </summary>
/// <remarks>The default value is true.</remarks>
public bool IsStreamOwner
{
get { return buffer.IsStreamOwner; }
set { buffer.IsStreamOwner = value; }
}
/// <summary>
/// true if the stream supports reading; otherwise, false.
/// </summary>
public override bool CanRead
{
get
{
return outputStream.CanRead;
}
}
/// <summary>
/// true if the stream supports seeking; otherwise, false.
/// </summary>
public override bool CanSeek
{
get
{
return outputStream.CanSeek;
}
}
/// <summary>
/// true if stream supports writing; otherwise, false.
/// </summary>
public override bool CanWrite
{
get
{
return outputStream.CanWrite;
}
}
/// <summary>
/// length of stream in bytes
/// </summary>
public override long Length
{
get
{
return outputStream.Length;
}
}
/// <summary>
/// gets or sets the position within the current stream.
/// </summary>
public override long Position
{
get
{
return outputStream.Position;
}
set
{
outputStream.Position = value;
}
}
/// <summary>
/// set the position within the current stream
/// </summary>
/// <param name="offset">The offset relative to the <paramref name="origin"/> to seek to</param>
/// <param name="origin">The <see cref="SeekOrigin"/> to seek from.</param>
/// <returns>The new position in the stream.</returns>
public override long Seek(long offset, SeekOrigin origin)
{
return outputStream.Seek(offset, origin);
}
/// <summary>
/// Set the length of the current stream
/// </summary>
/// <param name="value">The new stream length.</param>
public override void SetLength(long value)
{
outputStream.SetLength(value);
}
/// <summary>
/// Read a byte from the stream and advance the position within the stream
/// by one byte or returns -1 if at the end of the stream.
/// </summary>
/// <returns>The byte value or -1 if at end of stream</returns>
public override int ReadByte()
{
return outputStream.ReadByte();
}
/// <summary>
/// read bytes from the current stream and advance the position within the
/// stream by the number of bytes read.
/// </summary>
/// <param name="buffer">The buffer to store read bytes in.</param>
/// <param name="offset">The index into the buffer to being storing bytes at.</param>
/// <param name="count">The desired number of bytes to read.</param>
/// <returns>The total number of bytes read, or zero if at the end of the stream.
/// The number of bytes may be less than the <paramref name="count">count</paramref>
/// requested if data is not avialable.</returns>
public override int Read(byte[] buffer, int offset, int count)
{
return outputStream.Read(buffer, offset, count);
}
/// <summary>
/// All buffered data is written to destination
/// </summary>
public override void Flush()
{
outputStream.Flush();
}
/// <summary>
/// Ends the TAR archive without closing the underlying OutputStream.
/// The result is that the EOF block of nulls is written.
/// </summary>
public void Finish()
{
if (IsEntryOpen)
{
CloseEntry();
}
WriteEofBlock();
}
/// <summary>
/// Ends the TAR archive and closes the underlying OutputStream.
/// </summary>
/// <remarks>This means that Finish() is called followed by calling the
/// TarBuffer's Close().</remarks>
protected override void Dispose(bool disposing)
{
if (!isClosed)
{
isClosed = true;
Finish();
buffer.Close();
}
}
/// <summary>
/// Get the record size being used by this stream's TarBuffer.
/// </summary>
public int RecordSize
{
get { return buffer.RecordSize; }
}
/// <summary>
/// Get the record size being used by this stream's TarBuffer.
/// </summary>
/// <returns>
/// The TarBuffer record size.
/// </returns>
[Obsolete("Use RecordSize property instead")]
public int GetRecordSize()
{
return buffer.RecordSize;
}
/// <summary>
/// Get a value indicating wether an entry is open, requiring more data to be written.
/// </summary>
private bool IsEntryOpen
{
get { return (currBytes < currSize); }
}
/// <summary>
/// Put an entry on the output stream. This writes the entry's
/// header and positions the output stream for writing
/// the contents of the entry. Once this method is called, the
/// stream is ready for calls to write() to write the entry's
/// contents. Once the contents are written, closeEntry()
/// <B>MUST</B> be called to ensure that all buffered data
/// is completely written to the output stream.
/// </summary>
/// <param name="entry">
/// The TarEntry to be written to the archive.
/// </param>
public void PutNextEntry(TarEntry entry)
{
if (entry == null)
{
throw new ArgumentNullException(nameof(entry));
}
if (entry.TarHeader.Name.Length > TarHeader.NAMELEN)
{
var longHeader = new TarHeader();
longHeader.TypeFlag = TarHeader.LF_GNU_LONGNAME;
longHeader.Name = longHeader.Name + "././@LongLink";
longHeader.Mode = 420;//644 by default
longHeader.UserId = entry.UserId;
longHeader.GroupId = entry.GroupId;
longHeader.GroupName = entry.GroupName;
longHeader.UserName = entry.UserName;
longHeader.LinkName = "";
longHeader.Size = entry.TarHeader.Name.Length + 1; // Plus one to avoid dropping last char
longHeader.WriteHeader(blockBuffer);
buffer.WriteBlock(blockBuffer); // Add special long filename header block
int nameCharIndex = 0;
while (nameCharIndex < entry.TarHeader.Name.Length + 1 /* we've allocated one for the null char, now we must make sure it gets written out */)
{
Array.Clear(blockBuffer, 0, blockBuffer.Length);
TarHeader.GetAsciiBytes(entry.TarHeader.Name, nameCharIndex, this.blockBuffer, 0, TarBuffer.BlockSize); // This func handles OK the extra char out of string length
nameCharIndex += TarBuffer.BlockSize;
buffer.WriteBlock(blockBuffer);
}
}
entry.WriteEntryHeader(blockBuffer);
buffer.WriteBlock(blockBuffer);
currBytes = 0;
currSize = entry.IsDirectory ? 0 : entry.Size;
}
/// <summary>
/// Close an entry. This method MUST be called for all file
/// entries that contain data. The reason is that we must
/// buffer data written to the stream in order to satisfy
/// the buffer's block based writes. Thus, there may be
/// data fragments still being assembled that must be written
/// to the output stream before this entry is closed and the
/// next entry written.
/// </summary>
public void CloseEntry()
{
if (assemblyBufferLength > 0)
{
Array.Clear(assemblyBuffer, assemblyBufferLength, assemblyBuffer.Length - assemblyBufferLength);
buffer.WriteBlock(assemblyBuffer);
currBytes += assemblyBufferLength;
assemblyBufferLength = 0;
}
if (currBytes < currSize)
{
string errorText = string.Format(
"Entry closed at '{0}' before the '{1}' bytes specified in the header were written",
currBytes, currSize);
throw new TarException(errorText);
}
}
/// <summary>
/// Writes a byte to the current tar archive entry.
/// This method simply calls Write(byte[], int, int).
/// </summary>
/// <param name="value">
/// The byte to be written.
/// </param>
public override void WriteByte(byte value)
{
Write(new byte[] { value }, 0, 1);
}
/// <summary>
/// Writes bytes to the current tar archive entry. This method
/// is aware of the current entry and will throw an exception if
/// you attempt to write bytes past the length specified for the
/// current entry. The method is also (painfully) aware of the
/// record buffering required by TarBuffer, and manages buffers
/// that are not a multiple of recordsize in length, including
/// assembling records from small buffers.
/// </summary>
/// <param name = "buffer">
/// The buffer to write to the archive.
/// </param>
/// <param name = "offset">
/// The offset in the buffer from which to get bytes.
/// </param>
/// <param name = "count">
/// The number of bytes to write.
/// </param>
public override void Write(byte[] buffer, int offset, int count)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
if (offset < 0)
{
throw new ArgumentOutOfRangeException(nameof(offset), "Cannot be negative");
}
if (buffer.Length - offset < count)
{
throw new ArgumentException("offset and count combination is invalid");
}
if (count < 0)
{
throw new ArgumentOutOfRangeException(nameof(count), "Cannot be negative");
}
if ((currBytes + count) > currSize)
{
string errorText = string.Format("request to write '{0}' bytes exceeds size in header of '{1}' bytes",
count, this.currSize);
throw new ArgumentOutOfRangeException(nameof(count), errorText);
}
//
// We have to deal with assembly!!!
// The programmer can be writing little 32 byte chunks for all
// we know, and we must assemble complete blocks for writing.
// TODO REVIEW Maybe this should be in TarBuffer? Could that help to
// eliminate some of the buffer copying.
//
if (assemblyBufferLength > 0)
{
if ((assemblyBufferLength + count) >= blockBuffer.Length)
{
int aLen = blockBuffer.Length - assemblyBufferLength;
Array.Copy(assemblyBuffer, 0, blockBuffer, 0, assemblyBufferLength);
Array.Copy(buffer, offset, blockBuffer, assemblyBufferLength, aLen);
this.buffer.WriteBlock(blockBuffer);
currBytes += blockBuffer.Length;
offset += aLen;
count -= aLen;
assemblyBufferLength = 0;
}
else
{
Array.Copy(buffer, offset, assemblyBuffer, assemblyBufferLength, count);
offset += count;
assemblyBufferLength += count;
count -= count;
}
}
//
// When we get here we have EITHER:
// o An empty "assembly" buffer.
// o No bytes to write (count == 0)
//
while (count > 0)
{
if (count < blockBuffer.Length)
{
Array.Copy(buffer, offset, assemblyBuffer, assemblyBufferLength, count);
assemblyBufferLength += count;
break;
}
this.buffer.WriteBlock(buffer, offset);
int bufferLength = blockBuffer.Length;
currBytes += bufferLength;
count -= bufferLength;
offset += bufferLength;
}
}
/// <summary>
/// Write an EOF (end of archive) block to the tar archive.
/// The end of the archive is indicated by two blocks consisting entirely of zero bytes.
/// </summary>
private void WriteEofBlock()
{
Array.Clear(blockBuffer, 0, blockBuffer.Length);
buffer.WriteBlock(blockBuffer);
buffer.WriteBlock(blockBuffer);
}
#region Instance Fields
/// <summary>
/// bytes written for this entry so far
/// </summary>
private long currBytes;
/// <summary>
/// current 'Assembly' buffer length
/// </summary>
private int assemblyBufferLength;
/// <summary>
/// Flag indicating wether this instance has been closed or not.
/// </summary>
private bool isClosed;
/// <summary>
/// Size for the current entry
/// </summary>
protected long currSize;
/// <summary>
/// single block working buffer
/// </summary>
protected byte[] blockBuffer;
/// <summary>
/// 'Assembly' buffer used to assemble data before writing
/// </summary>
protected byte[] assemblyBuffer;
/// <summary>
/// TarBuffer used to provide correct blocking factor
/// </summary>
protected TarBuffer buffer;
/// <summary>
/// the destination stream for the archive contents
/// </summary>
protected Stream outputStream;
#endregion Instance Fields
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a910c12df350b4fe0af9e9c2de66abec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ead08a2de28e94719b78142a9b1c73e0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9936f103f67be4f939dd589880efe282
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,604 @@
using System;
namespace ICSharpCode.SharpZipLib.Zip.Compression
{
/// <summary>
/// This is the Deflater class. The deflater class compresses input
/// with the deflate algorithm described in RFC 1951. It has several
/// compression levels and three different strategies described below.
///
/// This class is <i>not</i> thread safe. This is inherent in the API, due
/// to the split of deflate and setInput.
///
/// author of the original java version : Jochen Hoenicke
/// </summary>
public class Deflater
{
#region Deflater Documentation
/*
* The Deflater can do the following state transitions:
*
* (1) -> INIT_STATE ----> INIT_FINISHING_STATE ---.
* / | (2) (5) |
* / v (5) |
* (3)| SETDICT_STATE ---> SETDICT_FINISHING_STATE |(3)
* \ | (3) | ,--------'
* | | | (3) /
* v v (5) v v
* (1) -> BUSY_STATE ----> FINISHING_STATE
* | (6)
* v
* FINISHED_STATE
* \_____________________________________/
* | (7)
* v
* CLOSED_STATE
*
* (1) If we should produce a header we start in INIT_STATE, otherwise
* we start in BUSY_STATE.
* (2) A dictionary may be set only when we are in INIT_STATE, then
* we change the state as indicated.
* (3) Whether a dictionary is set or not, on the first call of deflate
* we change to BUSY_STATE.
* (4) -- intentionally left blank -- :)
* (5) FINISHING_STATE is entered, when flush() is called to indicate that
* there is no more INPUT. There are also states indicating, that
* the header wasn't written yet.
* (6) FINISHED_STATE is entered, when everything has been flushed to the
* internal pending output buffer.
* (7) At any time (7)
*
*/
#endregion Deflater Documentation
#region Public Constants
/// <summary>
/// The best and slowest compression level. This tries to find very
/// long and distant string repetitions.
/// </summary>
public const int BEST_COMPRESSION = 9;
/// <summary>
/// The worst but fastest compression level.
/// </summary>
public const int BEST_SPEED = 1;
/// <summary>
/// The default compression level.
/// </summary>
public const int DEFAULT_COMPRESSION = -1;
/// <summary>
/// This level won't compress at all but output uncompressed blocks.
/// </summary>
public const int NO_COMPRESSION = 0;
/// <summary>
/// The compression method. This is the only method supported so far.
/// There is no need to use this constant at all.
/// </summary>
public const int DEFLATED = 8;
#endregion Public Constants
#region Public Enum
/// <summary>
/// Compression Level as an enum for safer use
/// </summary>
public enum CompressionLevel
{
/// <summary>
/// The best and slowest compression level. This tries to find very
/// long and distant string repetitions.
/// </summary>
BEST_COMPRESSION = Deflater.BEST_COMPRESSION,
/// <summary>
/// The worst but fastest compression level.
/// </summary>
BEST_SPEED = Deflater.BEST_SPEED,
/// <summary>
/// The default compression level.
/// </summary>
DEFAULT_COMPRESSION = Deflater.DEFAULT_COMPRESSION,
/// <summary>
/// This level won't compress at all but output uncompressed blocks.
/// </summary>
NO_COMPRESSION = Deflater.NO_COMPRESSION,
/// <summary>
/// The compression method. This is the only method supported so far.
/// There is no need to use this constant at all.
/// </summary>
DEFLATED = Deflater.DEFLATED
}
#endregion Public Enum
#region Local Constants
private const int IS_SETDICT = 0x01;
private const int IS_FLUSHING = 0x04;
private const int IS_FINISHING = 0x08;
private const int INIT_STATE = 0x00;
private const int SETDICT_STATE = 0x01;
// private static int INIT_FINISHING_STATE = 0x08;
// private static int SETDICT_FINISHING_STATE = 0x09;
private const int BUSY_STATE = 0x10;
private const int FLUSHING_STATE = 0x14;
private const int FINISHING_STATE = 0x1c;
private const int FINISHED_STATE = 0x1e;
private const int CLOSED_STATE = 0x7f;
#endregion Local Constants
#region Constructors
/// <summary>
/// Creates a new deflater with default compression level.
/// </summary>
public Deflater() : this(DEFAULT_COMPRESSION, false)
{
}
/// <summary>
/// Creates a new deflater with given compression level.
/// </summary>
/// <param name="level">
/// the compression level, a value between NO_COMPRESSION
/// and BEST_COMPRESSION, or DEFAULT_COMPRESSION.
/// </param>
/// <exception cref="System.ArgumentOutOfRangeException">if lvl is out of range.</exception>
public Deflater(int level) : this(level, false)
{
}
/// <summary>
/// Creates a new deflater with given compression level.
/// </summary>
/// <param name="level">
/// the compression level, a value between NO_COMPRESSION
/// and BEST_COMPRESSION.
/// </param>
/// <param name="noZlibHeaderOrFooter">
/// true, if we should suppress the Zlib/RFC1950 header at the
/// beginning and the adler checksum at the end of the output. This is
/// useful for the GZIP/PKZIP formats.
/// </param>
/// <exception cref="System.ArgumentOutOfRangeException">if lvl is out of range.</exception>
public Deflater(int level, bool noZlibHeaderOrFooter)
{
if (level == DEFAULT_COMPRESSION)
{
level = 6;
}
else if (level < NO_COMPRESSION || level > BEST_COMPRESSION)
{
throw new ArgumentOutOfRangeException(nameof(level));
}
pending = new DeflaterPending();
engine = new DeflaterEngine(pending, noZlibHeaderOrFooter);
this.noZlibHeaderOrFooter = noZlibHeaderOrFooter;
SetStrategy(DeflateStrategy.Default);
SetLevel(level);
Reset();
}
#endregion Constructors
/// <summary>
/// Resets the deflater. The deflater acts afterwards as if it was
/// just created with the same compression level and strategy as it
/// had before.
/// </summary>
public void Reset()
{
state = (noZlibHeaderOrFooter ? BUSY_STATE : INIT_STATE);
totalOut = 0;
pending.Reset();
engine.Reset();
}
/// <summary>
/// Gets the current adler checksum of the data that was processed so far.
/// </summary>
public int Adler
{
get
{
return engine.Adler;
}
}
/// <summary>
/// Gets the number of input bytes processed so far.
/// </summary>
public long TotalIn
{
get
{
return engine.TotalIn;
}
}
/// <summary>
/// Gets the number of output bytes so far.
/// </summary>
public long TotalOut
{
get
{
return totalOut;
}
}
/// <summary>
/// Flushes the current input block. Further calls to deflate() will
/// produce enough output to inflate everything in the current input
/// block. This is not part of Sun's JDK so I have made it package
/// private. It is used by DeflaterOutputStream to implement
/// flush().
/// </summary>
public void Flush()
{
state |= IS_FLUSHING;
}
/// <summary>
/// Finishes the deflater with the current input block. It is an error
/// to give more input after this method was called. This method must
/// be called to force all bytes to be flushed.
/// </summary>
public void Finish()
{
state |= (IS_FLUSHING | IS_FINISHING);
}
/// <summary>
/// Returns true if the stream was finished and no more output bytes
/// are available.
/// </summary>
public bool IsFinished
{
get
{
return (state == FINISHED_STATE) && pending.IsFlushed;
}
}
/// <summary>
/// Returns true, if the input buffer is empty.
/// You should then call setInput().
/// NOTE: This method can also return true when the stream
/// was finished.
/// </summary>
public bool IsNeedingInput
{
get
{
return engine.NeedsInput();
}
}
/// <summary>
/// Sets the data which should be compressed next. This should be only
/// called when needsInput indicates that more input is needed.
/// If you call setInput when needsInput() returns false, the
/// previous input that is still pending will be thrown away.
/// The given byte array should not be changed, before needsInput() returns
/// true again.
/// This call is equivalent to <code>setInput(input, 0, input.length)</code>.
/// </summary>
/// <param name="input">
/// the buffer containing the input data.
/// </param>
/// <exception cref="System.InvalidOperationException">
/// if the buffer was finished() or ended().
/// </exception>
public void SetInput(byte[] input)
{
SetInput(input, 0, input.Length);
}
/// <summary>
/// Sets the data which should be compressed next. This should be
/// only called when needsInput indicates that more input is needed.
/// The given byte array should not be changed, before needsInput() returns
/// true again.
/// </summary>
/// <param name="input">
/// the buffer containing the input data.
/// </param>
/// <param name="offset">
/// the start of the data.
/// </param>
/// <param name="count">
/// the number of data bytes of input.
/// </param>
/// <exception cref="System.InvalidOperationException">
/// if the buffer was Finish()ed or if previous input is still pending.
/// </exception>
public void SetInput(byte[] input, int offset, int count)
{
if ((state & IS_FINISHING) != 0)
{
throw new InvalidOperationException("Finish() already called");
}
engine.SetInput(input, offset, count);
}
/// <summary>
/// Sets the compression level. There is no guarantee of the exact
/// position of the change, but if you call this when needsInput is
/// true the change of compression level will occur somewhere near
/// before the end of the so far given input.
/// </summary>
/// <param name="level">
/// the new compression level.
/// </param>
public void SetLevel(int level)
{
if (level == DEFAULT_COMPRESSION)
{
level = 6;
}
else if (level < NO_COMPRESSION || level > BEST_COMPRESSION)
{
throw new ArgumentOutOfRangeException(nameof(level));
}
if (this.level != level)
{
this.level = level;
engine.SetLevel(level);
}
}
/// <summary>
/// Get current compression level
/// </summary>
/// <returns>Returns the current compression level</returns>
public int GetLevel()
{
return level;
}
/// <summary>
/// Sets the compression strategy. Strategy is one of
/// DEFAULT_STRATEGY, HUFFMAN_ONLY and FILTERED. For the exact
/// position where the strategy is changed, the same as for
/// SetLevel() applies.
/// </summary>
/// <param name="strategy">
/// The new compression strategy.
/// </param>
public void SetStrategy(DeflateStrategy strategy)
{
engine.Strategy = strategy;
}
/// <summary>
/// Deflates the current input block with to the given array.
/// </summary>
/// <param name="output">
/// The buffer where compressed data is stored
/// </param>
/// <returns>
/// The number of compressed bytes added to the output, or 0 if either
/// IsNeedingInput() or IsFinished returns true or length is zero.
/// </returns>
public int Deflate(byte[] output)
{
return Deflate(output, 0, output.Length);
}
/// <summary>
/// Deflates the current input block to the given array.
/// </summary>
/// <param name="output">
/// Buffer to store the compressed data.
/// </param>
/// <param name="offset">
/// Offset into the output array.
/// </param>
/// <param name="length">
/// The maximum number of bytes that may be stored.
/// </param>
/// <returns>
/// The number of compressed bytes added to the output, or 0 if either
/// needsInput() or finished() returns true or length is zero.
/// </returns>
/// <exception cref="System.InvalidOperationException">
/// If Finish() was previously called.
/// </exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// If offset or length don't match the array length.
/// </exception>
public int Deflate(byte[] output, int offset, int length)
{
int origLength = length;
if (state == CLOSED_STATE)
{
throw new InvalidOperationException("Deflater closed");
}
if (state < BUSY_STATE)
{
// output header
int header = (DEFLATED +
((DeflaterConstants.MAX_WBITS - 8) << 4)) << 8;
int level_flags = (level - 1) >> 1;
if (level_flags < 0 || level_flags > 3)
{
level_flags = 3;
}
header |= level_flags << 6;
if ((state & IS_SETDICT) != 0)
{
// Dictionary was set
header |= DeflaterConstants.PRESET_DICT;
}
header += 31 - (header % 31);
pending.WriteShortMSB(header);
if ((state & IS_SETDICT) != 0)
{
int chksum = engine.Adler;
engine.ResetAdler();
pending.WriteShortMSB(chksum >> 16);
pending.WriteShortMSB(chksum & 0xffff);
}
state = BUSY_STATE | (state & (IS_FLUSHING | IS_FINISHING));
}
for (; ; )
{
int count = pending.Flush(output, offset, length);
offset += count;
totalOut += count;
length -= count;
if (length == 0 || state == FINISHED_STATE)
{
break;
}
if (!engine.Deflate((state & IS_FLUSHING) != 0, (state & IS_FINISHING) != 0))
{
switch (state)
{
case BUSY_STATE:
// We need more input now
return origLength - length;
case FLUSHING_STATE:
if (level != NO_COMPRESSION)
{
/* We have to supply some lookahead. 8 bit lookahead
* is needed by the zlib inflater, and we must fill
* the next byte, so that all bits are flushed.
*/
int neededbits = 8 + ((-pending.BitCount) & 7);
while (neededbits > 0)
{
/* write a static tree block consisting solely of
* an EOF:
*/
pending.WriteBits(2, 10);
neededbits -= 10;
}
}
state = BUSY_STATE;
break;
case FINISHING_STATE:
pending.AlignToByte();
// Compressed data is complete. Write footer information if required.
if (!noZlibHeaderOrFooter)
{
int adler = engine.Adler;
pending.WriteShortMSB(adler >> 16);
pending.WriteShortMSB(adler & 0xffff);
}
state = FINISHED_STATE;
break;
}
}
}
return origLength - length;
}
/// <summary>
/// Sets the dictionary which should be used in the deflate process.
/// This call is equivalent to <code>setDictionary(dict, 0, dict.Length)</code>.
/// </summary>
/// <param name="dictionary">
/// the dictionary.
/// </param>
/// <exception cref="System.InvalidOperationException">
/// if SetInput () or Deflate () were already called or another dictionary was already set.
/// </exception>
public void SetDictionary(byte[] dictionary)
{
SetDictionary(dictionary, 0, dictionary.Length);
}
/// <summary>
/// Sets the dictionary which should be used in the deflate process.
/// The dictionary is a byte array containing strings that are
/// likely to occur in the data which should be compressed. The
/// dictionary is not stored in the compressed output, only a
/// checksum. To decompress the output you need to supply the same
/// dictionary again.
/// </summary>
/// <param name="dictionary">
/// The dictionary data
/// </param>
/// <param name="index">
/// The index where dictionary information commences.
/// </param>
/// <param name="count">
/// The number of bytes in the dictionary.
/// </param>
/// <exception cref="System.InvalidOperationException">
/// If SetInput () or Deflate() were already called or another dictionary was already set.
/// </exception>
public void SetDictionary(byte[] dictionary, int index, int count)
{
if (state != INIT_STATE)
{
throw new InvalidOperationException();
}
state = SETDICT_STATE;
engine.SetDictionary(dictionary, index, count);
}
#region Instance Fields
/// <summary>
/// Compression level.
/// </summary>
private int level;
/// <summary>
/// If true no Zlib/RFC1950 headers or footers are generated
/// </summary>
private bool noZlibHeaderOrFooter;
/// <summary>
/// The current state.
/// </summary>
private int state;
/// <summary>
/// The total bytes of output written.
/// </summary>
private long totalOut;
/// <summary>
/// The pending output.
/// </summary>
private DeflaterPending pending;
/// <summary>
/// The deflater engine.
/// </summary>
private DeflaterEngine engine;
#endregion Instance Fields
}
}

Some files were not shown because too many files have changed in this diff Show More