Add kind-of-working LZMS decompression using cabinet.dll API
authorEric Biggers <ebiggers3@gmail.com>
Sat, 14 Dec 2013 21:34:34 +0000 (15:34 -0600)
committerEric Biggers <ebiggers3@gmail.com>
Sat, 14 Dec 2013 21:34:48 +0000 (15:34 -0600)
include/wimlib/lzms.h [new file with mode: 0644]
src/lzms-decompress.c
src/resource.c

diff --git a/include/wimlib/lzms.h b/include/wimlib/lzms.h
new file mode 100644 (file)
index 0000000..5584b9a
--- /dev/null
@@ -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
index 9459b0b..c73a3fd 100644 (file)
  * 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 <pthread.h>
+
+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;
 }
index 1e64f26..49f37a3 100644 (file)
@@ -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"] "