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
ExtractZipInfo(HANDLE hFile,ULONG * pSize)33 DWORD ExtractZipInfo(
34 HANDLE hFile,
35 ULONG *pSize)
36 {
37 UNREFERENCED_PARAMETER(hFile);
38 UNREFERENCED_PARAMETER(pSize);
39 return ERROR_NOT_SUPPORTED;
40 }
41
ExtractZipImage(HANDLE hFile,PUCHAR * pBuffer,PULONG pLength)42 DWORD 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
ZLIB_ERROR(int err)56 static 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
zcalloc(voidpf opaque,unsigned items,unsigned size)73 voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size))
74 {
75 UNREFERENCED_PARAMETER(opaque);
76 return LocalAlloc(LPTR, items * size);
77 }
78
zcfree(voidpf opaque,voidpf ptr)79 void 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
113 typedef 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 }
128 ZIP_HEADER, *PZIP_HEADER;
129
130 //
131 // Check if the file is ZIP compressed
132 //
ExtractZipInfo(HANDLE hFile,ULONG * pSize)133 DWORD 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 //
ExtractZipImage(HANDLE hFile,PUCHAR * pBuffer,PULONG pLength)182 DWORD 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
423