Add kind-of-working LZMS decompression using cabinet.dll API
[wimlib] / src / lzms-decompress.c
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;
 }