1
- using SabreTools . Compression ;
1
+ using System ;
2
+ using System . IO ;
3
+ using System . Text ;
4
+ using SabreTools . Compression . MSZIP ;
5
+ using SabreTools . IO . Extensions ;
6
+ using SabreTools . Models . MicrosoftCabinet ;
7
+ using static SabreTools . Models . MicrosoftCabinet . Constants ;
2
8
3
9
namespace Test
4
10
{
@@ -8,5 +14,284 @@ public static void Main(string[] args)
8
14
{
9
15
// No implementation, used for experimentation
10
16
}
17
+
18
+ private static void READMSZIPTEST ( )
19
+ {
20
+ using var fs = File . OpenRead ( "INFILE.cab" ) ;
21
+ var cab = Deserialize ( fs ) ;
22
+ if ( cab == null || cab . Folders == null || cab . Files == null )
23
+ return ;
24
+
25
+ for ( int f = 0 ; f < cab ! . Folders . Length ; f ++ )
26
+ {
27
+ var folder = cab . Folders [ f ] ;
28
+ if ( folder ? . DataBlocks == null || folder . DataBlocks . Length == 0 )
29
+ continue ;
30
+
31
+ var decomp = Decompressor . Create ( ) ;
32
+
33
+ var ms = new MemoryStream ( ) ;
34
+ foreach ( var db in folder . DataBlocks )
35
+ {
36
+ if ( db ? . CompressedData == null )
37
+ continue ;
38
+
39
+ decomp . CopyTo ( db . CompressedData , ms ) ;
40
+ }
41
+
42
+ if ( cab ? . Files == null || cab . Files . Length == 0 )
43
+ continue ;
44
+
45
+ foreach ( var file in cab . Files )
46
+ {
47
+ if ( file ? . Name == null || file . FolderIndex != ( FolderIndex ) f )
48
+ continue ;
49
+
50
+ byte [ ] fileData = new byte [ file . FileSize ] ;
51
+ Array . Copy ( ms . ToArray ( ) , file . FolderStartOffset , fileData , 0 , file . FileSize ) ;
52
+
53
+ using var of = File . OpenWrite ( Path . Combine ( "OUTDIR" , file . Name ) ) ;
54
+ of . Write ( fileData ) ;
55
+ of . Flush ( ) ;
56
+ }
57
+ }
58
+ }
59
+
60
+ /// <summary>
61
+ /// Parse a Stream into a cabinet
62
+ /// </summary>
63
+ /// <param name="data">Stream to parse</param>
64
+ /// <returns>Filled cabinet on success, null on error</returns>
65
+ private static Cabinet ? Deserialize ( Stream ? data )
66
+ {
67
+ // If the data is invalid
68
+ if ( data == null || ! data . CanRead )
69
+ return null ;
70
+
71
+ try
72
+ {
73
+ // Cache the current offset
74
+ int initialOffset = ( int ) data . Position ;
75
+
76
+ // Create a new cabinet to fill
77
+ var cabinet = new Cabinet ( ) ;
78
+
79
+ #region Cabinet Header
80
+
81
+ // Try to parse the cabinet header
82
+ var cabinetHeader = ParseCabinetHeader ( data ) ;
83
+ if ( cabinetHeader == null )
84
+ return null ;
85
+
86
+ // Set the cabinet header
87
+ cabinet . Header = cabinetHeader ;
88
+
89
+ #endregion
90
+
91
+ #region Folders
92
+
93
+ // Set the folder array
94
+ cabinet . Folders = new CFFOLDER [ cabinetHeader . FolderCount ] ;
95
+
96
+ // Try to parse each folder, if we have any
97
+ for ( int i = 0 ; i < cabinetHeader . FolderCount ; i ++ )
98
+ {
99
+ var folder = ParseFolder ( data , cabinetHeader ) ;
100
+ if ( folder == null )
101
+ return null ;
102
+
103
+ // Set the folder
104
+ cabinet . Folders [ i ] = folder ;
105
+ }
106
+
107
+ #endregion
108
+
109
+ #region Files
110
+
111
+ // Get the files offset
112
+ int filesOffset = ( int ) cabinetHeader . FilesOffset + initialOffset ;
113
+ if ( filesOffset > data . Length )
114
+ return null ;
115
+
116
+ // Seek to the offset
117
+ data . Seek ( filesOffset , SeekOrigin . Begin ) ;
118
+
119
+ // Set the file array
120
+ cabinet . Files = new CFFILE [ cabinetHeader . FileCount ] ;
121
+
122
+ // Try to parse each file, if we have any
123
+ for ( int i = 0 ; i < cabinetHeader . FileCount ; i ++ )
124
+ {
125
+ var file = ParseFile ( data ) ;
126
+ if ( file == null )
127
+ return null ;
128
+
129
+ // Set the file
130
+ cabinet . Files [ i ] = file ;
131
+ }
132
+
133
+ #endregion
134
+
135
+ return cabinet ;
136
+ }
137
+ catch
138
+ {
139
+ // Ignore the actual error
140
+ return null ;
141
+ }
142
+ }
143
+
144
+ /// <summary>
145
+ /// Parse a Stream into a cabinet header
146
+ /// </summary>
147
+ /// <param name="data">Stream to parse</param>
148
+ /// <returns>Filled cabinet header on success, null on error</returns>
149
+ private static CFHEADER ? ParseCabinetHeader ( Stream data )
150
+ {
151
+ var header = new CFHEADER ( ) ;
152
+
153
+ byte [ ] signature = data . ReadBytes ( 4 ) ;
154
+ header . Signature = Encoding . ASCII . GetString ( signature ) ;
155
+ if ( header . Signature != SignatureString )
156
+ return null ;
157
+
158
+ header . Reserved1 = data . ReadUInt32 ( ) ;
159
+ header . CabinetSize = data . ReadUInt32 ( ) ;
160
+ header . Reserved2 = data . ReadUInt32 ( ) ;
161
+ header . FilesOffset = data . ReadUInt32 ( ) ;
162
+ header . Reserved3 = data . ReadUInt32 ( ) ;
163
+ header . VersionMinor = data . ReadByteValue ( ) ;
164
+ header . VersionMajor = data . ReadByteValue ( ) ;
165
+ header . FolderCount = data . ReadUInt16 ( ) ;
166
+ header . FileCount = data . ReadUInt16 ( ) ;
167
+ header . Flags = ( HeaderFlags ) data . ReadUInt16 ( ) ;
168
+ header . SetID = data . ReadUInt16 ( ) ;
169
+ header . CabinetIndex = data . ReadUInt16 ( ) ;
170
+
171
+ #if NET20 || NET35
172
+ if ( ( header . Flags & HeaderFlags . RESERVE_PRESENT ) != 0 )
173
+ #else
174
+ if ( header . Flags . HasFlag ( HeaderFlags . RESERVE_PRESENT ) )
175
+ #endif
176
+ {
177
+ header . HeaderReservedSize = data . ReadUInt16 ( ) ;
178
+ if ( header . HeaderReservedSize > 60_000 )
179
+ return null ;
180
+
181
+ header . FolderReservedSize = data . ReadByteValue ( ) ;
182
+ header . DataReservedSize = data . ReadByteValue ( ) ;
183
+
184
+ if ( header . HeaderReservedSize > 0 )
185
+ header . ReservedData = data . ReadBytes ( header . HeaderReservedSize ) ;
186
+ }
187
+
188
+ #if NET20 || NET35
189
+ if ( ( header . Flags & HeaderFlags . PREV_CABINET ) != 0 )
190
+ #else
191
+ if ( header . Flags . HasFlag ( HeaderFlags . PREV_CABINET ) )
192
+ #endif
193
+ {
194
+ header . CabinetPrev = data . ReadNullTerminatedAnsiString ( ) ;
195
+ header . DiskPrev = data . ReadNullTerminatedAnsiString ( ) ;
196
+ }
197
+
198
+ #if NET20 || NET35
199
+ if ( ( header . Flags & HeaderFlags . NEXT_CABINET ) != 0 )
200
+ #else
201
+ if ( header . Flags . HasFlag ( HeaderFlags . NEXT_CABINET ) )
202
+ #endif
203
+ {
204
+ header . CabinetNext = data . ReadNullTerminatedAnsiString ( ) ;
205
+ header . DiskNext = data . ReadNullTerminatedAnsiString ( ) ;
206
+ }
207
+
208
+ return header ;
209
+ }
210
+
211
+ /// <summary>
212
+ /// Parse a Stream into a folder
213
+ /// </summary>
214
+ /// <param name="data">Stream to parse</param>
215
+ /// <param name="header">Cabinet header to get flags and sizes from</param>
216
+ /// <returns>Filled folder on success, null on error</returns>
217
+ private static CFFOLDER ParseFolder ( Stream data , CFHEADER header )
218
+ {
219
+ var folder = new CFFOLDER ( ) ;
220
+
221
+ folder . CabStartOffset = data . ReadUInt32 ( ) ;
222
+ folder . DataCount = data . ReadUInt16 ( ) ;
223
+ folder . CompressionType = ( CompressionType ) data . ReadUInt16 ( ) & CompressionType . MASK_TYPE ;
224
+
225
+ if ( header . FolderReservedSize > 0 )
226
+ folder . ReservedData = data . ReadBytes ( header . FolderReservedSize ) ;
227
+
228
+ if ( folder . CabStartOffset > 0 )
229
+ {
230
+ long currentPosition = data . Position ;
231
+ data . Seek ( folder . CabStartOffset , SeekOrigin . Begin ) ;
232
+
233
+ folder . DataBlocks = new CFDATA [ folder . DataCount ] ;
234
+ for ( int i = 0 ; i < folder . DataCount ; i ++ )
235
+ {
236
+ CFDATA dataBlock = ParseDataBlock ( data , header . DataReservedSize ) ;
237
+ folder . DataBlocks [ i ] = dataBlock ;
238
+ }
239
+
240
+ data . Seek ( currentPosition , SeekOrigin . Begin ) ;
241
+ }
242
+
243
+ return folder ;
244
+ }
245
+
246
+ /// <summary>
247
+ /// Parse a Stream into a data block
248
+ /// </summary>
249
+ /// <param name="data">Stream to parse</param>
250
+ /// <param name="dataReservedSize">Reserved byte size for data blocks</param>
251
+ /// <returns>Filled folder on success, null on error</returns>
252
+ private static CFDATA ParseDataBlock ( Stream data , byte dataReservedSize )
253
+ {
254
+ var dataBlock = new CFDATA ( ) ;
255
+
256
+ dataBlock . Checksum = data . ReadUInt32 ( ) ;
257
+ dataBlock . CompressedSize = data . ReadUInt16 ( ) ;
258
+ dataBlock . UncompressedSize = data . ReadUInt16 ( ) ;
259
+
260
+ if ( dataReservedSize > 0 )
261
+ dataBlock . ReservedData = data . ReadBytes ( dataReservedSize ) ;
262
+
263
+ if ( dataBlock . CompressedSize > 0 )
264
+ dataBlock . CompressedData = data . ReadBytes ( dataBlock . CompressedSize ) ;
265
+
266
+ return dataBlock ;
267
+ }
268
+
269
+ /// <summary>
270
+ /// Parse a Stream into a file
271
+ /// </summary>
272
+ /// <param name="data">Stream to parse</param>
273
+ /// <returns>Filled file on success, null on error</returns>
274
+ private static CFFILE ParseFile ( Stream data )
275
+ {
276
+ var file = new CFFILE ( ) ;
277
+
278
+ file . FileSize = data . ReadUInt32 ( ) ;
279
+ file . FolderStartOffset = data . ReadUInt32 ( ) ;
280
+ file . FolderIndex = ( FolderIndex ) data . ReadUInt16 ( ) ;
281
+ file . Date = data . ReadUInt16 ( ) ;
282
+ file . Time = data . ReadUInt16 ( ) ;
283
+ file . Attributes = ( SabreTools . Models . MicrosoftCabinet . FileAttributes ) data . ReadUInt16 ( ) ;
284
+
285
+ #if NET20 || NET35
286
+ if ( ( file . Attributes & SabreTools . Models . MicrosoftCabinet . FileAttributes . NAME_IS_UTF ) != 0 )
287
+ #else
288
+ if ( file . Attributes . HasFlag ( SabreTools . Models . MicrosoftCabinet . FileAttributes . NAME_IS_UTF ) )
289
+ #endif
290
+ file . Name = data . ReadNullTerminatedUnicodeString ( ) ;
291
+ else
292
+ file . Name = data . ReadNullTerminatedAnsiString ( ) ;
293
+
294
+ return file ;
295
+ }
11
296
}
12
297
}
0 commit comments