1/* This file is part of the KDE libraries
2 Copyright (C) 2007-2008 Per √ėyvind Karlsen <peroyvind@mandriva.org>
3
4 Based on kbzip2filter:
5 Copyright (C) 2000-2005 David Faure <faure@kde.org>
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
16
17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA.
21*/
22
23#include "kxzfilter.h"
24#include "loggingcategory.h"
25
26#include <config-compression.h>
27
28#if HAVE_XZ_SUPPORT
29extern "C" {
30#include <lzma.h>
31}
32
33#include <QDebug>
34
35#include <qiodevice.h>
36
37class Q_DECL_HIDDEN KXzFilter::Private
38{
39public:
40 Private()
41 : isInitialized(false)
42 {
43 memset(&zStream, 0, sizeof (zStream));
44 mode = 0;
45 }
46
47 lzma_stream zStream;
48 lzma_filter filters[5];
49 unsigned char props[5];
50 int mode;
51 bool isInitialized;
52 KXzFilter::Flag flag;
53};
54
55KXzFilter::KXzFilter()
56 : d(new Private)
57{
58}
59
60KXzFilter::~KXzFilter()
61{
62 delete d;
63}
64
65bool KXzFilter::init(int mode)
66{
67 QVector<unsigned char> props;
68 return init(mode, AUTO, props);
69}
70
71bool KXzFilter::init(int mode, Flag flag, const QVector<unsigned char> &properties)
72{
73 if (d->isInitialized) {
74 terminate();
75 }
76
77 d->flag = flag;
78 lzma_ret result;
79 d->zStream.next_in = nullptr;
80 d->zStream.avail_in = 0;
81 if (mode == QIODevice::ReadOnly) {
82 switch (flag) {
83 case AUTO:
84 /* We set the memlimit for decompression to 100MiB which should be
85 * more than enough to be sufficient for level 9 which requires 65 MiB.
86 */
87 result = lzma_auto_decoder(&d->zStream, 100 << 20, 0);
88 if (result != LZMA_OK) {
89 qCWarning(KArchiveLog) << "lzma_auto_decoder returned" << result;
90 return false;
91 }
92 break;
93 case LZMA: {
94 d->filters[0].id = LZMA_FILTER_LZMA1;
95 d->filters[0].options = nullptr;
96 d->filters[1].id = LZMA_VLI_UNKNOWN;
97 d->filters[1].options = nullptr;
98
99 Q_ASSERT(properties.size() == 5);
100 unsigned char props[5];
101 for (int i = 0; i < properties.size(); ++i) {
102 props[i] = properties[i];
103 }
104
105 result = lzma_properties_decode(&d->filters[0], nullptr, props, sizeof (props));
106 if (result != LZMA_OK) {
107 qCWarning(KArchiveLog) << "lzma_properties_decode returned" << result;
108 return false;
109 }
110 break;
111 }
112 case LZMA2: {
113 d->filters[0].id = LZMA_FILTER_LZMA2;
114 d->filters[0].options = nullptr;
115 d->filters[1].id = LZMA_VLI_UNKNOWN;
116 d->filters[1].options = nullptr;
117
118 Q_ASSERT(properties.size() == 1);
119 unsigned char props[1];
120 props[0] = properties[0];
121
122 result = lzma_properties_decode(&d->filters[0], nullptr, props, sizeof (props));
123 if (result != LZMA_OK) {
124 qCWarning(KArchiveLog) << "lzma_properties_decode returned" << result;
125 return false;
126 }
127 break;
128 }
129 case BCJ: {
130 d->filters[0].id = LZMA_FILTER_X86;
131 d->filters[0].options = nullptr;
132
133 unsigned char props[5] = { 0x5d, 0x00, 0x00, 0x08, 0x00 }
134 ;
135 d->filters[1].id = LZMA_FILTER_LZMA1;
136 d->filters[1].options = nullptr;
137 result = lzma_properties_decode(&d->filters[1], nullptr, props, sizeof (props));
138 if (result != LZMA_OK) {
139 qCWarning(KArchiveLog) << "lzma_properties_decode1 returned" << result;
140 return false;
141 }
142
143 d->filters[2].id = LZMA_VLI_UNKNOWN;
144 d->filters[2].options = nullptr;
145
146 break;
147 }
148 case POWERPC:
149 case IA64:
150 case ARM:
151 case ARMTHUMB:
152 case SPARC:
153 //qCDebug(KArchiveLog) << "flag" << flag << "props size" << properties.size();
154 break;
155 }
156
157 if (flag != AUTO) {
158 result = lzma_raw_decoder(&d->zStream, d->filters);
159 if (result != LZMA_OK) {
160 qCWarning(KArchiveLog) << "lzma_raw_decoder returned" << result;
161 return false;
162 }
163 }
164
165 } else if (mode == QIODevice::WriteOnly) {
166 if (flag == AUTO) {
167 result = lzma_easy_encoder(&d->zStream, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC32);
168 } else {
169 if (flag == LZMA2) {
170 lzma_options_lzma lzma_opt;
171 lzma_lzma_preset(&lzma_opt, LZMA_PRESET_DEFAULT);
172
173 d->filters[0].id = LZMA_FILTER_LZMA2;
174 d->filters[0].options = &lzma_opt;
175 d->filters[1].id = LZMA_VLI_UNKNOWN;
176 d->filters[1].options = nullptr;
177 }
178 result = lzma_raw_encoder(&d->zStream, d->filters);
179 }
180 if (result != LZMA_OK) {
181 qCWarning(KArchiveLog) << "lzma_easy_encoder returned" << result;
182 return false;
183 }
184 } else {
185 //qCWarning(KArchiveLog) << "Unsupported mode " << mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported";
186 return false;
187 }
188 d->mode = mode;
189 d->isInitialized = true;
190 return true;
191}
192
193int KXzFilter::mode() const
194{
195 return d->mode;
196}
197
198bool KXzFilter::terminate()
199{
200 if (d->mode == QIODevice::ReadOnly || d->mode == QIODevice::WriteOnly) {
201 lzma_end(&d->zStream);
202 } else {
203 //qCWarning(KArchiveLog) << "Unsupported mode " << d->mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported";
204 return false;
205 }
206 d->isInitialized = false;
207 return true;
208}
209
210void KXzFilter::reset()
211{
212 //qCDebug(KArchiveLog) << "KXzFilter::reset";
213 // liblzma doesn't have a reset call...
214 terminate();
215 init(d->mode);
216}
217
218void KXzFilter::setOutBuffer(char *data, uint maxlen)
219{
220 d->zStream.avail_out = maxlen;
221 d->zStream.next_out = (uint8_t *)data;
222}
223
224void KXzFilter::setInBuffer(const char *data, unsigned int size)
225{
226 d->zStream.avail_in = size;
227 d->zStream.next_in = (uint8_t *)const_cast<char *>(data);
228}
229
230int KXzFilter::inBufferAvailable() const
231{
232 return d->zStream.avail_in;
233}
234
235int KXzFilter::outBufferAvailable() const
236{
237 return d->zStream.avail_out;
238}
239
240KXzFilter::Result KXzFilter::uncompress()
241{
242 //qCDebug(KArchiveLog) << "Calling lzma_code with avail_in=" << inBufferAvailable() << " avail_out =" << outBufferAvailable();
243 lzma_ret result;
244 result = lzma_code(&d->zStream, LZMA_RUN);
245
246 /*if (result != LZMA_OK) {
247 qCDebug(KArchiveLog) << "lzma_code returned " << result;
248 //qCDebug(KArchiveLog) << "KXzFilter::uncompress " << ( result == LZMA_STREAM_END ? KFilterBase::End : KFilterBase::Error );
249 }*/
250
251 switch (result) {
252 case LZMA_OK:
253 return KFilterBase::Ok;
254 case LZMA_STREAM_END:
255 return KFilterBase::End;
256 default:
257 return KFilterBase::Error;
258 }
259}
260
261KXzFilter::Result KXzFilter::compress(bool finish)
262{
263 //qCDebug(KArchiveLog) << "Calling lzma_code with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
264 lzma_ret result = lzma_code(&d->zStream, finish ? LZMA_FINISH : LZMA_RUN);
265 switch (result) {
266 case LZMA_OK:
267 return KFilterBase::Ok;
268 break;
269 case LZMA_STREAM_END:
270 //qCDebug(KArchiveLog) << " lzma_code returned " << result;
271 return KFilterBase::End;
272 break;
273 default:
274 //qCDebug(KArchiveLog) << " lzma_code returned " << result;
275 return KFilterBase::Error;
276 break;
277 }
278}
279
280#endif /* HAVE_XZ_SUPPORT */
281