xref: /reactos/modules/rosapps/lib/vfdlib/vfdzip.c (revision 2b91b296)
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 
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 
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
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 
73 voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size))
74 {
75 	UNREFERENCED_PARAMETER(opaque);
76 	return LocalAlloc(LPTR, items * size);
77 }
78 
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 //
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 //
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