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