1
+ using System ;
2
+ using System . IO ;
3
+ using SabreTools . IO . Extensions ;
4
+
5
+ namespace SabreTools . Compression . MSZIP
6
+ {
7
+ /// <see href="https://msopenspecs.azureedge.net/files/MS-MCI/%5bMS-MCI%5d.pdf"/>
8
+ public class Decompressor
9
+ {
10
+ /// <summary>
11
+ /// Source stream for the decompressor
12
+ /// </summary>
13
+ private readonly Stream _source ;
14
+
15
+ #region Constructors
16
+
17
+ /// <summary>
18
+ /// Create a MS-ZIP decompressor
19
+ /// </summary>
20
+ private Decompressor ( Stream source )
21
+ {
22
+ // Validate the inputs
23
+ if ( source . Length == 0 )
24
+ throw new ArgumentOutOfRangeException ( nameof ( source ) ) ;
25
+ if ( ! source . CanRead )
26
+ throw new InvalidOperationException ( nameof ( source ) ) ;
27
+
28
+ _source = source ;
29
+ }
30
+
31
+ /// <summary>
32
+ /// Create a MS-ZIP decompressor
33
+ /// </summary>
34
+ public static Decompressor Create ( byte [ ] source )
35
+ => Create ( new MemoryStream ( source ) ) ;
36
+
37
+ /// <summary>
38
+ /// Create a MS-ZIP decompressor
39
+ /// </summary>
40
+ public static Decompressor Create ( Stream source )
41
+ {
42
+ // Create the decompressor
43
+ var decompressor = new Decompressor ( source ) ;
44
+
45
+ // Validate the header
46
+ var header = new Models . Compression . MSZIP . BlockHeader ( ) ;
47
+ header . Signature = source . ReadUInt16 ( ) ;
48
+ if ( header . Signature != 0x4B43 )
49
+ throw new InvalidDataException ( nameof ( source ) ) ;
50
+
51
+ // Return
52
+ return decompressor ;
53
+ }
54
+
55
+ #endregion
56
+
57
+ /// <summary>
58
+ /// Decompress source data to an output stream
59
+ /// </summary>
60
+ public bool CopyTo ( Stream dest )
61
+ {
62
+ // Ignore unwritable streams
63
+ if ( ! dest . CanWrite )
64
+ return false ;
65
+
66
+ byte [ ] ? history = null ;
67
+ while ( true )
68
+ {
69
+ byte [ ] buffer = new byte [ 32 * 1024 ] ;
70
+ var blockStream = new Deflate . DeflateStream ( _source , Deflate . CompressionMode . Decompress ) ;
71
+ if ( history != null )
72
+ blockStream . SetDictionary ( history ) ;
73
+
74
+ int read = blockStream . Read ( buffer , 0 , buffer . Length ) ;
75
+ if ( read <= 0 )
76
+ break ;
77
+
78
+ // Write to output
79
+ dest . Write ( buffer , 0 , read ) ;
80
+
81
+ // Save the history for rollover
82
+ history = new byte [ read ] ;
83
+ Array . Copy ( buffer , history , read ) ;
84
+
85
+ // Handle end of stream
86
+ if ( _source . Position >= _source . Length )
87
+ break ;
88
+
89
+ // Validate the header
90
+ var header = new Models . Compression . MSZIP . BlockHeader ( ) ;
91
+ header . Signature = _source . ReadUInt16 ( ) ;
92
+ if ( header . Signature != 0x4B43 )
93
+ break ;
94
+ }
95
+
96
+ // Flush and return
97
+ dest . Flush ( ) ;
98
+ return true ;
99
+ }
100
+ }
101
+ }
0 commit comments