mirror of
https://github.com/reactos/reactos.git
synced 2025-01-07 06:45:24 +00:00
423 lines
8.7 KiB
C
423 lines
8.7 KiB
C
|
/*
|
||
|
vfdzip.c
|
||
|
|
||
|
Virtual Floppy Drive for Windows
|
||
|
Driver control library
|
||
|
Zip compressed floppy image handling
|
||
|
|
||
|
Copyright (C) 2003-2005 Ken Kato
|
||
|
*/
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
#pragma message(__FILE__": Compiled as C++ for testing purpose.")
|
||
|
#endif // __cplusplus
|
||
|
|
||
|
#define WIN32_LEAN_AND_MEAN
|
||
|
#include <windows.h>
|
||
|
|
||
|
#include "vfdtypes.h"
|
||
|
#include "vfdio.h"
|
||
|
#include "vfdlib.h"
|
||
|
|
||
|
#ifndef __REACTOS__
|
||
|
#define ZLIB_WINAPI
|
||
|
#else
|
||
|
#define Z_SOLO
|
||
|
#define ZLIB_INTERNAL
|
||
|
#endif
|
||
|
#include "zlib.h"
|
||
|
|
||
|
#ifdef VFD_NO_ZLIB
|
||
|
#pragma message("ZIP image support is disabled.")
|
||
|
|
||
|
DWORD ExtractZipInfo(
|
||
|
HANDLE hFile,
|
||
|
ULONG *pSize)
|
||
|
{
|
||
|
UNREFERENCED_PARAMETER(hFile);
|
||
|
UNREFERENCED_PARAMETER(pSize);
|
||
|
return ERROR_NOT_SUPPORTED;
|
||
|
}
|
||
|
|
||
|
DWORD ExtractZipImage(
|
||
|
HANDLE hFile,
|
||
|
PUCHAR *pBuffer,
|
||
|
PULONG pLength)
|
||
|
{
|
||
|
UNREFERENCED_PARAMETER(hFile);
|
||
|
UNREFERENCED_PARAMETER(pBuffer);
|
||
|
UNREFERENCED_PARAMETER(pLength);
|
||
|
return ERROR_NOT_SUPPORTED;
|
||
|
}
|
||
|
|
||
|
#else // VFD_NO_ZLIB
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
static const char *ZLIB_ERROR(int err)
|
||
|
{
|
||
|
switch (err) {
|
||
|
case Z_OK : return "Z_OK";
|
||
|
case Z_STREAM_END : return "Z_STREAM_END";
|
||
|
case Z_NEED_DICT : return "Z_NEED_DICT";
|
||
|
case Z_ERRNO : return "Z_ERRNO";
|
||
|
case Z_STREAM_ERROR : return "Z_STREAM_ERROR";
|
||
|
case Z_DATA_ERROR : return "Z_DATA_ERROR";
|
||
|
case Z_MEM_ERROR : return "Z_MEM_ERROR";
|
||
|
case Z_BUF_ERROR : return "Z_BUF_ERROR";
|
||
|
case Z_VERSION_ERROR: return "Z_VERSION_ERROR";
|
||
|
default : return "unknown";
|
||
|
}
|
||
|
}
|
||
|
#endif // _DEBUG
|
||
|
|
||
|
voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size))
|
||
|
{
|
||
|
UNREFERENCED_PARAMETER(opaque);
|
||
|
return LocalAlloc(LPTR, items * size);
|
||
|
}
|
||
|
|
||
|
void zcfree OF((voidpf opaque, voidpf ptr))
|
||
|
{
|
||
|
UNREFERENCED_PARAMETER(opaque);
|
||
|
LocalFree(ptr);
|
||
|
}
|
||
|
|
||
|
#define ZIP_LOCAL_SIGNATURE 0x04034b50
|
||
|
|
||
|
#define ZIP_FLAG_ENCRYPTED 0x01
|
||
|
|
||
|
#define ZIP_FLAG_DEFLATE_NORMAL 0x00
|
||
|
#define ZIP_FLAG_DEFLATE_MAX 0x02
|
||
|
#define ZIP_FLAG_DEFLATE_FAST 0x04
|
||
|
#define ZIP_FLAG_DEFLATE_SUPER 0x06
|
||
|
#define ZIP_FLAG_DEFLATE_MASK 0x06
|
||
|
|
||
|
#define ZIP_FLAG_SIZE_IN_DESC 0x08
|
||
|
|
||
|
#define ZIP_METHOD_STORED 0
|
||
|
#define ZIP_METHOD_SHRUNK 1
|
||
|
#define ZIP_METHOD_REDUCED1 2
|
||
|
#define ZIP_METHOD_REDUCED2 3
|
||
|
#define ZIP_METHOD_REDUCED3 4
|
||
|
#define ZIP_METHOD_REDUCED4 5
|
||
|
#define ZIP_METHOD_IMPLODED 6
|
||
|
#define ZIP_METHOD_TOKENIZED 7
|
||
|
#define ZIP_METHOD_DEFLATED 8
|
||
|
#define ZIP_METHOD_DEFLATE64 9
|
||
|
#define ZIP_METHOD_PKWARE_IMP 10
|
||
|
#define ZIP_METHOD_RESERVED 11
|
||
|
#define ZIP_METHOD_BZIP2 12
|
||
|
|
||
|
#pragma pack(1)
|
||
|
|
||
|
typedef struct _zip_local_file_header {
|
||
|
ULONG header_signature;
|
||
|
USHORT required_version;
|
||
|
USHORT general_flags;
|
||
|
USHORT compression_method;
|
||
|
USHORT last_mod_time;
|
||
|
USHORT last_mod_date;
|
||
|
ULONG crc32;
|
||
|
ULONG compressed_size;
|
||
|
ULONG uncompressed_size;
|
||
|
USHORT file_name_length;
|
||
|
USHORT extra_field_length;
|
||
|
CHAR file_name[1];
|
||
|
// followed by extra field data, then compressed data
|
||
|
}
|
||
|
ZIP_HEADER, *PZIP_HEADER;
|
||
|
|
||
|
//
|
||
|
// Check if the file is ZIP compressed
|
||
|
//
|
||
|
DWORD ExtractZipInfo(
|
||
|
HANDLE hFile,
|
||
|
ULONG *pSize)
|
||
|
{
|
||
|
ZIP_HEADER zip_hdr;
|
||
|
DWORD result;
|
||
|
DWORD ret;
|
||
|
|
||
|
if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) != 0) {
|
||
|
ret = GetLastError();
|
||
|
|
||
|
VFDTRACE(0,
|
||
|
("SetFilePointer() - %s\n",
|
||
|
SystemMessage(ret)));
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
if (!ReadFile(hFile, &zip_hdr, sizeof(zip_hdr), &result, NULL)) {
|
||
|
ret = GetLastError();
|
||
|
|
||
|
VFDTRACE(0,
|
||
|
("ReadFile() - %s\n",
|
||
|
SystemMessage(ret)));
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
if (result != sizeof(zip_hdr) ||
|
||
|
zip_hdr.header_signature != ZIP_LOCAL_SIGNATURE ||
|
||
|
zip_hdr.compression_method != ZIP_METHOD_DEFLATED ||
|
||
|
(zip_hdr.general_flags & ZIP_FLAG_ENCRYPTED)) {
|
||
|
|
||
|
VFDTRACE(0,
|
||
|
("[VFD] Invalid ZIP file\n"));
|
||
|
|
||
|
return ERROR_INVALID_DATA;
|
||
|
}
|
||
|
|
||
|
// correct (and supported) ZIP header detected
|
||
|
|
||
|
*pSize = zip_hdr.uncompressed_size;
|
||
|
|
||
|
return ERROR_SUCCESS;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Extract original data from IMZ file
|
||
|
//
|
||
|
DWORD ExtractZipImage(
|
||
|
HANDLE hFile,
|
||
|
PUCHAR *pBuffer,
|
||
|
PULONG pLength)
|
||
|
{
|
||
|
UCHAR buf[VFD_BYTES_PER_SECTOR];
|
||
|
DWORD result;
|
||
|
DWORD ret;
|
||
|
|
||
|
PZIP_HEADER zip_hdr;
|
||
|
ULONG compressed;
|
||
|
ULONG uncompressed;
|
||
|
PUCHAR file_cache;
|
||
|
z_stream stream;
|
||
|
int zlib_ret;
|
||
|
|
||
|
VFDTRACE(0,
|
||
|
("[VFD] VfdExtractImz - IN\n"));
|
||
|
|
||
|
*pBuffer = NULL;
|
||
|
*pLength = 0;
|
||
|
|
||
|
//
|
||
|
// Read PKZIP local file header of the first file in the file
|
||
|
// -- An IMZ file actually is just a ZIP file with a different
|
||
|
// extension, which contains a single floppy image file
|
||
|
//
|
||
|
if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) != 0) {
|
||
|
ret = GetLastError();
|
||
|
|
||
|
VFDTRACE(0,(
|
||
|
"SetFilePointer - %s", SystemMessage(ret)));;
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
if (!ReadFile(hFile, buf, VFD_BYTES_PER_SECTOR, &result, NULL) ||
|
||
|
result < VFD_BYTES_PER_SECTOR) {
|
||
|
|
||
|
ret = GetLastError();
|
||
|
|
||
|
VFDTRACE(0,(
|
||
|
"ReadFile - %s", SystemMessage(ret)));;
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
zip_hdr = (PZIP_HEADER)buf;
|
||
|
|
||
|
// check local file header signature
|
||
|
|
||
|
if (zip_hdr->header_signature != ZIP_LOCAL_SIGNATURE) {
|
||
|
|
||
|
VFDTRACE(0,
|
||
|
("[VFD] PKZIP header signature not found.\n"));
|
||
|
|
||
|
return ERROR_INVALID_DATA;
|
||
|
}
|
||
|
|
||
|
// check compression method
|
||
|
|
||
|
if (zip_hdr->compression_method != Z_DEFLATED) {
|
||
|
|
||
|
VFDTRACE(0,
|
||
|
("[VFD] Bad PKZIP compression method.\n"));
|
||
|
|
||
|
return ERROR_NOT_SUPPORTED;
|
||
|
}
|
||
|
|
||
|
if (zip_hdr->general_flags & 0x01) {
|
||
|
// encrypted zip not supported
|
||
|
|
||
|
VFDTRACE(0,
|
||
|
("[VFD] PKZIP encrypted.\n"));
|
||
|
|
||
|
return ERROR_NOT_SUPPORTED;
|
||
|
}
|
||
|
|
||
|
// check uncompressed image size
|
||
|
|
||
|
compressed = zip_hdr->compressed_size;
|
||
|
uncompressed = zip_hdr->uncompressed_size;
|
||
|
|
||
|
switch (uncompressed) {
|
||
|
case VFD_SECTOR_TO_BYTE(320):
|
||
|
case VFD_SECTOR_TO_BYTE(360):
|
||
|
case VFD_SECTOR_TO_BYTE(640):
|
||
|
case VFD_SECTOR_TO_BYTE(720):
|
||
|
case VFD_SECTOR_TO_BYTE(1280):
|
||
|
case VFD_SECTOR_TO_BYTE(1440):
|
||
|
case VFD_SECTOR_TO_BYTE(1640):
|
||
|
case VFD_SECTOR_TO_BYTE(2400):
|
||
|
case VFD_SECTOR_TO_BYTE(2880):
|
||
|
case VFD_SECTOR_TO_BYTE(3360):
|
||
|
case VFD_SECTOR_TO_BYTE(3444):
|
||
|
case VFD_SECTOR_TO_BYTE(5760):
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
VFDTRACE(0,
|
||
|
("[VFD] Unsupported image size %lu.\n",
|
||
|
uncompressed));
|
||
|
|
||
|
return ERROR_NOT_SUPPORTED;
|
||
|
}
|
||
|
|
||
|
// check local file header length
|
||
|
// -- Just for simplicity, the compressed data must start in the
|
||
|
// first sector in the file: this is not a problem in most cases.
|
||
|
|
||
|
if (FIELD_OFFSET(ZIP_HEADER, file_name) +
|
||
|
zip_hdr->file_name_length +
|
||
|
zip_hdr->extra_field_length >= VFD_BYTES_PER_SECTOR) {
|
||
|
|
||
|
VFDTRACE(0,
|
||
|
("[VFD] PKZIP header too long.\n"));
|
||
|
|
||
|
return ERROR_NOT_SUPPORTED;
|
||
|
}
|
||
|
|
||
|
// allocate memory to store uncompressed data
|
||
|
|
||
|
file_cache = (PUCHAR)LocalAlloc(LPTR, uncompressed);
|
||
|
|
||
|
if (!file_cache) {
|
||
|
|
||
|
VFDTRACE(0,
|
||
|
("[VFD] Failed to allocate file cache.\n"));
|
||
|
|
||
|
return ERROR_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
// initialize the zlib stream
|
||
|
|
||
|
ZeroMemory(&stream, sizeof(stream));
|
||
|
|
||
|
// set initial input data information
|
||
|
|
||
|
stream.next_in = (PUCHAR)zip_hdr->file_name +
|
||
|
zip_hdr->file_name_length + zip_hdr->extra_field_length;
|
||
|
|
||
|
stream.avail_in = VFD_BYTES_PER_SECTOR -
|
||
|
FIELD_OFFSET(ZIP_HEADER, file_name) -
|
||
|
zip_hdr->file_name_length - zip_hdr->extra_field_length;
|
||
|
|
||
|
// set output buffer information
|
||
|
|
||
|
stream.next_out = file_cache;
|
||
|
stream.avail_out = uncompressed;
|
||
|
|
||
|
zlib_ret = inflateInit2(&stream, -MAX_WBITS);
|
||
|
|
||
|
// negative MAX_WBITS value passed to the inflateInit2() function
|
||
|
// indicates that there is no zlib header.
|
||
|
// In this case inflate() function requires an extra "dummy" byte
|
||
|
// after the compressed stream in order to complete decompression
|
||
|
// and return Z_STREAM_END. However, both compressed and uncompressed
|
||
|
// data size are already known from the pkzip header, Z_STREAM_END
|
||
|
// is not absolutely necessary to know the completion of the operation.
|
||
|
|
||
|
if (zlib_ret != Z_OK) {
|
||
|
LocalFree(file_cache);
|
||
|
|
||
|
VFDTRACE(0,
|
||
|
("[VFD] inflateInit2() failed - %s.\n",
|
||
|
ZLIB_ERROR(zlib_ret)));
|
||
|
|
||
|
return ERROR_INVALID_FUNCTION;
|
||
|
}
|
||
|
|
||
|
for (;;) {
|
||
|
|
||
|
// uncompress current block
|
||
|
|
||
|
zlib_ret = inflate(&stream, Z_NO_FLUSH);
|
||
|
|
||
|
if (zlib_ret != Z_OK) {
|
||
|
if (zlib_ret == Z_STREAM_END) {
|
||
|
ret = ERROR_SUCCESS;
|
||
|
}
|
||
|
else {
|
||
|
VFDTRACE(0,
|
||
|
("[VFD] inflate() failed - %s.\n",
|
||
|
ZLIB_ERROR(zlib_ret)));
|
||
|
|
||
|
ret = ERROR_INVALID_FUNCTION;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (stream.total_out >= uncompressed) {
|
||
|
// uncompress completed - no need to wait for Z_STREAM_END
|
||
|
// (inflate() would return Z_STREAM_END on the next call)
|
||
|
ret = ERROR_SUCCESS;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (stream.total_in >= compressed) {
|
||
|
// somehow there is not enought compressed data
|
||
|
ret = ERROR_INVALID_FUNCTION;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// read next block from file
|
||
|
|
||
|
if (!ReadFile(hFile, buf, VFD_BYTES_PER_SECTOR, &result, NULL) ||
|
||
|
result <= 0) {
|
||
|
|
||
|
ret = GetLastError();
|
||
|
|
||
|
VFDTRACE(0,
|
||
|
("[VFD] Read compressed data - %s.\n",
|
||
|
SystemMessage(ret)));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
stream.avail_in = result;
|
||
|
stream.next_in = buf;
|
||
|
}
|
||
|
|
||
|
// cleanup the zlib stream
|
||
|
|
||
|
inflateEnd(&stream);
|
||
|
|
||
|
// set the return information
|
||
|
|
||
|
if (ret == ERROR_SUCCESS) {
|
||
|
*pBuffer = file_cache;
|
||
|
*pLength = uncompressed;
|
||
|
}
|
||
|
else {
|
||
|
LocalFree(file_cache);
|
||
|
}
|
||
|
|
||
|
VFDTRACE(0,
|
||
|
("[VFD] VfdExtractImz - OUT\n"));
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
#endif // VFD_NO_ZLIB
|