Reactos
1/*
2 vfdzip.c
3
4 Virtual Floppy Drive for Windows
5 Driver control library
6 Zip compressed floppy image handling
7
8 Copyright (C) 2003-2005 Ken Kato
9*/
10
11#ifdef __cplusplus
12#pragma message(__FILE__": Compiled as C++ for testing purpose.")
13#endif // __cplusplus
14
15#define WIN32_LEAN_AND_MEAN
16#include <windows.h>
17
18#include "vfdtypes.h"
19#include "vfdio.h"
20#include "vfdlib.h"
21
22#ifndef __REACTOS__
23#define ZLIB_WINAPI
24#else
25#define Z_SOLO
26#define ZLIB_INTERNAL
27#endif
28#include "zlib.h"
29
30#ifdef VFD_NO_ZLIB
31#pragma message("ZIP image support is disabled.")
32
33DWORD ExtractZipInfo(
34 HANDLE hFile,
35 ULONG *pSize)
36{
37 UNREFERENCED_PARAMETER(hFile);
38 UNREFERENCED_PARAMETER(pSize);
39 return ERROR_NOT_SUPPORTED;
40}
41
42DWORD ExtractZipImage(
43 HANDLE hFile,
44 PUCHAR *pBuffer,
45 PULONG pLength)
46{
47 UNREFERENCED_PARAMETER(hFile);
48 UNREFERENCED_PARAMETER(pBuffer);
49 UNREFERENCED_PARAMETER(pLength);
50 return ERROR_NOT_SUPPORTED;
51}
52
53#else // VFD_NO_ZLIB
54
55#ifdef _DEBUG
56static const char *ZLIB_ERROR(int err)
57{
58 switch (err) {
59 case Z_OK : return "Z_OK";
60 case Z_STREAM_END : return "Z_STREAM_END";
61 case Z_NEED_DICT : return "Z_NEED_DICT";
62 case Z_ERRNO : return "Z_ERRNO";
63 case Z_STREAM_ERROR : return "Z_STREAM_ERROR";
64 case Z_DATA_ERROR : return "Z_DATA_ERROR";
65 case Z_MEM_ERROR : return "Z_MEM_ERROR";
66 case Z_BUF_ERROR : return "Z_BUF_ERROR";
67 case Z_VERSION_ERROR: return "Z_VERSION_ERROR";
68 default : return "unknown";
69 }
70}
71#endif // _DEBUG
72
73voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size))
74{
75 UNREFERENCED_PARAMETER(opaque);
76 return LocalAlloc(LPTR, items * size);
77}
78
79void zcfree OF((voidpf opaque, voidpf ptr))
80{
81 UNREFERENCED_PARAMETER(opaque);
82 LocalFree(ptr);
83}
84
85#define ZIP_LOCAL_SIGNATURE 0x04034b50
86
87#define ZIP_FLAG_ENCRYPTED 0x01
88
89#define ZIP_FLAG_DEFLATE_NORMAL 0x00
90#define ZIP_FLAG_DEFLATE_MAX 0x02
91#define ZIP_FLAG_DEFLATE_FAST 0x04
92#define ZIP_FLAG_DEFLATE_SUPER 0x06
93#define ZIP_FLAG_DEFLATE_MASK 0x06
94
95#define ZIP_FLAG_SIZE_IN_DESC 0x08
96
97#define ZIP_METHOD_STORED 0
98#define ZIP_METHOD_SHRUNK 1
99#define ZIP_METHOD_REDUCED1 2
100#define ZIP_METHOD_REDUCED2 3
101#define ZIP_METHOD_REDUCED3 4
102#define ZIP_METHOD_REDUCED4 5
103#define ZIP_METHOD_IMPLODED 6
104#define ZIP_METHOD_TOKENIZED 7
105#define ZIP_METHOD_DEFLATED 8
106#define ZIP_METHOD_DEFLATE64 9
107#define ZIP_METHOD_PKWARE_IMP 10
108#define ZIP_METHOD_RESERVED 11
109#define ZIP_METHOD_BZIP2 12
110
111#pragma pack(1)
112
113typedef struct _zip_local_file_header {
114 ULONG header_signature;
115 USHORT required_version;
116 USHORT general_flags;
117 USHORT compression_method;
118 USHORT last_mod_time;
119 USHORT last_mod_date;
120 ULONG crc32;
121 ULONG compressed_size;
122 ULONG uncompressed_size;
123 USHORT file_name_length;
124 USHORT extra_field_length;
125 CHAR file_name[1];
126 // followed by extra field data, then compressed data
127}
128ZIP_HEADER, *PZIP_HEADER;
129
130//
131// Check if the file is ZIP compressed
132//
133DWORD ExtractZipInfo(
134 HANDLE hFile,
135 ULONG *pSize)
136{
137 ZIP_HEADER zip_hdr;
138 DWORD result;
139 DWORD ret;
140
141 if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) != 0) {
142 ret = GetLastError();
143
144 VFDTRACE(0,
145 ("SetFilePointer() - %s\n",
146 SystemMessage(ret)));
147
148 return ret;
149 }
150
151 if (!ReadFile(hFile, &zip_hdr, sizeof(zip_hdr), &result, NULL)) {
152 ret = GetLastError();
153
154 VFDTRACE(0,
155 ("ReadFile() - %s\n",
156 SystemMessage(ret)));
157
158 return ret;
159 }
160
161 if (result != sizeof(zip_hdr) ||
162 zip_hdr.header_signature != ZIP_LOCAL_SIGNATURE ||
163 zip_hdr.compression_method != ZIP_METHOD_DEFLATED ||
164 (zip_hdr.general_flags & ZIP_FLAG_ENCRYPTED)) {
165
166 VFDTRACE(0,
167 ("[VFD] Invalid ZIP file\n"));
168
169 return ERROR_INVALID_DATA;
170 }
171
172 // correct (and supported) ZIP header detected
173
174 *pSize = zip_hdr.uncompressed_size;
175
176 return ERROR_SUCCESS;
177}
178
179//
180// Extract original data from IMZ file
181//
182DWORD ExtractZipImage(
183 HANDLE hFile,
184 PUCHAR *pBuffer,
185 PULONG pLength)
186{
187 UCHAR buf[VFD_BYTES_PER_SECTOR];
188 DWORD result;
189 DWORD ret;
190
191 PZIP_HEADER zip_hdr;
192 ULONG compressed;
193 ULONG uncompressed;
194 PUCHAR file_cache;
195 z_stream stream;
196 int zlib_ret;
197
198 VFDTRACE(0,
199 ("[VFD] VfdExtractImz - IN\n"));
200
201 *pBuffer = NULL;
202 *pLength = 0;
203
204 //
205 // Read PKZIP local file header of the first file in the file
206 // -- An IMZ file actually is just a ZIP file with a different
207 // extension, which contains a single floppy image file
208 //
209 if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) != 0) {
210 ret = GetLastError();
211
212 VFDTRACE(0,(
213 "SetFilePointer - %s", SystemMessage(ret)));;
214
215 return ret;
216 }
217
218 if (!ReadFile(hFile, buf, VFD_BYTES_PER_SECTOR, &result, NULL) ||
219 result < VFD_BYTES_PER_SECTOR) {
220
221 ret = GetLastError();
222
223 VFDTRACE(0,(
224 "ReadFile - %s", SystemMessage(ret)));;
225
226 return ret;
227 }
228
229 zip_hdr = (PZIP_HEADER)buf;
230
231 // check local file header signature
232
233 if (zip_hdr->header_signature != ZIP_LOCAL_SIGNATURE) {
234
235 VFDTRACE(0,
236 ("[VFD] PKZIP header signature not found.\n"));
237
238 return ERROR_INVALID_DATA;
239 }
240
241 // check compression method
242
243 if (zip_hdr->compression_method != Z_DEFLATED) {
244
245 VFDTRACE(0,
246 ("[VFD] Bad PKZIP compression method.\n"));
247
248 return ERROR_NOT_SUPPORTED;
249 }
250
251 if (zip_hdr->general_flags & 0x01) {
252 // encrypted zip not supported
253
254 VFDTRACE(0,
255 ("[VFD] PKZIP encrypted.\n"));
256
257 return ERROR_NOT_SUPPORTED;
258 }
259
260 // check uncompressed image size
261
262 compressed = zip_hdr->compressed_size;
263 uncompressed = zip_hdr->uncompressed_size;
264
265 switch (uncompressed) {
266 case VFD_SECTOR_TO_BYTE(320):
267 case VFD_SECTOR_TO_BYTE(360):
268 case VFD_SECTOR_TO_BYTE(640):
269 case VFD_SECTOR_TO_BYTE(720):
270 case VFD_SECTOR_TO_BYTE(1280):
271 case VFD_SECTOR_TO_BYTE(1440):
272 case VFD_SECTOR_TO_BYTE(1640):
273 case VFD_SECTOR_TO_BYTE(2400):
274 case VFD_SECTOR_TO_BYTE(2880):
275 case VFD_SECTOR_TO_BYTE(3360):
276 case VFD_SECTOR_TO_BYTE(3444):
277 case VFD_SECTOR_TO_BYTE(5760):
278 break;
279
280 default:
281 VFDTRACE(0,
282 ("[VFD] Unsupported image size %lu.\n",
283 uncompressed));
284
285 return ERROR_NOT_SUPPORTED;
286 }
287
288 // check local file header length
289 // -- Just for simplicity, the compressed data must start in the
290 // first sector in the file: this is not a problem in most cases.
291
292 if (FIELD_OFFSET(ZIP_HEADER, file_name) +
293 zip_hdr->file_name_length +
294 zip_hdr->extra_field_length >= VFD_BYTES_PER_SECTOR) {
295
296 VFDTRACE(0,
297 ("[VFD] PKZIP header too long.\n"));
298
299 return ERROR_NOT_SUPPORTED;
300 }
301
302 // allocate memory to store uncompressed data
303
304 file_cache = (PUCHAR)LocalAlloc(LPTR, uncompressed);
305
306 if (!file_cache) {
307
308 VFDTRACE(0,
309 ("[VFD] Failed to allocate file cache.\n"));
310
311 return ERROR_OUTOFMEMORY;
312 }
313
314 // initialize the zlib stream
315
316 ZeroMemory(&stream, sizeof(stream));
317
318 // set initial input data information
319
320 stream.next_in = (PUCHAR)zip_hdr->file_name +
321 zip_hdr->file_name_length + zip_hdr->extra_field_length;
322
323 stream.avail_in = VFD_BYTES_PER_SECTOR -
324 FIELD_OFFSET(ZIP_HEADER, file_name) -
325 zip_hdr->file_name_length - zip_hdr->extra_field_length;
326
327 // set output buffer information
328
329 stream.next_out = file_cache;
330 stream.avail_out = uncompressed;
331
332 zlib_ret = inflateInit2(&stream, -MAX_WBITS);
333
334 // negative MAX_WBITS value passed to the inflateInit2() function
335 // indicates that there is no zlib header.
336 // In this case inflate() function requires an extra "dummy" byte
337 // after the compressed stream in order to complete decompression
338 // and return Z_STREAM_END. However, both compressed and uncompressed
339 // data size are already known from the pkzip header, Z_STREAM_END
340 // is not absolutely necessary to know the completion of the operation.
341
342 if (zlib_ret != Z_OK) {
343 LocalFree(file_cache);
344
345 VFDTRACE(0,
346 ("[VFD] inflateInit2() failed - %s.\n",
347 ZLIB_ERROR(zlib_ret)));
348
349 return ERROR_INVALID_FUNCTION;
350 }
351
352 for (;;) {
353
354 // uncompress current block
355
356 zlib_ret = inflate(&stream, Z_NO_FLUSH);
357
358 if (zlib_ret != Z_OK) {
359 if (zlib_ret == Z_STREAM_END) {
360 ret = ERROR_SUCCESS;
361 }
362 else {
363 VFDTRACE(0,
364 ("[VFD] inflate() failed - %s.\n",
365 ZLIB_ERROR(zlib_ret)));
366
367 ret = ERROR_INVALID_FUNCTION;
368 }
369 break;
370 }
371
372 if (stream.total_out >= uncompressed) {
373 // uncompress completed - no need to wait for Z_STREAM_END
374 // (inflate() would return Z_STREAM_END on the next call)
375 ret = ERROR_SUCCESS;
376 break;
377 }
378
379 if (stream.total_in >= compressed) {
380 // somehow there is not enought compressed data
381 ret = ERROR_INVALID_FUNCTION;
382 break;
383 }
384
385 // read next block from file
386
387 if (!ReadFile(hFile, buf, VFD_BYTES_PER_SECTOR, &result, NULL) ||
388 result <= 0) {
389
390 ret = GetLastError();
391
392 VFDTRACE(0,
393 ("[VFD] Read compressed data - %s.\n",
394 SystemMessage(ret)));
395 break;
396 }
397
398 stream.avail_in = result;
399 stream.next_in = buf;
400 }
401
402 // cleanup the zlib stream
403
404 inflateEnd(&stream);
405
406 // set the return information
407
408 if (ret == ERROR_SUCCESS) {
409 *pBuffer = file_cache;
410 *pLength = uncompressed;
411 }
412 else {
413 LocalFree(file_cache);
414 }
415
416 VFDTRACE(0,
417 ("[VFD] VfdExtractImz - OUT\n"));
418
419 return ret;
420}
421
422#endif // VFD_NO_ZLIB