KArchive

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

KDE's Doxygen guidelines are available online.