From: Eric Biggers Date: Sat, 14 Dec 2013 21:34:34 +0000 (-0600) Subject: Add kind-of-working LZMS decompression using cabinet.dll API X-Git-Tag: v1.6.0~140 X-Git-Url: https://wimlib.net/git/?p=wimlib;a=commitdiff_plain;h=60523d25f34692d6f3a7c8bbda88eead17f23b12 Add kind-of-working LZMS decompression using cabinet.dll API --- diff --git a/include/wimlib/lzms.h b/include/wimlib/lzms.h new file mode 100644 index 00000000..5584b9a4 --- /dev/null +++ b/include/wimlib/lzms.h @@ -0,0 +1,8 @@ +#ifndef _WIMLIB_LZMS_H +#define _WIMLIB_LZMS_H + +int +lzms_decompress(const void *cdata, unsigned clen, void *udata, unsigned unlen, + unsigned window_size); + +#endif diff --git a/src/lzms-decompress.c b/src/lzms-decompress.c index 9459b0b9..c73a3fdd 100644 --- a/src/lzms-decompress.c +++ b/src/lzms-decompress.c @@ -23,13 +23,120 @@ * along with wimlib; if not, see http://www.gnu.org/licenses/. */ +#include "wimlib/win32_common.h" #include "wimlib/lzms.h" #include "wimlib/error.h" +#include + +typedef HANDLE DECOMPRESSOR_HANDLE; +typedef PVOID PCOMPRESS_ALLOCATION_ROUTINES; +typedef DECOMPRESSOR_HANDLE *PDECOMPRESSOR_HANDLE; + +typedef enum { + COMPRESS_INFORMATION_CLASS_INVALID = 0, + COMPRESS_INFORMATION_CLASS_LEVEL, + COMPRESS_INFORMATION_CLASS_BLOCK_SIZE +} COMPRESS_INFORMATION_CLASS; + +#define COMPRESS_ALGORITHM_LZMS 0x00000005 +#define COMPRESS_RAW 0x20000000 /* Not documented */ + +static HMODULE hCabinetDll; +static pthread_mutex_t cabinetDllMutex = PTHREAD_MUTEX_INITIALIZER; + +static BOOL (WINAPI *CreateDecompressor) + (DWORD Algorithm, + PCOMPRESS_ALLOCATION_ROUTINES AllocationRoutines, + PDECOMPRESSOR_HANDLE DecompressorHandle); + +static BOOL (WINAPI *CloseDecompressor) + (DECOMPRESSOR_HANDLE DecompressorHandle); + +static BOOL (WINAPI *Decompress) + (DECOMPRESSOR_HANDLE DecompressorHandle, + PVOID CompressedData, + SIZE_T CompressedDataSize, + PVOID UncompressedBuffer, + SIZE_T UncompressedBufferSize, + PSIZE_T UncompressedDataSize); int -lzms_decompress(const void *cdata, unsigned clen, void *udata, unsigned unlen, +lzms_decompress(const void *cbuf, unsigned clen, void *ubuf, unsigned ulen, unsigned window_size) { - ERROR("LZMS decompression stub: not implemented"); - return -1; + int ret; + DECOMPRESSOR_HANDLE h; + + ERROR("clen=%u, ulen=%u, window_size=%u", clen, ulen, window_size); + + if (hCabinetDll == NULL) { + pthread_mutex_lock(&cabinetDllMutex); + + if (hCabinetDll == NULL) { + hCabinetDll = LoadLibrary(L"Cabinet.dll"); + + if (hCabinetDll == NULL) { + ERROR("Can't load Cabinet.dll"); + ret = -1; + goto unlock; + } + + CreateDecompressor = (void*)GetProcAddress(hCabinetDll, "CreateDecompressor"); + Decompress = (void*)GetProcAddress(hCabinetDll, "Decompress"); + CloseDecompressor = (void*)GetProcAddress(hCabinetDll, "CloseDecompressor"); + + if (CreateDecompressor == NULL || + Decompress == NULL || + CloseDecompressor == NULL) + { + ERROR("Can't find LZMS compression routines in Cabinet.dll"); + ret = -1; + goto unlock; + } + } + ret = 0; + unlock: + pthread_mutex_unlock(&cabinetDllMutex); + if (ret) + goto out; + } + + + if (!CreateDecompressor(COMPRESS_ALGORITHM_LZMS | COMPRESS_RAW, NULL, &h)) { + ERROR("Failed to create LZMS decompressor (err %d)!", GetLastError()); + ret = -1; + goto out; + } + + + /* TODO: Some sort of chunk header? */ + unsigned offset; + if (clen <= window_size) { + offset = 0; + } else { + const unsigned *p = cbuf; + ERROR("%08x(%u) %08x %08x %08x %08x(%u)", + p[0], p[0], p[1], p[2], p[3], p[4], p[4]); + offset = 20; + } + SIZE_T actual_ulen = -1; + if (!Decompress(h, (void*)cbuf + offset, clen - offset, ubuf, ulen, &actual_ulen)) { + ERROR("Failed to decompress LZMS-compressed data (err %d)!", GetLastError()); + ret = -1; + goto out_close_decompressor; + } + + if (actual_ulen != ulen) { + ERROR("Unexpected actual uncompressed length (got %u, expected %u)", + actual_ulen, ulen); + ret = -1; + goto out_close_decompressor; + } + + ERROR("Successfully decompressed data."); + ret = 0; +out_close_decompressor: + CloseDecompressor(h); +out: + return ret; } diff --git a/src/resource.c b/src/resource.c index 1e64f260..49f37a3c 100644 --- a/src/resource.c +++ b/src/resource.c @@ -135,6 +135,51 @@ read_compressed_wim_resource(const struct wim_resource_spec * const rspec, if (size == 0) return 0; + if (rspec->ctype == WIMLIB_COMPRESSION_TYPE_LZMS) { + /* TODO */ + + unsigned clen = rspec->size_in_wim; + unsigned ulen = rspec->uncompressed_size; + + fprintf(stderr, "clen=%u, ulen=%u, offset=%lu\n", clen, ulen, + rspec->offset_in_wim); + + u8 *cbuf = MALLOC(clen); + u8 *ubuf = MALLOC(ulen); + + ret = full_pread(&rspec->wim->in_fd, + cbuf, clen, rspec->offset_in_wim); + if (ret) { + ERROR_WITH_ERRNO("Can't read compressed data"); + goto out_free_bufs; + } + + ret = lzms_decompress(cbuf, clen, ubuf, ulen, + orig_chunk_size); + if (ret) { + ERROR("LZMS decompression error."); + errno = EINVAL; + ret = WIMLIB_ERR_DECOMPRESSION; + goto out_free_bufs; + } + if (cb) { + u32 chunk_size; + for (u64 i = offset; i < offset + size; i += chunk_size) { + chunk_size = min(offset + size - i, cb_chunk_size); + ret = cb(&ubuf[i], chunk_size, ctx_or_buf); + if (ret) + goto out_free_bufs; + } + } else { + memcpy(ctx_or_buf, &ubuf[offset], size); + } + ret = 0; + out_free_bufs: + FREE(ubuf); + FREE(cbuf); + return ret; + } + u64 *chunk_offsets = NULL; u8 *out_buf = NULL; u8 *tmp_buf = NULL; @@ -631,7 +676,7 @@ read_partial_wim_resource(const struct wim_lookup_table_entry *lte, } else { /* Normal mode: read must not overrun end of original size. */ wimlib_assert(offset + size >= size && - offset + size <= rspec->uncompressed_size); + lte->offset_in_res + offset + size <= rspec->uncompressed_size); } DEBUG("Reading WIM resource: %"PRIu64" @ +%"PRIu64"[+%"PRIu64"] "