c73a3fdd44afc3b2bb53917119ac248693d97d85
[wimlib] / src / lzms-decompress.c
1 /*
2  * lzms-decompress.c
3  *
4  * LZMS decompression routines.
5  */
6
7 /*
8  * Copyright (C) 2013 Eric Biggers
9  *
10  * This file is part of wimlib, a library for working with WIM files.
11  *
12  * wimlib is free software; you can redistribute it and/or modify it under the
13  * terms of the GNU General Public License as published by the Free
14  * Software Foundation; either version 3 of the License, or (at your option)
15  * any later version.
16  *
17  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
18  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
19  * A PARTICULAR PURPOSE. See the GNU General Public License for more
20  * details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with wimlib; if not, see http://www.gnu.org/licenses/.
24  */
25
26 #include "wimlib/win32_common.h"
27 #include "wimlib/lzms.h"
28 #include "wimlib/error.h"
29 #include <pthread.h>
30
31 typedef HANDLE DECOMPRESSOR_HANDLE;
32 typedef PVOID PCOMPRESS_ALLOCATION_ROUTINES;
33 typedef DECOMPRESSOR_HANDLE *PDECOMPRESSOR_HANDLE;
34
35 typedef enum {
36         COMPRESS_INFORMATION_CLASS_INVALID = 0,
37         COMPRESS_INFORMATION_CLASS_LEVEL,
38         COMPRESS_INFORMATION_CLASS_BLOCK_SIZE
39 } COMPRESS_INFORMATION_CLASS;
40
41 #define COMPRESS_ALGORITHM_LZMS 0x00000005
42 #define COMPRESS_RAW            0x20000000 /* Not documented  */
43
44 static HMODULE hCabinetDll;
45 static pthread_mutex_t cabinetDllMutex = PTHREAD_MUTEX_INITIALIZER;
46
47 static BOOL (WINAPI *CreateDecompressor)
48         (DWORD Algorithm,
49          PCOMPRESS_ALLOCATION_ROUTINES AllocationRoutines,
50          PDECOMPRESSOR_HANDLE DecompressorHandle);
51
52 static BOOL (WINAPI *CloseDecompressor)
53         (DECOMPRESSOR_HANDLE DecompressorHandle);
54
55 static BOOL (WINAPI *Decompress)
56         (DECOMPRESSOR_HANDLE DecompressorHandle,
57          PVOID CompressedData,
58          SIZE_T CompressedDataSize,
59          PVOID UncompressedBuffer,
60          SIZE_T UncompressedBufferSize,
61          PSIZE_T UncompressedDataSize);
62
63 int
64 lzms_decompress(const void *cbuf, unsigned clen, void *ubuf, unsigned ulen,
65                 unsigned window_size)
66 {
67         int ret;
68         DECOMPRESSOR_HANDLE h;
69
70         ERROR("clen=%u, ulen=%u, window_size=%u", clen, ulen, window_size);
71
72         if (hCabinetDll == NULL) {
73                 pthread_mutex_lock(&cabinetDllMutex);
74
75                 if (hCabinetDll == NULL) {
76                         hCabinetDll = LoadLibrary(L"Cabinet.dll");
77
78                         if (hCabinetDll == NULL) {
79                                 ERROR("Can't load Cabinet.dll");
80                                 ret = -1;
81                                 goto unlock;
82                         }
83
84                         CreateDecompressor = (void*)GetProcAddress(hCabinetDll, "CreateDecompressor");
85                         Decompress = (void*)GetProcAddress(hCabinetDll, "Decompress");
86                         CloseDecompressor = (void*)GetProcAddress(hCabinetDll, "CloseDecompressor");
87
88                         if (CreateDecompressor == NULL ||
89                             Decompress == NULL ||
90                             CloseDecompressor == NULL)
91                         {
92                                 ERROR("Can't find LZMS compression routines in Cabinet.dll");
93                                 ret = -1;
94                                 goto unlock;
95                         }
96                 }
97                 ret = 0;
98         unlock:
99                 pthread_mutex_unlock(&cabinetDllMutex);
100                 if (ret)
101                         goto out;
102         }
103
104
105         if (!CreateDecompressor(COMPRESS_ALGORITHM_LZMS | COMPRESS_RAW, NULL, &h)) {
106                 ERROR("Failed to create LZMS decompressor (err %d)!", GetLastError());
107                 ret = -1;
108                 goto out;
109         }
110
111
112         /* TODO:  Some sort of chunk header?  */
113         unsigned offset;
114         if (clen <= window_size) {
115                 offset = 0;
116         } else {
117                 const unsigned *p = cbuf;
118                 ERROR("%08x(%u) %08x %08x %08x %08x(%u)",
119                       p[0], p[0], p[1], p[2], p[3], p[4], p[4]);
120                 offset = 20;
121         }
122         SIZE_T actual_ulen = -1;
123         if (!Decompress(h, (void*)cbuf + offset, clen - offset, ubuf, ulen, &actual_ulen)) {
124                 ERROR("Failed to decompress LZMS-compressed data (err %d)!", GetLastError());
125                 ret = -1;
126                 goto out_close_decompressor;
127         }
128
129         if (actual_ulen != ulen) {
130                 ERROR("Unexpected actual uncompressed length (got %u, expected %u)",
131                       actual_ulen, ulen);
132                 ret = -1;
133                 goto out_close_decompressor;
134         }
135
136         ERROR("Successfully decompressed data.");
137         ret = 0;
138 out_close_decompressor:
139         CloseDecompressor(h);
140 out:
141         return ret;
142 }